Merge pull request #385 from dantleech/phpstan-level-3

PHPStan Level 3
This commit is contained in:
Rob Lourens 2022-11-21 14:14:04 -08:00 коммит произвёл GitHub
Родитель 3eccfd2733 937564f03f
Коммит 6bd3e3ba00
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 49 добавлений и 38 удалений

Просмотреть файл

@ -25,7 +25,6 @@ cache:
- validation/frameworks - validation/frameworks
before_script: before_script:
- if [[ $STATIC_ANALYSIS = true ]]; then composer require phpstan/phpstan --no-update; fi
- composer install - composer install
- set -e # Stop on first error. - set -e # Stop on first error.
- phpenv config-rm xdebug.ini || true - phpenv config-rm xdebug.ini || true

Просмотреть файл

@ -6,7 +6,8 @@
"php": ">=7.2" "php": ">=7.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^8.5.15" "phpunit/phpunit": "^8.5.15",
"phpstan/phpstan": "^1.8"
}, },
"license": "MIT", "license": "MIT",
"authors": [ "authors": [

Просмотреть файл

@ -1,5 +1,5 @@
parameters: parameters:
level: 2 level: 3
paths: paths:
- src/ - src/
ignoreErrors: ignoreErrors:

Просмотреть файл

@ -140,6 +140,8 @@ abstract class Node implements \JsonSerializable {
while ($node->parent !== null) { while ($node->parent !== null) {
$node = $node->parent; $node = $node->parent;
} }
/** @var SourceFileNode $node */
return $node; return $node;
} }
@ -613,6 +615,7 @@ abstract class Node implements \JsonSerializable {
$namespaceDefinition = null; $namespaceDefinition = null;
} }
/** @var NamespaceDefinition|null $namespaceDefinition */
return $namespaceDefinition; return $namespaceDefinition;
} }

Просмотреть файл

@ -16,7 +16,7 @@ class EnumCaseDeclaration extends Node {
/** @var Token */ /** @var Token */
public $caseKeyword; public $caseKeyword;
/** @var QualifiedName */ /** @var Token */
public $name; public $name;
/** @var Token|null */ /** @var Token|null */

Просмотреть файл

@ -11,7 +11,7 @@ use Microsoft\PhpParser\Token;
class AssignmentExpression extends BinaryExpression { class AssignmentExpression extends BinaryExpression {
/** @var Expression */ /** @var Expression|Token */
public $leftOperand; public $leftOperand;
/** @var Token */ /** @var Token */

Просмотреть файл

@ -11,13 +11,13 @@ use Microsoft\PhpParser\Token;
class BinaryExpression extends Expression { class BinaryExpression extends Expression {
/** @var Expression */ /** @var Expression|Token */
public $leftOperand; public $leftOperand;
/** @var Token */ /** @var Token */
public $operator; public $operator;
/** @var Expression */ /** @var Expression|Token */
public $rightOperand; public $rightOperand;
const CHILD_NAMES = [ const CHILD_NAMES = [

Просмотреть файл

@ -13,9 +13,6 @@ class PrefixUpdateExpression extends UnaryExpression {
/** @var Token */ /** @var Token */
public $incrementOrDecrementOperator; public $incrementOrDecrementOperator;
/** @var Variable */
public $operand;
const CHILD_NAMES = [ const CHILD_NAMES = [
'incrementOrDecrementOperator', 'incrementOrDecrementOperator',
'operand' 'operand'

Просмотреть файл

@ -6,6 +6,7 @@
namespace Microsoft\PhpParser\Node\Expression; namespace Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\MissingToken;
use Microsoft\PhpParser\Node\Expression; use Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Token; use Microsoft\PhpParser\Token;
@ -17,7 +18,7 @@ class SubscriptExpression extends Expression {
/** @var Token */ /** @var Token */
public $openBracketOrBrace; public $openBracketOrBrace;
/** @var Expression */ /** @var Expression|MissingToken */
public $accessExpression; public $accessExpression;
/** @var Token */ /** @var Token */

Просмотреть файл

@ -7,9 +7,10 @@
namespace Microsoft\PhpParser\Node\Expression; namespace Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Node\Expression; use Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Token;
class UnaryExpression extends Expression { class UnaryExpression extends Expression {
/** @var UnaryExpression|Variable */ /** @var Expression|Variable|Token */
public $operand; public $operand;
const CHILD_NAMES = [ const CHILD_NAMES = [

Просмотреть файл

@ -13,9 +13,6 @@ class UnaryOpExpression extends UnaryExpression {
/** @var Token */ /** @var Token */
public $operator; public $operator;
/** @var UnaryExpression */
public $operand;
const CHILD_NAMES = [ const CHILD_NAMES = [
'operator', 'operator',
'operand' 'operand'

Просмотреть файл

@ -6,6 +6,7 @@
namespace Microsoft\PhpParser\Node; namespace Microsoft\PhpParser\Node;
use Microsoft\PhpParser\MissingToken;
use Microsoft\PhpParser\Token; use Microsoft\PhpParser\Token;
trait FunctionReturnType { trait FunctionReturnType {
@ -14,6 +15,6 @@ trait FunctionReturnType {
// TODO: This may be the wrong choice if ?type can ever be mixed with other types in union types // TODO: This may be the wrong choice if ?type can ever be mixed with other types in union types
/** @var Token|null */ /** @var Token|null */
public $questionToken; public $questionToken;
/** @var DelimitedList\QualifiedNameList|null */ /** @var DelimitedList\QualifiedNameList|null|MissingToken */
public $returnTypeList; public $returnTypeList;
} }

Просмотреть файл

@ -6,6 +6,7 @@
namespace Microsoft\PhpParser\Node; namespace Microsoft\PhpParser\Node;
use Microsoft\PhpParser\MissingToken;
use Microsoft\PhpParser\ModifiedTypeInterface; use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait; use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node;
@ -20,7 +21,7 @@ class MissingMemberDeclaration extends Node implements ModifiedTypeInterface {
/** @var Token|null needed along with typeDeclaration for what looked like typed property declarations but was missing VariableName */ /** @var Token|null needed along with typeDeclaration for what looked like typed property declarations but was missing VariableName */
public $questionToken; public $questionToken;
/** @var DelimitedList\QualifiedNameList|null */ /** @var DelimitedList\QualifiedNameList|null|MissingToken */
public $typeDeclarationList; public $typeDeclarationList;
const CHILD_NAMES = [ const CHILD_NAMES = [

Просмотреть файл

@ -6,12 +6,13 @@
namespace Microsoft\PhpParser\Node; namespace Microsoft\PhpParser\Node;
use Microsoft\PhpParser\MissingToken;
use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\DelimitedList; use Microsoft\PhpParser\Node\DelimitedList;
use Microsoft\PhpParser\Token; use Microsoft\PhpParser\Token;
class NamespaceUseClause extends Node { class NamespaceUseClause extends Node {
/** @var QualifiedName */ /** @var QualifiedName|MissingToken */
public $namespaceName; public $namespaceName;
/** @var NamespaceAliasingClause */ /** @var NamespaceAliasingClause */
public $namespaceAliasingClause; public $namespaceAliasingClause;

Просмотреть файл

@ -20,7 +20,7 @@ class InlineHtml extends StatementNode {
public $scriptSectionStartTag; public $scriptSectionStartTag;
/** /**
* @var ExpressionStatement|null used to represent the expression echoed by `<?=` while parsing. * @var EchoStatement|null used to represent the expression echoed by `<?=` while parsing.
* *
* This should always be null in the returned AST, * This should always be null in the returned AST,
* and is deliberately excluded from CHILD_NAMES. * and is deliberately excluded from CHILD_NAMES.

Просмотреть файл

@ -6,6 +6,7 @@
namespace Microsoft\PhpParser\Node\Statement; namespace Microsoft\PhpParser\Node\Statement;
use Microsoft\PhpParser\MissingToken;
use Microsoft\PhpParser\Node\QualifiedName; use Microsoft\PhpParser\Node\QualifiedName;
use Microsoft\PhpParser\Node\StatementNode; use Microsoft\PhpParser\Node\StatementNode;
use Microsoft\PhpParser\Token; use Microsoft\PhpParser\Token;
@ -17,7 +18,7 @@ use Microsoft\PhpParser\Node\SourceFileNode;
class NamespaceDefinition extends StatementNode { class NamespaceDefinition extends StatementNode {
/** @var Token */ /** @var Token */
public $namespaceKeyword; public $namespaceKeyword;
/** @var QualifiedName|null */ /** @var QualifiedName|null|MissingToken */
public $name; public $name;
/** @var CompoundStatementNode|Token */ /** @var CompoundStatementNode|Token */
public $compoundStatementOrSemicolon; public $compoundStatementOrSemicolon;

Просмотреть файл

@ -691,7 +691,7 @@ class Parser {
return $classNode; return $classNode;
} }
private function parseClassMembers($parentNode) : Node { private function parseClassMembers($parentNode) : ClassMembersNode {
$classMembers = new ClassMembersNode(); $classMembers = new ClassMembersNode();
$classMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken); $classMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
$classMembers->classMemberDeclarations = $this->parseList($classMembers, ParseContext::ClassMembers); $classMembers->classMemberDeclarations = $this->parseList($classMembers, ParseContext::ClassMembers);
@ -804,7 +804,7 @@ class Parser {
} }
/** /**
* @return DelimitedList\AttributeElementList * @return DelimitedList\AttributeElementList|null
*/ */
private function parseAttributeElementList(AttributeGroup $parentNode) { private function parseAttributeElementList(AttributeGroup $parentNode) {
return $this->parseDelimitedList( return $this->parseDelimitedList(
@ -1638,13 +1638,14 @@ class Parser {
} }
/** /**
* @param string $className (name of subclass of DelimitedList) * @template TDelimitedList of DelimitedList
* @param class-string<TDelimitedList> $className (name of subclass of DelimitedList)
* @param int|int[] $delimiter * @param int|int[] $delimiter
* @param callable $isElementStartFn * @param callable $isElementStartFn
* @param callable $parseElementFn * @param callable $parseElementFn
* @param Node $parentNode * @param Node $parentNode
* @param bool $allowEmptyElements * @param bool $allowEmptyElements
* @return DelimitedList|null instance of $className * @return TDelimitedList|null instance of $className
*/ */
private function parseDelimitedList($className, $delimiter, $isElementStartFn, $parseElementFn, $parentNode, $allowEmptyElements = false) { private function parseDelimitedList($className, $delimiter, $isElementStartFn, $parseElementFn, $parentNode, $allowEmptyElements = false) {
// TODO consider allowing empty delimiter to be more tolerant // TODO consider allowing empty delimiter to be more tolerant
@ -1994,7 +1995,7 @@ class Parser {
/** /**
* @param Node $parentNode * @param Node $parentNode
* @param bool $force * @param bool $force
* @return Node|MissingToken|array - The expression, or a missing token, or (if $force) an array containing a missed and skipped token * @return Expression|MissingToken|array - The expression, or a missing token, or (if $force) an array containing a missed and skipped token
*/ */
private function parseExpression($parentNode, $force = false) { private function parseExpression($parentNode, $force = false) {
$token = $this->getCurrentToken(); $token = $this->getCurrentToken();
@ -2020,7 +2021,7 @@ class Parser {
/** /**
* @param Node $parentNode * @param Node $parentNode
* @return Expression * @return UnaryExpression|MissingToken|Variable|ThrowExpression
*/ */
private function parseUnaryExpressionOrHigher($parentNode) { private function parseUnaryExpressionOrHigher($parentNode) {
$token = $this->getCurrentToken(); $token = $this->getCurrentToken();
@ -3212,9 +3213,16 @@ class Parser {
private function parseScopedPropertyAccessExpression($expression, $fallbackParentNode): ScopedPropertyAccessExpression { private function parseScopedPropertyAccessExpression($expression, $fallbackParentNode): ScopedPropertyAccessExpression {
$scopedPropertyAccessExpression = new ScopedPropertyAccessExpression(); $scopedPropertyAccessExpression = new ScopedPropertyAccessExpression();
$scopedPropertyAccessExpression->parent = $expression->parent ?? $fallbackParentNode; $scopedPropertyAccessExpression->parent = $expression->parent ?? $fallbackParentNode;
if ($expression instanceof Node) { if ($expression instanceof Node) {
$expression->parent = $scopedPropertyAccessExpression; $expression->parent = $scopedPropertyAccessExpression;
$scopedPropertyAccessExpression->scopeResolutionQualifier = $expression; // TODO ensure always a Node
// scopeResolutionQualifier does not accept `Node` but
// `Expression|QualifiedName|Token`. I'm not sure if we can depend
// on that being the case.
//
// @phpstan-ignore-next-line
$scopedPropertyAccessExpression->scopeResolutionQualifier = $expression;
} }
$scopedPropertyAccessExpression->doubleColon = $this->eat1(TokenKind::ColonColonToken); $scopedPropertyAccessExpression->doubleColon = $this->eat1(TokenKind::ColonColonToken);
@ -3362,18 +3370,18 @@ class Parser {
} }
private function parseEnumCaseDeclaration($parentNode) { private function parseEnumCaseDeclaration($parentNode) {
$classConstDeclaration = new EnumCaseDeclaration(); $enumCaseDeclaration = new EnumCaseDeclaration();
$classConstDeclaration->parent = $parentNode; $enumCaseDeclaration->parent = $parentNode;
$classConstDeclaration->caseKeyword = $this->eat1(TokenKind::CaseKeyword); $enumCaseDeclaration->caseKeyword = $this->eat1(TokenKind::CaseKeyword);
$classConstDeclaration->name = $this->eat($this->nameOrKeywordOrReservedWordTokens); $enumCaseDeclaration->name = $this->eat($this->nameOrKeywordOrReservedWordTokens);
$classConstDeclaration->equalsToken = $this->eatOptional1(TokenKind::EqualsToken); $enumCaseDeclaration->equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
if ($classConstDeclaration->equalsToken !== null) { if ($enumCaseDeclaration->equalsToken !== null) {
// TODO add post-parse rule that checks for invalid assignments // TODO add post-parse rule that checks for invalid assignments
$classConstDeclaration->assignment = $this->parseExpression($classConstDeclaration); $enumCaseDeclaration->assignment = $this->parseExpression($enumCaseDeclaration);
} }
$classConstDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken); $enumCaseDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
return $classConstDeclaration; return $enumCaseDeclaration;
} }
/** /**
@ -3446,7 +3454,7 @@ class Parser {
return $result; return $result;
} }
private function parseInterfaceDeclaration($parentNode) { private function parseInterfaceDeclaration($parentNode): InterfaceDeclaration {
$interfaceDeclaration = new InterfaceDeclaration(); // TODO verify not nested $interfaceDeclaration = new InterfaceDeclaration(); // TODO verify not nested
$interfaceDeclaration->parent = $parentNode; $interfaceDeclaration->parent = $parentNode;
$interfaceDeclaration->interfaceKeyword = $this->eat1(TokenKind::InterfaceKeyword); $interfaceDeclaration->interfaceKeyword = $this->eat1(TokenKind::InterfaceKeyword);
@ -3456,7 +3464,7 @@ class Parser {
return $interfaceDeclaration; return $interfaceDeclaration;
} }
private function parseInterfaceMembers($parentNode) : Node { private function parseInterfaceMembers($parentNode) : InterfaceMembers {
$interfaceMembers = new InterfaceMembers(); $interfaceMembers = new InterfaceMembers();
$interfaceMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken); $interfaceMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
$interfaceMembers->interfaceMemberDeclarations = $this->parseList($interfaceMembers, ParseContext::InterfaceMembers); $interfaceMembers->interfaceMemberDeclarations = $this->parseList($interfaceMembers, ParseContext::InterfaceMembers);