Skip to content

Commit

Permalink
Config option treatPhpDocTypesAsCertain to ignore always true/false t…
Browse files Browse the repository at this point in the history
…ype comparisons for types coming from phpDocs
  • Loading branch information
ondrejmirtes committed Jan 19, 2020
1 parent 1598d10 commit 222545e
Show file tree
Hide file tree
Showing 49 changed files with 1,660 additions and 110 deletions.
68 changes: 60 additions & 8 deletions conf/config.level4.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@ includes:

rules:
- PHPStan\Rules\Arrays\DeadForeachRule
- PHPStan\Rules\Comparison\BooleanAndConstantConditionRule
- PHPStan\Rules\Comparison\BooleanNotConstantConditionRule
- PHPStan\Rules\Comparison\BooleanOrConstantConditionRule
- PHPStan\Rules\Comparison\ElseIfConstantConditionRule
- PHPStan\Rules\Comparison\IfConstantConditionRule
- PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule
- PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
- PHPStan\Rules\Comparison\UnreachableIfBranchesRule
- PHPStan\Rules\Comparison\UnreachableTernaryElseBranchRule
- PHPStan\Rules\DeadCode\NoopRule
- PHPStan\Rules\DeadCode\UnreachableStatementRule
- PHPStan\Rules\Exceptions\DeadCatchRule
Expand All @@ -27,27 +19,66 @@ services:
class: PHPStan\Rules\Classes\ImpossibleInstanceOfRule
arguments:
checkAlwaysTrueInstanceof: %checkAlwaysTrueInstanceof%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\BooleanAndConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\BooleanOrConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\BooleanNotConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\ElseIfConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\IfConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeFunctionCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeStaticMethodCallRule
arguments:
checkAlwaysTrueCheckTypeFunctionCall: %checkAlwaysTrueCheckTypeFunctionCall%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

Expand All @@ -58,6 +89,27 @@ services:
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\UnreachableIfBranchesRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Comparison\UnreachableTernaryElseBranchRule
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
arguments:
Expand Down
7 changes: 7 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ parameters:
polluteScopeWithLoopInitialAssignments: true
polluteScopeWithAlwaysIterableForeach: true
polluteCatchScopeWithTryAssignments: false
treatPhpDocTypesAsCertain: true
tipsOfTheDay: true
reportMagicMethods: false
reportMagicProperties: false
Expand Down Expand Up @@ -143,6 +144,7 @@ parametersSchema:
polluteScopeWithLoopInitialAssignments: bool()
polluteScopeWithAlwaysIterableForeach: bool()
polluteCatchScopeWithTryAssignments: bool()
treatPhpDocTypesAsCertain: bool()
reportMagicMethods: bool()
reportMagicProperties: bool()
ignoreErrors: listOf(
Expand Down Expand Up @@ -414,11 +416,14 @@ services:

-
class: PHPStan\Rules\Comparison\ConstantConditionRuleHelper
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%

-
class: PHPStan\Rules\Comparison\ImpossibleCheckTypeHelper
arguments:
universalObjectCratesClasses: %universalObjectCratesClasses%
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%

-
class: PHPStan\Rules\FunctionCallParametersCheck
Expand Down Expand Up @@ -834,6 +839,8 @@ services:

-
class: PHPStan\Type\Php\TypeSpecifyingFunctionsDynamicReturnTypeExtension
arguments:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

Expand Down
8 changes: 7 additions & 1 deletion src/Analyser/DirectScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class DirectScopeFactory implements ScopeFactory
/** @var \PHPStan\Rules\Properties\PropertyReflectionFinder */
private $propertyReflectionFinder;

/** @var bool */
private $treatPhpDocTypesAsCertain;

/** @var string[] */
private $dynamicConstantNames;

Expand All @@ -48,6 +51,7 @@ public function __construct(
\PhpParser\PrettyPrinter\Standard $printer,
TypeSpecifier $typeSpecifier,
PropertyReflectionFinder $propertyReflectionFinder,
bool $treatPhpDocTypesAsCertain,
Container $container
)
{
Expand All @@ -58,6 +62,7 @@ public function __construct(
$this->printer = $printer;
$this->typeSpecifier = $typeSpecifier;
$this->propertyReflectionFinder = $propertyReflectionFinder;
$this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
$this->dynamicConstantNames = $container->getParameter('dynamicConstantNames');
}

Expand Down Expand Up @@ -114,7 +119,8 @@ public function create(
$inFirstLevelStatement,
$currentlyAssignedExpressions,
$nativeExpressionTypes,
$this->dynamicConstantNames
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain
);
}

Expand Down
7 changes: 6 additions & 1 deletion src/Analyser/LazyScopeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class LazyScopeFactory implements ScopeFactory
/** @var string[] */
private $dynamicConstantNames;

/** @var bool */
private $treatPhpDocTypesAsCertain;

public function __construct(
string $scopeClass,
Container $container
Expand All @@ -31,6 +34,7 @@ public function __construct(
$this->scopeClass = $scopeClass;
$this->container = $container;
$this->dynamicConstantNames = $container->getParameter('dynamicConstantNames');
$this->treatPhpDocTypesAsCertain = $container->getParameter('treatPhpDocTypesAsCertain');
}

/**
Expand Down Expand Up @@ -86,7 +90,8 @@ public function create(
$inFirstLevelStatement,
$currentlyAssignedExpressions,
$nativeExpressionTypes,
$this->dynamicConstantNames
$this->dynamicConstantNames,
$this->treatPhpDocTypesAsCertain
);
}

Expand Down
108 changes: 99 additions & 9 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ class MutatingScope implements Scope
/** @var string[] */
private $dynamicConstantNames;

/** @var bool */
private $treatPhpDocTypesAsCertain;

/**
* @param \PHPStan\Analyser\ScopeFactory $scopeFactory
* @param ReflectionProvider $reflectionProvider
Expand All @@ -175,6 +178,7 @@ class MutatingScope implements Scope
* @param array<string, true> $currentlyAssignedExpressions
* @param array<string, Type> $nativeExpressionTypes
* @param string[] $dynamicConstantNames
* @paarm bool $treatPhpDocTypesAsCertain
*/
public function __construct(
ScopeFactory $scopeFactory,
Expand All @@ -195,7 +199,8 @@ public function __construct(
bool $inFirstLevelStatement = true,
array $currentlyAssignedExpressions = [],
array $nativeExpressionTypes = [],
array $dynamicConstantNames = []
array $dynamicConstantNames = [],
bool $treatPhpDocTypesAsCertain = true
)
{
if ($namespace === '') {
Expand All @@ -221,6 +226,7 @@ public function __construct(
$this->currentlyAssignedExpressions = $currentlyAssignedExpressions;
$this->nativeExpressionTypes = $nativeExpressionTypes;
$this->dynamicConstantNames = $dynamicConstantNames;
$this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
}

public function getFile(): string
Expand Down Expand Up @@ -505,15 +511,25 @@ private function resolveType(Expr $node): Type
$node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanAnd
|| $node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalAnd
) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
if ($this->treatPhpDocTypesAsCertain) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
} else {
$leftBooleanType = $this->getNativeType($node->left)->toBoolean();
}

if (
$leftBooleanType instanceof ConstantBooleanType
&& !$leftBooleanType->getValue()
) {
return new ConstantBooleanType(false);
}

$rightBooleanType = $this->filterByTruthyValue($node->left)->getType($node->right)->toBoolean();
if ($this->treatPhpDocTypesAsCertain) {
$rightBooleanType = $this->filterByTruthyValue($node->left)->getType($node->right)->toBoolean();
} else {
$rightBooleanType = $this->promoteNativeTypes()->filterByTruthyValue($node->left)->getType($node->right)->toBoolean();
}

if (
$rightBooleanType instanceof ConstantBooleanType
&& !$rightBooleanType->getValue()
Expand All @@ -537,15 +553,24 @@ private function resolveType(Expr $node): Type
$node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanOr
|| $node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalOr
) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
if ($this->treatPhpDocTypesAsCertain) {
$leftBooleanType = $this->getType($node->left)->toBoolean();
} else {
$leftBooleanType = $this->getNativeType($node->left)->toBoolean();
}
if (
$leftBooleanType instanceof ConstantBooleanType
&& $leftBooleanType->getValue()
) {
return new ConstantBooleanType(true);
}

$rightBooleanType = $this->filterByFalseyValue($node->left)->getType($node->right)->toBoolean();
if ($this->treatPhpDocTypesAsCertain) {
$rightBooleanType = $this->filterByFalseyValue($node->left)->getType($node->right)->toBoolean();
} else {
$rightBooleanType = $this->promoteNativeTypes()->filterByFalseyValue($node->left)->getType($node->right)->toBoolean();
}

if (
$rightBooleanType instanceof ConstantBooleanType
&& $rightBooleanType->getValue()
Expand Down Expand Up @@ -663,7 +688,11 @@ private function resolveType(Expr $node): Type
}

if ($node instanceof Expr\Instanceof_) {
$expressionType = $this->getType($node->expr);
if ($this->treatPhpDocTypesAsCertain) {
$expressionType = $this->getType($node->expr);
} else {
$expressionType = $this->getNativeType($node->expr);
}
if (
$this->isInTrait()
&& TypeUtils::findThisType($expressionType) !== null
Expand Down Expand Up @@ -1721,6 +1750,69 @@ public function getNativeType(Expr $expr): Type
return $this->getType($expr);
}

public function doNotTreatPhpDocTypesAsCertain(): Scope
{
if (!$this->treatPhpDocTypesAsCertain) {
return $this;
}

return new self(
$this->scopeFactory,
$this->reflectionProvider,
$this->dynamicReturnTypeExtensionRegistry,
$this->operatorTypeSpecifyingExtensionRegistry,
$this->printer,
$this->typeSpecifier,
$this->propertyReflectionFinder,
$this->context,
$this->declareStrictTypes,
$this->function,
$this->namespace,
$this->variableTypes,
$this->moreSpecificTypes,
$this->inClosureBindScopeClass,
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
$this->nativeExpressionTypes,
$this->dynamicConstantNames,
false
);
}

private function promoteNativeTypes(): self
{
$variableTypes = $this->variableTypes;
foreach ($this->nativeExpressionTypes as $expressionType => $type) {
if (substr($expressionType, 0, 1) !== '$') {
var_dump($expressionType);
throw new \PHPStan\ShouldNotHappenException();
}

$variableName = substr($expressionType, 1);
$has = $this->hasVariableType($variableName);
if ($has->no()) {
throw new \PHPStan\ShouldNotHappenException();
}

$variableTypes[$variableName] = new VariableTypeHolder($type, $has);
}

return $this->scopeFactory->create(
$this->context,
$this->declareStrictTypes,
$this->function,
$this->namespace,
$variableTypes,
$this->moreSpecificTypes,
$this->inClosureBindScopeClass,
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
[]
);
}

/**
* @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $propertyFetch
* @return bool
Expand Down Expand Up @@ -2085,9 +2177,7 @@ private function enterFunctionLike(
): self
{
$variableTypes = $this->getVariableTypes();
$nativeExpressionTypes = array_map(static function (VariableTypeHolder $holder): Type {
return $holder->getType();
}, $variableTypes);
$nativeExpressionTypes = [];
foreach (ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getParameters() as $parameter) {
$variableTypes[$parameter->getName()] = VariableTypeHolder::createYes($parameter->getType());
$nativeExpressionTypes[sprintf('$%s', $parameter->getName())] = $parameter->getNativeType();
Expand Down
Loading

0 comments on commit 222545e

Please sign in to comment.