-
Notifications
You must be signed in to change notification settings - Fork 501
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
Replace error-prone instanceof in Rules classes #3858
base: 2.1.x
Are you sure you want to change the base?
Replace error-prone instanceof in Rules classes #3858
Conversation
&& !$isFirstDeclaration | ||
!$isFirstDeclaration | ||
&& !$method->isPrivate() | ||
&& ($returnType->isNull()->yes() || $returnType->isTrue()->yes() || $returnType->isFalse()->yes()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logical operators are short-circuit evaluated, so rearranging expressions can lead to micro-optimizations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to replace these calls by just accessing the first element. Union types should be properly handled. Multiple values should be iterated over.
bbbe15f
to
776e845
Compare
Since |
9fc4e20
to
c57fe20
Compare
$class = $type->getClassName(); | ||
$referencedClassReflection = $type->getClassReflection(); | ||
$classNames = $type->getObjectClassNames(); | ||
sort($classNames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
This pull request has been marked as ready for review. |
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends contains non-object type %s.', $type->describe(VerbosityLevel::typeOnly()))) | ||
->identifier('requireExtends.nonObject') | ||
->build(); | ||
continue; | ||
} | ||
|
||
$class = $type->getClassName(); | ||
$referencedClassReflection = $type->getClassReflection(); | ||
$classNames = $type->getObjectClassNames(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce this var earlier so you don't need to call getObjectClassNames
twice (methods called on Type can get expensive)
@@ -49,34 +51,38 @@ public function processNode(Node $node, Scope $scope): array | |||
$errors = []; | |||
foreach ($implementsTags as $implementsTag) { | |||
$type = $implementsTag->getType(); | |||
if (!$type instanceof ObjectType) { | |||
if (count($type->getObjectClassNames()) === 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce a var so you don't need to call getObjectClassNames
twice
*/ | ||
private function findConstantStrings(Type $type): array | ||
{ | ||
if ($type instanceof ConstantStringType) { | ||
return [$type]; | ||
if (count($type->getConstantStrings()) > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce a var so you don't need to call getConstantStrings
twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
$arrayKeyValue = $keyType->toArrayKey(); | ||
if ($arrayKeyValue instanceof ConstantIntegerType) { | ||
$arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues(); | ||
if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it better/possible to handle union of scalar values here (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the following?
modified src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php
@@ -71,10 +71,12 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
} else {
$keyType = $itemNode->getScope()->getType($key);
$arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues();
- if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) {
- $autoGeneratedIndex = $autoGeneratedIndex === null
- ? $arrayKeyValues[0]
- : max($autoGeneratedIndex, $arrayKeyValues[0]);
+ foreach ($arrayKeyValues as $arrayKeyValue) {
+ if (is_int($arrayKeyValue)) {
+ $autoGeneratedIndex = $autoGeneratedIndex === null
+ ? $arrayKeyValue
+ : max($autoGeneratedIndex, $arrayKeyValue);
+ }
}
}
Or is it type-level arithmetic?
modified src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php
@@ -42,7 +42,6 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
$valueLines = [];
/**
- * @var int|false|null $autoGeneratedIndex
* - An int value represent the biggest integer used as array key.
* When no key is provided this value + 1 will be used.
* - Null is used as initializer instead of 0 to avoid issue with negative keys.
@@ -63,27 +62,37 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
}
if ($autoGeneratedIndex === null) {
- $autoGeneratedIndex = 0;
- $keyType = new ConstantIntegerType(0);
+ $autoGeneratedIndex = new ConstantIntegerType(0);
} else {
- $keyType = new ConstantIntegerType(++$autoGeneratedIndex);
+ $autoGeneratedIndex = $scope->getType(new Plus(
+ new Int_(1),
+ new TypeExpr($autoGeneratedIndex),
+ ));
}
+ $keyType = $autoGeneratedIndex;
} else {
$keyType = $itemNode->getScope()->getType($key);
- $arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues();
- if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) {
+ $arrayKeyType = $keyType->toArrayKey();
+ if ($autoGeneratedIndex !== false && $arrayKeyType->isInteger()->yes()) {
$autoGeneratedIndex = $autoGeneratedIndex === null
- ? $arrayKeyValues[0]
- : max($autoGeneratedIndex, $arrayKeyValues[0]);
+ ? $keyType
+ : $scope->getType(new FuncCall(
+ new Name('\max'),
+ [
+ new Arg(new TypeExpr($autoGeneratedIndex)),
+ new Arg(new TypeExpr($arrayKeyType)),
+ ]
+ ));
}
}
$autoGeneratedIndex
is explained as follows:
/**
* - An int value represent the biggest integer used as array key.
* When no key is provided this value + 1 will be used.
* - Null is used as initializer instead of 0 to avoid issue with negative keys.
* - False means a non-scalar value was encountered and we cannot be sure of the next keys.
*/
$autoGeneratedIndex = null;
Since the key value actually changes depending on the input value, more appropriate typing is possible with type operations. However, since DuplicateKeysInLiteralArraysRule
has some subtle behavior, I would like to keep the changes in this branch to a minimum.
@@ -57,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array | |||
return $ruleErrorBuilder->treatPhpDocTypesAsCertainTip(); | |||
}; | |||
|
|||
if (!$nodeType->getValue()) { | |||
if (in_array(false, $nodeType->getConstantScalarValues(), true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure but isn't $nodeType->isFalse()->yes()
better ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct, thanks!
$assertValue = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->toBoolean(); | ||
if (!$assertValue instanceof ConstantBooleanType) { | ||
$assertValues = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->getConstantScalarValues(); | ||
if (count($assertValues) !== 1 || !is_bool($assertValues[0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could maybe have kept the toBoolean
and checked
$assertValue->isTrue()->yes() || $assertValue->isFalse()->yes()
Co-authored-by: Markus Staab <[email protected]>
83b0029
to
b031d1d
Compare
Co-authored-by: Vincent Langlet <[email protected]>
Co-authored-by: Markus Staab <[email protected]>
b031d1d
to
5d96286
Compare
This pull request has been marked as ready for review. |
/** @var bool */ | ||
return $assertValue->getConstantScalarValues()[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can return $assertValue->isTrue()->yes()
if you want to avoid the var phpdoc
Reduce usage of
$type instanceof *Type
pattern in Rules classes.refs phpstan/phpstan#8311