Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error on references of PHPStan internal class names #2932

Merged
merged 3 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -833,11 +833,17 @@ services:
reportMaybes: %reportMaybes%
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\ClassNameCheck

-
class: PHPStan\Rules\ClassCaseSensitivityCheck
arguments:
checkInternalClassCaseSensitivity: %checkInternalClassCaseSensitivity%

-
class: PHPStan\Rules\ClassForbiddenNameCheck

-
class: PHPStan\Rules\Classes\LocalTypeAliasesCheck
arguments:
Expand Down
14 changes: 7 additions & 7 deletions src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use PHPStan\Reflection\PhpVersionStaticAccessor;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\Classes\DuplicateClassDeclarationRule;
use PHPStan\Rules\Classes\DuplicateDeclarationRule;
use PHPStan\Rules\Classes\ExistingClassesInClassImplementsRule;
Expand All @@ -26,6 +25,7 @@
use PHPStan\Rules\Classes\LocalTypeAliasesCheck;
use PHPStan\Rules\Classes\LocalTypeAliasesRule;
use PHPStan\Rules\Classes\LocalTypeTraitAliasesRule;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\DirectRegistry as DirectRuleRegistry;
use PHPStan\Rules\FunctionDefinitionCheck;
use PHPStan\Rules\Functions\DuplicateFunctionDeclarationRule;
Expand Down Expand Up @@ -148,7 +148,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
$templateTypeCheck = $container->getByType(TemplateTypeCheck::class);
$varianceCheck = $container->getByType(VarianceCheck::class);
$reflectionProvider = $container->getByType(ReflectionProvider::class);
$classCaseSensitivityCheck = $container->getByType(ClassCaseSensitivityCheck::class);
$classNameCheck = $container->getByType(ClassNameCheck::class);
$functionDefinitionCheck = $container->getByType(FunctionDefinitionCheck::class);
$missingTypehintCheck = $container->getByType(MissingTypehintCheck::class);
$unresolvableTypeHelper = $container->getByType(UnresolvableTypeHelper::class);
Expand All @@ -159,13 +159,13 @@ private function getRuleRegistry(Container $container): RuleRegistry

$rules = [
// level 0
new ExistingClassesInClassImplementsRule($classCaseSensitivityCheck, $reflectionProvider),
new ExistingClassesInInterfaceExtendsRule($classCaseSensitivityCheck, $reflectionProvider),
new ExistingClassInClassExtendsRule($classCaseSensitivityCheck, $reflectionProvider),
new ExistingClassInTraitUseRule($classCaseSensitivityCheck, $reflectionProvider),
new ExistingClassesInClassImplementsRule($classNameCheck, $reflectionProvider),
new ExistingClassesInInterfaceExtendsRule($classNameCheck, $reflectionProvider),
new ExistingClassInClassExtendsRule($classNameCheck, $reflectionProvider),
new ExistingClassInTraitUseRule($classNameCheck, $reflectionProvider),
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
new ExistingClassesInPropertiesRule($reflectionProvider, $classNameCheck, $unresolvableTypeHelper, $phpVersion, true, false),
new OverridingMethodRule($phpVersion, new MethodSignatureRule($phpClassReflectionExtension, true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $phpClassReflectionExtension, $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc'], $container->getParameter('checkMissingOverrideMethodAttribute')),
new DuplicateDeclarationRule(),
new LocalTypeAliasesRule($localTypeAliasesCheck),
Expand Down
4 changes: 2 additions & 2 deletions src/Rules/AttributesCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AttributesCheck
public function __construct(
private ReflectionProvider $reflectionProvider,
private FunctionCallParametersCheck $functionCallParametersCheck,
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private bool $deprecationRulesInstalled,
)
{
Expand Down Expand Up @@ -67,7 +67,7 @@ public function check(
$errors[] = RuleErrorBuilder::message(sprintf('Attribute class %s is abstract.', $name))->line($attribute->getLine())->build();
}

foreach ($this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $attribute)]) as $caseSensitivityError) {
foreach ($this->classCheck->checkClassNames([new ClassNameNodePair($name, $attribute)]) as $caseSensitivityError) {
$errors[] = $caseSensitivityError;
}

Expand Down
60 changes: 60 additions & 0 deletions src/Rules/ClassForbiddenNameCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules;

use function sprintf;
use function str_starts_with;
use function strpos;
use function substr;

class ClassForbiddenNameCheck
{

private const INTERNAL_CLASS_PREFIXES = [
'PHPStan' => '_PHPStan_',
'Rector' => 'RectorPrefix',
'PHP-Scoper' => '_PhpScoper',
];

/**
* @param ClassNameNodePair[] $pairs
* @return RuleError[]
*/
public function checkClassNames(array $pairs): array
{
$errors = [];
foreach ($pairs as $pair) {
$className = $pair->getClassName();

$projectName = null;
foreach (self::INTERNAL_CLASS_PREFIXES as $project => $prefix) {
if (str_starts_with($className, $prefix)) {
$projectName = $project;
break;
}
}

if ($projectName === null) {
continue;
}

$error = RuleErrorBuilder::message(sprintf(
'Referencing prefixed %s class: %s.',
$projectName,
$className,
))->line($pair->getNode()->getLine())->nonIgnorable();

if (strpos($className, '\\') !== false) {
$error->tip(sprintf(
'This is most likely unintentional. Did you mean to type %s?',
substr($className, strpos($className, '\\')),
));
}

$errors[] = $error->build();
}

return $errors;
}

}
35 changes: 35 additions & 0 deletions src/Rules/ClassNameCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules;

class ClassNameCheck
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassForbiddenNameCheck $classForbiddenNameCheck,
)
{
}

/**
* @param ClassNameNodePair[] $pairs
* @return RuleError[]
*/
public function checkClassNames(array $pairs, bool $checkClassCaseSensitivity = true): array
{
$errors = [];

if ($checkClassCaseSensitivity) {
foreach ($this->classCaseSensitivityCheck->checkClassNames($pairs) as $error) {
$errors[] = $error;
}
}
foreach ($this->classForbiddenNameCheck->checkClassNames($pairs) as $error) {
$errors[] = $error;
}

return $errors;
}

}
6 changes: 3 additions & 3 deletions src/Rules/Classes/ClassConstantRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use PHPStan\Internal\SprintfHelper;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -34,7 +34,7 @@ class ClassConstantRule implements Rule
public function __construct(
private ReflectionProvider $reflectionProvider,
private RuleLevelHelper $ruleLevelHelper,
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private PhpVersion $phpVersion,
)
{
Expand Down Expand Up @@ -101,7 +101,7 @@ public function processNode(Node $node, Scope $scope): array
];
}

$messages = $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($className, $class)]);
$messages = $this->classCheck->checkClassNames([new ClassNameNodePair($className, $class)]);

$classType = $scope->resolveTypeByName($class);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Rules/Classes/ExistingClassInClassExtendsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -18,7 +18,7 @@ class ExistingClassInClassExtendsRule implements Rule
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private ReflectionProvider $reflectionProvider,
)
{
Expand All @@ -35,7 +35,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}
$extendedClassName = (string) $node->extends;
$messages = $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($extendedClassName, $node->extends)]);
$messages = $this->classCheck->checkClassNames([new ClassNameNodePair($extendedClassName, $node->extends)]);
$currentClassName = null;
if (isset($node->namespacedName)) {
$currentClassName = (string) $node->namespacedName;
Expand Down
17 changes: 10 additions & 7 deletions src/Rules/Classes/ExistingClassInInstanceOfRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use PhpParser\Node\Expr\Instanceof_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -24,7 +24,7 @@ class ExistingClassInInstanceOfRule implements Rule

public function __construct(
private ReflectionProvider $reflectionProvider,
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private bool $checkClassCaseSensitivity,
)
{
Expand Down Expand Up @@ -69,13 +69,16 @@ public function processNode(Node $node, Scope $scope): array
return [
RuleErrorBuilder::message(sprintf('Class %s not found.', $name))->line($class->getLine())->discoveringSymbolsTip()->build(),
];
} elseif ($this->checkClassCaseSensitivity) {
$errors = array_merge(
$errors,
$this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $class)]),
);
}

$errors = array_merge(
$errors,
$this->classCheck->checkClassNames(
[new ClassNameNodePair($name, $class)],
$this->checkClassCaseSensitivity,
),
);

$classReflection = $this->reflectionProvider->getClass($name);

if ($classReflection->isTrait()) {
Expand Down
6 changes: 3 additions & 3 deletions src/Rules/Classes/ExistingClassInTraitUseRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -20,7 +20,7 @@ class ExistingClassInTraitUseRule implements Rule
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private ReflectionProvider $reflectionProvider,
)
{
Expand All @@ -33,7 +33,7 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
$messages = $this->classCaseSensitivityCheck->checkClassNames(
$messages = $this->classCheck->checkClassNames(
array_map(static fn (Node\Name $traitName): ClassNameNodePair => new ClassNameNodePair((string) $traitName, $traitName), $node->traits),
);

Expand Down
6 changes: 3 additions & 3 deletions src/Rules/Classes/ExistingClassesInClassImplementsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -19,7 +19,7 @@ class ExistingClassesInClassImplementsRule implements Rule
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private ReflectionProvider $reflectionProvider,
)
{
Expand All @@ -32,7 +32,7 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
$messages = $this->classCaseSensitivityCheck->checkClassNames(
$messages = $this->classCheck->checkClassNames(
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->implements),
);

Expand Down
6 changes: 3 additions & 3 deletions src/Rules/Classes/ExistingClassesInEnumImplementsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -19,7 +19,7 @@ class ExistingClassesInEnumImplementsRule implements Rule
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private ReflectionProvider $reflectionProvider,
)
{
Expand All @@ -32,7 +32,7 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
$messages = $this->classCaseSensitivityCheck->checkClassNames(
$messages = $this->classCheck->checkClassNames(
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->implements),
);

Expand Down
6 changes: 3 additions & 3 deletions src/Rules/Classes/ExistingClassesInInterfaceExtendsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
Expand All @@ -19,7 +19,7 @@ class ExistingClassesInInterfaceExtendsRule implements Rule
{

public function __construct(
private ClassCaseSensitivityCheck $classCaseSensitivityCheck,
private ClassNameCheck $classCheck,
private ReflectionProvider $reflectionProvider,
)
{
Expand All @@ -32,7 +32,7 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
$messages = $this->classCaseSensitivityCheck->checkClassNames(
$messages = $this->classCheck->checkClassNames(
array_map(static fn (Node\Name $interfaceName): ClassNameNodePair => new ClassNameNodePair((string) $interfaceName, $interfaceName), $node->extends),
);

Expand Down