Skip to content

Commit

Permalink
Code around class_exists() does not complain about nonexistent classes
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 19, 2020
1 parent 8b097e5 commit 1605bb2
Show file tree
Hide file tree
Showing 32 changed files with 413 additions and 23 deletions.
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ parameters:
count: 1
path: src/Analyser/MutatingScope.php

-
message: "#^Access to constant EXTENSIONS on an unknown class PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig\\.$#"
count: 1
path: src/Command/CommandHelper.php

-
message: "#^Anonymous function has an unused use \\$container\\.$#"
count: 2
Expand Down
5 changes: 4 additions & 1 deletion src/Analyser/DirectScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public function __construct(
* @param bool $inFirstLevelStatement
* @param array<string, true> $currentlyAssignedExpressions
* @param array<string, Type> $nativeExpressionTypes
* @param array<\PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection> $inFunctionCallsStack
*
* @return MutatingScope
*/
Expand All @@ -92,7 +93,8 @@ public function create(
?ParametersAcceptor $anonymousFunctionReflection = null,
bool $inFirstLevelStatement = true,
array $currentlyAssignedExpressions = [],
array $nativeExpressionTypes = []
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = []
): MutatingScope
{
$scopeClass = $this->scopeClass;
Expand All @@ -119,6 +121,7 @@ public function create(
$inFirstLevelStatement,
$currentlyAssignedExpressions,
$nativeExpressionTypes,
$inFunctionCallsStack,
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain
);
Expand Down
5 changes: 4 additions & 1 deletion src/Analyser/LazyScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public function __construct(
* @param bool $inFirstLevelStatement
* @param array<string, true> $currentlyAssignedExpressions
* @param array<string, Type> $nativeExpressionTypes
* @param array<\PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection> $inFunctionCallsStack
*
* @return MutatingScope
*/
Expand All @@ -63,7 +64,8 @@ public function create(
?ParametersAcceptor $anonymousFunctionReflection = null,
bool $inFirstLevelStatement = true,
array $currentlyAssignedExpressions = [],
array $nativeExpressionTypes = []
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = []
): MutatingScope
{
$scopeClass = $this->scopeClass;
Expand All @@ -90,6 +92,7 @@ public function create(
$inFirstLevelStatement,
$currentlyAssignedExpressions,
$nativeExpressionTypes,
$inFunctionCallsStack,
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain
);
Expand Down
102 changes: 94 additions & 8 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp;
Expand All @@ -19,6 +20,7 @@
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\EncapsedStringPart;
use PhpParser\Node\Scalar\LNumber;
Expand All @@ -27,6 +29,7 @@
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ConstantReflection;
use PHPStan\Reflection\Dummy\DummyConstructorReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Native\NativeParameterReflection;
use PHPStan\Reflection\ParametersAcceptor;
Expand Down Expand Up @@ -149,6 +152,9 @@ class MutatingScope implements Scope
/** @var array<string, true> */
private $currentlyAssignedExpressions = [];

/** @var array<MethodReflection|FunctionReflection> */
private $inFunctionCallsStack = [];

/** @var array<string, Type> */
private $nativeExpressionTypes;

Expand Down Expand Up @@ -177,6 +183,7 @@ class MutatingScope implements Scope
* @param bool $inFirstLevelStatement
* @param array<string, true> $currentlyAssignedExpressions
* @param array<string, Type> $nativeExpressionTypes
* @param array<MethodReflection|FunctionReflection> $inFunctionCallsStack
* @param string[] $dynamicConstantNames
* @paarm bool $treatPhpDocTypesAsCertain
*/
Expand All @@ -199,6 +206,7 @@ public function __construct(
bool $inFirstLevelStatement = true,
array $currentlyAssignedExpressions = [],
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = [],
array $dynamicConstantNames = [],
bool $treatPhpDocTypesAsCertain = true
)
Expand All @@ -225,6 +233,7 @@ public function __construct(
$this->inFirstLevelStatement = $inFirstLevelStatement;
$this->currentlyAssignedExpressions = $currentlyAssignedExpressions;
$this->nativeExpressionTypes = $nativeExpressionTypes;
$this->inFunctionCallsStack = $inFunctionCallsStack;
$this->dynamicConstantNames = $dynamicConstantNames;
$this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
}
Expand Down Expand Up @@ -1793,6 +1802,7 @@ public function doNotTreatPhpDocTypesAsCertain(): Scope
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
$this->nativeExpressionTypes,
$this->inFunctionCallsStack,
$this->dynamicConstantNames,
false
);
Expand Down Expand Up @@ -2027,6 +2037,74 @@ public function isSpecified(Expr $node): bool
&& $this->moreSpecificTypes[$exprString]->getCertainty()->yes();
}

/**
* @param MethodReflection|FunctionReflection $reflection
* @return self
*/
public function pushInFunctionCall($reflection): self
{
$stack = $this->inFunctionCallsStack;
$stack[] = $reflection;

return $this->scopeFactory->create(
$this->context,
$this->isDeclareStrictTypes(),
$this->getFunction(),
$this->getNamespace(),
$this->getVariableTypes(),
$this->moreSpecificTypes,
$this->inClosureBindScopeClass,
$this->anonymousFunctionReflection,
$this->isInFirstLevelStatement(),
$this->currentlyAssignedExpressions,
$this->nativeExpressionTypes,
$stack
);
}

public function popInFunctionCall(): self
{
$stack = $this->inFunctionCallsStack;
array_pop($stack);

return $this->scopeFactory->create(
$this->context,
$this->isDeclareStrictTypes(),
$this->getFunction(),
$this->getNamespace(),
$this->getVariableTypes(),
$this->moreSpecificTypes,
$this->inClosureBindScopeClass,
$this->anonymousFunctionReflection,
$this->isInFirstLevelStatement(),
$this->currentlyAssignedExpressions,
$this->nativeExpressionTypes,
$stack
);
}

public function isInClassExists(string $className): bool
{
foreach ($this->inFunctionCallsStack as $inFunctionCall) {
if (!$inFunctionCall instanceof FunctionReflection) {
continue;
}

if (in_array($inFunctionCall->getName(), [
'class_exists',
'interface_exists',
'trait_exists',
], true)) {
return true;
}
}
$expr = new FuncCall(new FullyQualified('class_exists'), [
new Arg(new String_($className)),
]);

return (new ConstantBooleanType(true))->isSuperTypeOf($this->getType($expr))->yes();
}

public function enterClass(ClassReflection $classReflection): self
{
return $this->scopeFactory->create(
Expand Down Expand Up @@ -2628,7 +2706,8 @@ public function assignVariable(string $variableName, Type $type, ?TrinaryLogic $
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
$nativeTypes
$nativeTypes,
$this->inFunctionCallsStack
);
}

Expand Down Expand Up @@ -2710,7 +2789,8 @@ public function specifyExpressionType(Expr $expr, Type $type): self
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
$nativeTypes
$nativeTypes,
$this->inFunctionCallsStack
);
} elseif ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) {
$constantArrays = TypeUtils::getConstantArrays($this->getType($expr->var));
Expand Down Expand Up @@ -2881,7 +2961,8 @@ public function exitFirstLevelStatements(): self
$this->anonymousFunctionReflection,
false,
$this->currentlyAssignedExpressions,
$this->nativeExpressionTypes
$this->nativeExpressionTypes,
$this->inFunctionCallsStack
);
}

Expand Down Expand Up @@ -2946,7 +3027,8 @@ public function mergeWith(?self $otherScope): self
array_map($typeToVariableHolder, $otherScope->nativeExpressionTypes)
), static function (VariableTypeHolder $holder): bool {
return $holder->getCertainty()->yes();
}))
})),
[]
);
}

Expand Down Expand Up @@ -3009,7 +3091,8 @@ public function processFinallyScope(self $finallyScope, self $originalFinallySco
array_map($typeToVariableHolder, $this->nativeExpressionTypes),
array_map($typeToVariableHolder, $finallyScope->nativeExpressionTypes),
array_map($typeToVariableHolder, $originalFinallyScope->nativeExpressionTypes)
))
)),
[]
);
}

Expand Down Expand Up @@ -3097,7 +3180,8 @@ public function processClosureScope(
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
[],
$this->nativeExpressionTypes
$this->nativeExpressionTypes,
$this->inFunctionCallsStack
);
}

Expand Down Expand Up @@ -3142,7 +3226,8 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
[],
$nativeTypes
$nativeTypes,
[]
);
}

Expand Down Expand Up @@ -3180,7 +3265,8 @@ public function generalizeWith(self $otherScope): self
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
[],
$nativeTypes
$nativeTypes,
[]
);
}

Expand Down
8 changes: 8 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,10 @@ private function processArgs(
$parameters = $parametersAcceptor->getParameters();
}

if ($calleeReflection !== null) {
$scope = $scope->pushInFunctionCall($calleeReflection);
}

$hasYield = false;
foreach ($args as $i => $arg) {
$nodeCallback($arg, $scope);
Expand Down Expand Up @@ -2185,6 +2189,10 @@ private function processArgs(
$scope = $scope->restoreOriginalScopeAfterClosureBind($originalScope);
}

if ($calleeReflection !== null) {
$scope = $scope->popInFunctionCall();
}

return new ExpressionResult($scope, $hasYield);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public function getTypeFromValue($value): Type;

public function isSpecified(Expr $node): bool;

public function isInClassExists(string $className): bool;

public function isInClosureBind(): bool;

public function isParameterValueNullable(Param $parameter): bool;
Expand Down
6 changes: 5 additions & 1 deletion src/Analyser/ScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PHPStan\Analyser;

use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Type\Type;

Expand All @@ -20,6 +22,7 @@ interface ScopeFactory
* @param bool $inFirstLevelStatement
* @param array<string, true> $currentlyAssignedExpressions
* @param array<string, Type> $nativeExpressionTypes
* @param array<MethodReflection|FunctionReflection> $inFunctionCallsStack
*
* @return MutatingScope
*/
Expand All @@ -34,7 +37,8 @@ public function create(
?ParametersAcceptor $anonymousFunctionReflection = null,
bool $inFirstLevelStatement = true,
array $currentlyAssignedExpressions = [],
array $nativeExpressionTypes = []
array $nativeExpressionTypes = [],
array $inFunctionCallsStack = []
): MutatingScope;

}
4 changes: 4 additions & 0 deletions src/Rules/Classes/ClassConstantRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public function processNode(Node $node, Scope $scope): array
$className = $currentClassReflection->getParentClass()->getName();
} else {
if (!$this->reflectionProvider->hasClass($className)) {
if ($scope->isInClassExists($className)) {
return [];
}

if (strtolower($constantName) === 'class') {
return [
RuleErrorBuilder::message(sprintf('Class %s not found.', $className))->build(),
Expand Down
4 changes: 4 additions & 0 deletions src/Rules/Classes/ExistingClassInInstanceOfRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public function processNode(Node $node, Scope $scope): array
}

if (!$this->reflectionProvider->hasClass($name)) {
if ($scope->isInClassExists($name)) {
return [];
}

return [
RuleErrorBuilder::message(sprintf('Class %s not found.', $name))->line($class->getLine())->build(),
];
Expand Down
4 changes: 4 additions & 0 deletions src/Rules/Classes/InstantiationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ private function checkClassName(string $class, Node $node, Scope $scope): array
$classReflection = $scope->getClassReflection()->getParentClass();
} else {
if (!$this->reflectionProvider->hasClass($class)) {
if ($scope->isInClassExists($class)) {
return [];
}

return [
RuleErrorBuilder::message(sprintf('Instantiated class %s not found.', $class))->build(),
];
Expand Down
3 changes: 3 additions & 0 deletions src/Rules/Exceptions/CaughtExceptionExistenceRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public function processNode(Node $node, Scope $scope): array
foreach ($node->types as $class) {
$className = (string) $class;
if (!$this->reflectionProvider->hasClass($className)) {
if ($scope->isInClassExists($className)) {
continue;
}
$errors[] = RuleErrorBuilder::message(sprintf('Caught class %s not found.', $className))->line($class->getLine())->build();
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Rules/Methods/CallStaticMethodsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ public function processNode(Node $node, Scope $scope): array
$classReflection = $currentClassReflection->getParentClass();
} else {
if (!$this->reflectionProvider->hasClass($className)) {
if ($scope->isInClassExists($className)) {
return [];
}

return [
RuleErrorBuilder::message(sprintf(
'Call to static method %s() on an unknown class %s.',
Expand Down
Loading

0 comments on commit 1605bb2

Please sign in to comment.