2019-01-07 15:34:16 +01:00
|
|
|
<?php
|
2022-12-17 05:00:34 +01:00
|
|
|
|
2019-01-07 15:34:16 +01:00
|
|
|
namespace Psalm\Example\Plugin;
|
|
|
|
|
2021-12-27 20:18:33 +01:00
|
|
|
use Exception;
|
2019-01-07 15:34:16 +01:00
|
|
|
use PhpParser;
|
|
|
|
use Psalm\CodeLocation;
|
2022-12-18 17:15:15 +01:00
|
|
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
|
|
|
use Psalm\Internal\MethodIdentifier;
|
2021-12-27 20:18:33 +01:00
|
|
|
use Psalm\Issue\PluginIssue;
|
|
|
|
use Psalm\IssueBuffer;
|
2021-01-06 15:05:53 +01:00
|
|
|
use Psalm\Plugin\EventHandler\AfterFunctionCallAnalysisInterface;
|
|
|
|
use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface;
|
|
|
|
use Psalm\Plugin\EventHandler\Event\AfterFunctionCallAnalysisEvent;
|
|
|
|
use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
|
2021-12-14 00:31:46 +01:00
|
|
|
|
2022-12-18 17:15:15 +01:00
|
|
|
use function end;
|
2021-01-06 15:05:53 +01:00
|
|
|
use function explode;
|
|
|
|
use function strtolower;
|
2019-01-07 15:34:16 +01:00
|
|
|
|
|
|
|
/**
|
2021-06-14 14:45:38 +02:00
|
|
|
* Checks that functions and methods are correctly-cased
|
2019-01-07 15:34:16 +01:00
|
|
|
*/
|
2023-10-26 17:00:29 +02:00
|
|
|
final class FunctionCasingChecker implements AfterFunctionCallAnalysisInterface, AfterMethodCallAnalysisInterface
|
2019-01-07 15:34:16 +01:00
|
|
|
{
|
2021-01-06 15:05:53 +01:00
|
|
|
public static function afterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void
|
|
|
|
{
|
|
|
|
$expr = $event->getExpr();
|
|
|
|
$codebase = $event->getCodebase();
|
|
|
|
$declaring_method_id = $event->getDeclaringMethodId();
|
|
|
|
$statements_source = $event->getStatementsSource();
|
2019-01-07 15:34:16 +01:00
|
|
|
if (!$expr->name instanceof PhpParser\Node\Identifier) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2020-10-15 00:51:15 +02:00
|
|
|
/** @psalm-suppress ArgumentTypeCoercion */
|
2021-12-14 01:54:11 +01:00
|
|
|
$method_id = new MethodIdentifier(...explode('::', $declaring_method_id));
|
2020-02-15 02:54:26 +01:00
|
|
|
$function_storage = $codebase->methods->getStorage($method_id);
|
2019-01-07 15:34:16 +01:00
|
|
|
|
|
|
|
if ($function_storage->cased_name === '__call') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-05 21:48:41 +02:00
|
|
|
if ($function_storage->cased_name === '__callStatic') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-07 15:34:16 +01:00
|
|
|
if ($function_storage->cased_name !== (string)$expr->name) {
|
2022-12-17 05:00:34 +01:00
|
|
|
IssueBuffer::maybeAdd(
|
2019-01-07 15:34:16 +01:00
|
|
|
new IncorrectFunctionCasing(
|
|
|
|
'Function is incorrectly cased, expecting ' . $function_storage->cased_name,
|
2022-12-18 17:15:15 +01:00
|
|
|
new CodeLocation($statements_source, $expr->name),
|
2019-01-07 15:34:16 +01:00
|
|
|
),
|
2022-12-18 17:15:15 +01:00
|
|
|
$statements_source->getSuppressedIssues(),
|
2022-12-17 05:00:34 +01:00
|
|
|
);
|
2019-01-07 15:34:16 +01:00
|
|
|
}
|
2021-12-27 20:18:33 +01:00
|
|
|
} catch (Exception $e) {
|
2019-01-07 15:34:16 +01:00
|
|
|
// can throw if storage is missing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 15:05:53 +01:00
|
|
|
public static function afterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent $event): void
|
|
|
|
{
|
|
|
|
$expr = $event->getExpr();
|
|
|
|
$codebase = $event->getCodebase();
|
|
|
|
$statements_source = $event->getStatementsSource();
|
|
|
|
$function_id = $event->getFunctionId();
|
2019-03-16 17:34:48 +01:00
|
|
|
if ($expr->name instanceof PhpParser\Node\Expr) {
|
2019-01-07 15:34:16 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$function_storage = $codebase->functions->getStorage(
|
2021-12-14 00:31:46 +01:00
|
|
|
$statements_source instanceof StatementsAnalyzer
|
2019-01-07 15:34:16 +01:00
|
|
|
? $statements_source
|
|
|
|
: null,
|
2022-12-18 17:15:15 +01:00
|
|
|
strtolower($function_id),
|
2019-01-07 15:34:16 +01:00
|
|
|
);
|
|
|
|
|
2019-11-25 21:20:31 +01:00
|
|
|
if (!$function_storage->cased_name) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-16 17:34:48 +01:00
|
|
|
$function_name_parts = explode('\\', $function_storage->cased_name);
|
|
|
|
|
2023-06-27 15:10:03 +02:00
|
|
|
if (end($function_name_parts) !== $expr->name->getLast()) {
|
2022-12-17 05:00:34 +01:00
|
|
|
IssueBuffer::maybeAdd(
|
2019-01-07 15:34:16 +01:00
|
|
|
new IncorrectFunctionCasing(
|
|
|
|
'Function is incorrectly cased, expecting ' . $function_storage->cased_name,
|
2022-12-18 17:15:15 +01:00
|
|
|
new CodeLocation($statements_source, $expr->name),
|
2019-01-07 15:34:16 +01:00
|
|
|
),
|
2022-12-18 17:15:15 +01:00
|
|
|
$statements_source->getSuppressedIssues(),
|
2022-12-17 05:00:34 +01:00
|
|
|
);
|
2019-01-07 15:34:16 +01:00
|
|
|
}
|
2021-12-27 20:18:33 +01:00
|
|
|
} catch (Exception $e) {
|
2019-01-07 15:34:16 +01:00
|
|
|
// can throw if storage is missing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-26 17:00:29 +02:00
|
|
|
final class IncorrectFunctionCasing extends PluginIssue
|
2021-01-06 15:05:53 +01:00
|
|
|
{
|
2019-01-07 15:34:16 +01:00
|
|
|
}
|