[DX] Add strict PHPStan rules - step #4 (#1333)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2021-11-28 20:48:56 +03:00 committed by GitHub
parent 6340d263f4
commit 185ee1b473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 109 additions and 85 deletions

View File

@ -177,7 +177,7 @@ final class PhpDocInfoPrinter
if (StringUtils::isMatch(
$output,
self::OPENING_DOCBLOCK_REGEX
) && $output && ! StringUtils::isMatch($output, self::CLOSING_DOCBLOCK_REGEX)) {
) && ! StringUtils::isMatch($output, self::CLOSING_DOCBLOCK_REGEX)) {
$output .= ' */';
}
@ -247,7 +247,12 @@ final class PhpDocInfoPrinter
{
$lastTokenPosition = $this->getCurrentPhpDocInfo()
->getPhpDocNode()
->getAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION) ?: $this->currentTokenPosition;
->getAttribute(PhpDocAttributeKey::LAST_PHP_DOC_TOKEN_POSITION);
if ($lastTokenPosition === null) {
$lastTokenPosition = $this->currentTokenPosition;
}
if ($lastTokenPosition === 0) {
$lastTokenPosition = 1;
}

View File

@ -56,7 +56,7 @@ final class FileHashComputer
$loaderResolver = new LoaderResolver($fileLoaders);
$loader = $loaderResolver->resolve($filePath);
if (! $loader) {
if ($loader === false) {
throw new ShouldNotHappenException();
}

View File

@ -82,7 +82,7 @@ final class ConsoleOutputFormatter implements OutputFormatterInterface
$message = sprintf('<options=bold>%d) %s</>', ++$i, $relativeFilePath);
$this->outputStyle->writeln($message);
$this->outputStyle->newLine();
$this->outputStyle->newline();
$this->outputStyle->writeln($fileDiff->getDiffConsoleFormatted());
$rectorsChangelogsLines = $this->createRectorChangelogLines($fileDiff);

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\NodeTypeResolver\NodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;
use PHPStan\Analyser\Scope;
@ -17,6 +18,8 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\VariableTypeResolver\VariableTypeResolverTest
*
* @implements NodeTypeResolverInterface<Variable>
*/
final class VariableTypeResolver implements NodeTypeResolverInterface
{
@ -27,7 +30,7 @@ final class VariableTypeResolver implements NodeTypeResolverInterface
}
/**
* @return array<class-string<Node>>
* @return array<class-string<Expr>>
*/
public function getNodeClasses(): array
{

View File

@ -86,6 +86,10 @@ final class StaticTypeAnalyzer
private function isAlwaysTruableArrayType(ArrayType $arrayType): bool
{
$itemType = $arrayType->getItemType();
return $itemType instanceof ConstantScalarType && $itemType->getValue();
if (! $itemType instanceof ConstantScalarType) {
return false;
}
return (bool) $itemType->getValue();
}
}

View File

@ -52,7 +52,7 @@ final class DynamicSourceLocatorProvider implements SourceLocatorProviderInterfa
// do not cache for PHPUnit, as in test every fixture is different
$isPHPUnitRun = StaticPHPUnitEnvironment::isPHPUnitRun();
if ($this->aggregateSourceLocator && ! $isPHPUnitRun) {
if ($this->aggregateSourceLocator instanceof AggregateSourceLocator && ! $isPHPUnitRun) {
return $this->aggregateSourceLocator;
}

View File

@ -179,10 +179,7 @@ final class ParallelFileProcessor
$fileDiffs[] = FileDiff::decode($jsonError);
}
// @todo why there is a null check?
if ($postFileCallback !== null) {
$postFileCallback($json[Bridge::FILES_COUNT]);
}
$postFileCallback($json[Bridge::FILES_COUNT]);
$systemErrorsCount += $json[Bridge::SYSTEM_ERRORS_COUNT];
if ($systemErrorsCount >= self::SYSTEM_ERROR_COUNT_LIMIT) {

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace Rector\ReadWrite\Contract;
use PhpParser\Node;
use PhpParser\Node\Expr;
/**
* @template TExpr as Expr
*/
interface ReadNodeAnalyzerInterface
{
public function supports(Node $node): bool;
public function supports(Expr $expr): bool;
public function isRead(Node $node): bool;
/**
* @param TExpr $expr
*/
public function isRead(Expr $expr): bool;
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\ReadWrite\ReadNodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Stmt\Class_;
@ -13,6 +14,9 @@ use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\ReadWrite\Contract\ReadNodeAnalyzerInterface;
/**
* @implements ReadNodeAnalyzerInterface<PropertyFetch|StaticPropertyFetch>
*/
final class LocalPropertyFetchReadNodeAnalyzer implements ReadNodeAnalyzerInterface
{
public function __construct(
@ -23,23 +27,20 @@ final class LocalPropertyFetchReadNodeAnalyzer implements ReadNodeAnalyzerInterf
) {
}
public function supports(Node $node): bool
public function supports(Expr $expr): bool
{
return $node instanceof PropertyFetch || $node instanceof StaticPropertyFetch;
return $expr instanceof PropertyFetch || $expr instanceof StaticPropertyFetch;
}
/**
* @param PropertyFetch $node
*/
public function isRead(Node $node): bool
public function isRead(Expr $expr): bool
{
$class = $this->betterNodeFinder->findParentType($node, Class_::class);
$class = $this->betterNodeFinder->findParentType($expr, Class_::class);
if (! $class instanceof Class_) {
// assume worse to keep node protected
return true;
}
$propertyName = $this->nodeNameResolver->getName($node->name);
$propertyName = $this->nodeNameResolver->getName($expr->name);
if ($propertyName === null) {
// assume worse to keep node protected
return true;

View File

@ -4,12 +4,15 @@ declare(strict_types=1);
namespace Rector\ReadWrite\ReadNodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use Rector\NodeNestingScope\ParentScopeFinder;
use Rector\ReadWrite\Contract\ReadNodeAnalyzerInterface;
use Rector\ReadWrite\NodeFinder\NodeUsageFinder;
/**
* @implements ReadNodeAnalyzerInterface<Variable>
*/
final class VariableReadNodeAnalyzer implements ReadNodeAnalyzerInterface
{
public function __construct(
@ -19,22 +22,22 @@ final class VariableReadNodeAnalyzer implements ReadNodeAnalyzerInterface
) {
}
public function supports(Node $node): bool
public function supports(Expr $expr): bool
{
return $node instanceof Variable;
return $expr instanceof Variable;
}
/**
* @param Variable $node
* @param Variable $expr
*/
public function isRead(Node $node): bool
public function isRead(Expr $expr): bool
{
$parentScope = $this->parentScopeFinder->find($node);
$parentScope = $this->parentScopeFinder->find($expr);
if ($parentScope === null) {
return false;
}
$variableUsages = $this->nodeUsageFinder->findVariableUsages((array) $parentScope->stmts, $node);
$variableUsages = $this->nodeUsageFinder->findVariableUsages((array) $parentScope->stmts, $expr);
foreach ($variableUsages as $variableUsage) {
if ($this->justReadExprAnalyzer->isReadContext($variableUsage)) {
return true;

View File

@ -9,12 +9,18 @@ use PHPStan\Analyser\NameScope;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Type;
/**
* @template TTypeNode as TypeNode
*/
interface PhpDocTypeMapperInterface
{
/**
* @return class-string<TypeNode>
* @return class-string<TTypeNode>
*/
public function getNodeType(): string;
/**
* @param TTypeNode $typeNode
*/
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type;
}

View File

@ -14,7 +14,6 @@ use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\StaticTypeMapper\StaticTypeMapper;
@ -96,15 +95,10 @@ final class NameScopeFactory
$aliasName = $useUse->getAlias()
->name;
$useName = $useUse->name->toString();
if (! is_string($useName)) {
throw new ShouldNotHappenException();
}
// uses must be lowercase, as PHPStan lowercases it
$lowercasedAliasName = strtolower($aliasName);
$useNamesByAlias[$lowercasedAliasName] = $useName;
$useNamesByAlias[$lowercasedAliasName] = $useUse->name->toString();
}
}

View File

@ -27,6 +27,9 @@ use Rector\StaticTypeMapper\ValueObject\Type\ParentStaticType;
use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType;
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;
/**
* @implements PhpDocTypeMapperInterface<IdentifierTypeNode>
*/
final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
{
public function __construct(
@ -38,9 +41,6 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
) {
}
/**
* @return class-string<TypeNode>
*/
public function getNodeType(): string
{
return IdentifierTypeNode::class;

View File

@ -14,13 +14,13 @@ use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface;
use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper;
use Symfony\Contracts\Service\Attribute\Required;
/**
* @implements PhpDocTypeMapperInterface<IntersectionTypeNode>
*/
final class IntersectionTypeMapper implements PhpDocTypeMapperInterface
{
private PhpDocTypeMapper $phpDocTypeMapper;
/**
* @return class-string<TypeNode>
*/
public function getNodeType(): string
{
return IntersectionTypeNode::class;

View File

@ -15,6 +15,9 @@ use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface;
/**
* @implements PhpDocTypeMapperInterface<NullableTypeNode>
*/
final class NullableTypeMapper implements PhpDocTypeMapperInterface
{
public function __construct(
@ -23,17 +26,11 @@ final class NullableTypeMapper implements PhpDocTypeMapperInterface
) {
}
/**
* @return class-string<TypeNode>
*/
public function getNodeType(): string
{
return NullableTypeNode::class;
}
/**
* @param NullableTypeNode $typeNode
*/
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
{
$type = $typeNode->type;

View File

@ -14,6 +14,9 @@ use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface;
use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper;
use Symfony\Contracts\Service\Attribute\Required;
/**
* @implements PhpDocTypeMapperInterface<UnionTypeNode>
*/
final class UnionTypeMapper implements PhpDocTypeMapperInterface
{
private PhpDocTypeMapper $phpDocTypeMapper;
@ -23,9 +26,6 @@ final class UnionTypeMapper implements PhpDocTypeMapperInterface
) {
}
/**
* @return class-string<TypeNode>
*/
public function getNodeType(): string
{
return UnionTypeNode::class;

View File

@ -17,9 +17,6 @@ use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
*/
final class ExprNodeMapper implements PhpParserNodeMapperInterface
{
/**
* @return class-string<Node>
*/
public function getNodeType(): string
{
return Expr::class;

View File

@ -28,9 +28,6 @@ final class FullyQualifiedNodeMapper implements PhpParserNodeMapperInterface
) {
}
/**
* @return class-string<Node>
*/
public function getNodeType(): string
{
return FullyQualified::class;

View File

@ -20,9 +20,6 @@ final class IdentifierNodeMapper implements PhpParserNodeMapperInterface
) {
}
/**
* @return class-string<Node>
*/
public function getNodeType(): string
{
return Identifier::class;

View File

@ -42,9 +42,6 @@ final class NameNodeMapper implements PhpParserNodeMapperInterface
) {
}
/**
* @return class-string<Node>
*/
public function getNodeType(): string
{
return Name::class;

View File

@ -15,9 +15,6 @@ use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
*/
final class StringNodeMapper implements PhpParserNodeMapperInterface
{
/**
* @return class-string<Node>
*/
public function getNodeType(): string
{
return String_::class;

View File

@ -590,3 +590,12 @@ parameters:
-
message: '#Strict comparison using \!\=\= between null and null will always evaluate to false#'
path: rules/Renaming/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector.php
# stricter child type on purpose
- '#Parameter \#1 \$tokenIterator \(Rector\\BetterPhpDocParser\\ValueObject\\Parser\\BetterTokenIterator\) of method Rector\\BetterPhpDocParser\\PhpDocParser\\BetterPhpDocParser\:\:parseTagValue\(\) should be contravariant with parameter \$tokens \(PHPStan\\PhpDocParser\\Parser\\TokenIterator\) of method PHPStan\\PhpDocParser\\Parser\\PhpDocParser\:\:parseTagValue\(\)#'
# on purpose
- '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" for static reflection to work#'
# stricter child type on purpose
- '#Parameter \#1 \$tokenIterator \(Rector\\BetterPhpDocParser\\ValueObject\\Parser\\BetterTokenIterator\) of method Rector\\BetterPhpDocParser\\PhpDocParser\\BetterPhpDocParser\:\:parseTagValue\(\) should be contravariant with parameter \$tokens \(PHPStan\\PhpDocParser\\Parser\\TokenIterator\) of method PHPStan\\PhpDocParser\\Parser\\PhpDocParser\:\:parseTagValue\(\)#'

View File

@ -78,7 +78,7 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?FuncCall
{
$isJustSwapped = $node->getAttribute(self::JUST_SWAPPED);
$isJustSwapped = (bool) $node->getAttribute(self::JUST_SWAPPED, false);
if ($isJustSwapped) {
return null;
}

View File

@ -110,7 +110,7 @@ CODE_SAMPLE
$classReflection = $this->reflectionProvider->getClass($propertyFetchVarType->getClassName());
if (! $classReflection->hasProperty($propertyFetchName) || $classReflection->isBuiltIn()) {
if (! $classReflection->hasProperty($propertyFetchName) || $classReflection->isBuiltin()) {
$newNodes[] = $this->replaceToPropertyExistsWithNullCheck(
$issetVar->var,
$propertyFetchName,

View File

@ -152,7 +152,7 @@ final class UseImportsAdder
private function isCurrentNamespace(string $namespaceName, ObjectType $objectType): bool
{
$afterCurrentNamespace = Strings::after($objectType->getClassName(), $namespaceName . '\\');
if (! $afterCurrentNamespace) {
if ($afterCurrentNamespace === null) {
return false;
}

View File

@ -219,10 +219,14 @@ final class ShortNameResolver
$shortNamesToFullyQualifiedNames = [];
foreach ($shortNames as $shortName) {
$stmtsMatchedName = $this->useImportNameMatcher->matchNameWithStmts($shortName, $stmts);
if ($reflectionClass instanceof ReflectionClass) {
$fullyQualifiedName = Reflection::expandClassName($shortName, $reflectionClass);
} elseif (is_string($stmtsMatchedName)) {
$fullyQualifiedName = $stmtsMatchedName;
} else {
$fullyQualifiedName = $this->useImportNameMatcher->matchNameWithStmts($shortName, $stmts) ?: $shortName;
$fullyQualifiedName = $shortName;
}
$shortNamesToFullyQualifiedNames[$shortName] = $fullyQualifiedName;

View File

@ -43,7 +43,12 @@ final class ClassNaming
$name = trim($name, '\\');
return Strings::after($name, '\\', -1) ?: $name;
$shortName = Strings::after($name, '\\', -1);
if (is_string($shortName)) {
return $shortName;
}
return $name;
}
public function getNamespace(string $fullyQualifiedName): ?string

View File

@ -92,8 +92,8 @@ final class NameImporter
// Importing root namespace classes (like \DateTime) is optional
if (! $this->parameterProvider->provideBoolParameter(Option::IMPORT_SHORT_CLASSES)) {
$name = $this->nodeNameResolver->getName($name);
if ($name !== null && substr_count($name, '\\') === 0) {
$stringName = $this->nodeNameResolver->getName($name);
if ($stringName !== null && substr_count($stringName, '\\') === 0) {
return true;
}
}
@ -161,7 +161,7 @@ final class NameImporter
$fullName = $name->toString();
$autoImportNames = $this->parameterProvider->provideParameter(Option::AUTO_IMPORT_NAMES);
$autoImportNames = $this->parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES);
if ($autoImportNames && ! $parentNode instanceof Node && ! \str_contains(
$fullName,
'\\'

View File

@ -96,7 +96,7 @@ CODE_SAMPLE
foreach ($node->parts as $part) {
if ($part instanceof EncapsedStringPart) {
$this->collectEncapsedStringPart($part);
} elseif ($part instanceof Expr) {
} else {
$this->collectExpr($part);
}
}

View File

@ -29,7 +29,7 @@ final class BuiltInMethodAnalyzer
}
foreach ($classReflection->getInterfaces() as $interfaceReflection) {
if (! $interfaceReflection->isBuiltIn()) {
if (! $interfaceReflection->isBuiltin()) {
continue;
}

View File

@ -50,7 +50,7 @@ final class InflectorSingularResolver
public function resolve(string $currentName): string
{
$matchBy = Strings::match($currentName, self::BY_MIDDLE_REGEX);
if ($matchBy) {
if ($matchBy !== null) {
return Strings::substring($currentName, 0, -strlen($matchBy['by']));
}

View File

@ -61,7 +61,7 @@ CODE_SAMPLE
return null;
}
return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Countable', 'is_countable') ?: $node;
return $this->isArrayAndDualCheckToAble->processBooleanOr($node, 'Countable', 'is_countable');
}
public function provideMinPhpVersion(): int

View File

@ -192,7 +192,7 @@ CODE_SAMPLE
return;
}
if (! $methodName) {
if (! is_string($methodName)) {
return;
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\Privatization\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt;
@ -45,7 +46,7 @@ final class PropertyFetchByMethodAnalyzer
$propertyUsageByMethods = [];
foreach ($propertyNames as $propertyName) {
if ($this->isPropertyHasDefaultValue($class, $propertyName)) {
if ($this->isPropertyWithDefaultValue($class, $propertyName)) {
continue;
}
@ -85,10 +86,14 @@ final class PropertyFetchByMethodAnalyzer
return $this->isPropertyChangingInMultipleMethodCalls($classMethod, $propertyName);
}
private function isPropertyHasDefaultValue(Class_ $class, string $propertyName): bool
private function isPropertyWithDefaultValue(Class_ $class, string $propertyName): bool
{
$property = $class->getProperty($propertyName);
return $property instanceof Property && $property->props[0]->default;
if (! $property instanceof Property) {
return false;
}
return $property->props[0]->default instanceof Expr;
}
private function isInConstructWithPropertyChanging(ClassMethod $classMethod, string $propertyName): bool
@ -149,7 +154,7 @@ final class PropertyFetchByMethodAnalyzer
private function verifyPropertyReadInIf(?bool $isPropertyReadInIf, Node $node, string $propertyName): ?bool
{
if ($node instanceof If_) {
$isPropertyReadInIf = $this->refactorIf($node, $propertyName);
return $this->refactorIf($node, $propertyName);
}
return $isPropertyReadInIf;

View File

@ -340,7 +340,7 @@ final class ClassRenamer
$classLike->implements = array_unique($classLike->implements);
foreach ($classLike->implements as $key => $implementName) {
$virtualNode = $implementName->getAttribute(AttributeKey::VIRTUAL_NODE);
$virtualNode = (bool) $implementName->getAttribute(AttributeKey::VIRTUAL_NODE, false);
if (! $virtualNode) {
continue;
}