From 3510b0a6274cc42f7219367cb3abfc123ffa09d6 Mon Sep 17 00:00:00 2001 From: ekisu Date: Thu, 7 Sep 2023 16:47:14 -0300 Subject: [PATCH 1/6] Allow asserting the type of `$this` --- src/Parser/PhpDocParser.php | 4 +- tests/PHPStan/Parser/PhpDocParserTest.php | 57 ++++++++++++++++++----- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index c21358cf..15a2aa5c 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -1127,15 +1127,13 @@ private function parseAssertParameter(TokenIterator $tokens): array { if ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { $parameter = '$this'; - $requirePropertyOrMethod = true; $tokens->next(); } else { $parameter = $tokens->currentTokenValue(); - $requirePropertyOrMethod = false; $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); } - if ($requirePropertyOrMethod || $tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) { $tokens->consumeTokenType(Lexer::TOKEN_ARROW); $propertyOrMethod = $tokens->currentTokenValue(); diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index b448dc24..97d6cbfe 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -4563,21 +4563,56 @@ public function provideAssertTagsData(): Iterator ]; yield [ - 'invalid $this', + 'OK $this', '/** @phpstan-assert Type $this */', new PhpDocNode([ new PhpDocTagNode( '@phpstan-assert', - new InvalidTagValueNode( - 'Type $this', - new ParserException( - '*/', - Lexer::TOKEN_CLOSE_PHPDOC, - 31, - Lexer::TOKEN_ARROW, - null, - 1 - ) + new AssertTagValueNode( + new IdentifierTypeNode('Type'), + '$this', + false, + '' + ) + ), + ]), + ]; + + yield [ + 'OK $this with description', + '/** @phpstan-assert Type $this assert Type to $this */', + new PhpDocNode([ + new PhpDocTagNode( + '@phpstan-assert', + new AssertTagValueNode( + new IdentifierTypeNode('Type'), + '$this', + false, + 'assert Type to $this' + ) + ), + ]), + ]; + + yield [ + 'OK $this with generic type', + '/** @phpstan-assert GenericType $this */', + new PhpDocNode([ + new PhpDocTagNode( + '@phpstan-assert', + new AssertTagValueNode( + new GenericTypeNode( + new IdentifierTypeNode('GenericType'), + [ + new IdentifierTypeNode('T'), + ], + [ + GenericTypeNode::VARIANCE_INVARIANT, + ] + ), + '$this', + false, + '' ) ), ]), From 36db47aab3ed4eb4c66c2e75e51545b930ca66be Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 01:44:11 +0000 Subject: [PATCH 2/6] Update actions/checkout action to v4 --- .github/workflows/apiref.yml | 2 +- .github/workflows/backward-compatibility.yml | 2 +- .github/workflows/build.yml | 10 +++++----- .github/workflows/create-tag.yml | 2 +- .github/workflows/merge-maintained-branch.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/send-pr.yml | 2 +- .github/workflows/test-slevomat-coding-standard.yml | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/apiref.yml b/.github/workflows/apiref.yml index d3d55f98..85f91693 100644 --- a/.github/workflows/apiref.yml +++ b/.github/workflows/apiref.yml @@ -18,7 +18,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/.github/workflows/backward-compatibility.yml b/.github/workflows/backward-compatibility.yml index eb78a350..213da72c 100644 --- a/.github/workflows/backward-compatibility.yml +++ b/.github/workflows/backward-compatibility.yml @@ -17,7 +17,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61dd4466..d356f34e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -53,10 +53,10 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Checkout build-cs" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: "phpstan/build-cs" path: "build-cs" @@ -104,7 +104,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -144,7 +144,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index 8452d986..a8535014 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -21,7 +21,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.PHPSTAN_BOT_TOKEN }} diff --git a/.github/workflows/merge-maintained-branch.yml b/.github/workflows/merge-maintained-branch.yml index 3aa2b0b3..18d17974 100644 --- a/.github/workflows/merge-maintained-branch.yml +++ b/.github/workflows/merge-maintained-branch.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Merge branch" uses: everlytic/branch-merge@1.1.5 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92b72547..e4a8ac62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Generate changelog id: changelog diff --git a/.github/workflows/send-pr.yml b/.github/workflows/send-pr.yml index bc305f97..023293c7 100644 --- a/.github/workflows/send-pr.yml +++ b/.github/workflows/send-pr.yml @@ -18,7 +18,7 @@ jobs: php-version: "8.1" - name: "Checkout phpstan-src" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: phpstan/phpstan-src path: phpstan-src diff --git a/.github/workflows/test-slevomat-coding-standard.yml b/.github/workflows/test-slevomat-coding-standard.yml index fd03d240..fb266231 100644 --- a/.github/workflows/test-slevomat-coding-standard.yml +++ b/.github/workflows/test-slevomat-coding-standard.yml @@ -25,10 +25,10 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Checkout Slevomat Coding Standard" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: slevomat/coding-standard path: slevomat-cs From 8202c4407e936d980df15eea482c08aad91a72b1 Mon Sep 17 00:00:00 2001 From: "Jing Xu(RainX)" Date: Sun, 17 Sep 2023 22:47:17 +0800 Subject: [PATCH 3/6] Make the CI job pass after upgrading PHPStan to v1.10.34 --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 04100fcd..4596cc77 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -10,11 +10,6 @@ parameters: count: 1 path: src/Ast/NodeTraverser.php - - - message: "#^Strict comparison using \\=\\=\\= between 2 and 2 will always evaluate to true\\.$#" - count: 2 - path: src/Ast/NodeTraverser.php - - message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#" count: 1 From 877b8f279184b562018f46d2fd039cdf30245f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 26 Jun 2023 11:15:17 +0200 Subject: [PATCH 4/6] simplify/unify parseGeneric method --- src/Parser/TypeParser.php | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index d0b1fdea..c751bce3 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -398,42 +398,33 @@ public function isHtml(TokenIterator $tokens): bool public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\GenericTypeNode { $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); $genericTypes = []; $variances = []; - [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); - - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - - while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $isFirst = true; + while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { - // trailing comma case - $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); - $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); - $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); - if ($startLine !== null && $startIndex !== null) { - $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); - } - return $type; + // trailing comma case + if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + break; } + $isFirst = false; + [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); } - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); - $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); - $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); - $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); - $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); if ($startLine !== null && $startIndex !== null) { $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); + return $type; } From 9f854d275c2dbf84915a5c0ec9a2d17d2cd86b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 18 Sep 2023 11:38:31 +0200 Subject: [PATCH 5/6] fix/unify callable template parsing with EOL --- src/Parser/TypeParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index c751bce3..22d00868 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -415,6 +415,7 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $isFirst = false; [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); } $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); @@ -422,7 +423,6 @@ public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } - $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); return $type; From bcad8d995980440892759db0c32acae7c8e79442 Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Tue, 26 Sep 2023 14:28:12 +0200 Subject: [PATCH 6/6] Allow conditional type in callable return type --- src/Parser/TypeParser.php | 2 +- tests/PHPStan/Parser/TypeParserTest.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index 22d00868..46de7aae 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -524,7 +524,7 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo return $this->parseNullable($tokens); } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { - $type = $this->parse($tokens); + $type = $this->subParse($tokens); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index 85ae0db8..2c66d98d 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -2154,6 +2154,18 @@ public function provideParseData(): array ]), ]), ], + [ + 'Closure(Container):($serviceId is class-string ? TService : mixed)', + new CallableTypeNode(new IdentifierTypeNode('Closure'), [ + new CallableTypeParameterNode(new IdentifierTypeNode('Container'), false, false, '', false), + ], new ConditionalTypeForParameterNode( + '$serviceId', + new GenericTypeNode(new IdentifierTypeNode('class-string'), [new IdentifierTypeNode('TService')], ['invariant']), + new IdentifierTypeNode('TService'), + new IdentifierTypeNode('mixed'), + false + )), + ], ]; }