From 6646411b788635d94fc8e95b9db497a0925a97cf Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 29 May 2026 09:26:12 +0200 Subject: [PATCH 1/4] Fix file path of trait errors reported directly in the trait When an error inside a trait is reported once directly in the trait (Error::removeTraitContext()), the displayed file is moved to the trait file but filePath was left pointing at the using-class file. This broke inline @phpstan-ignore matching (looked up by filePath) and the editorUrl (getTraitFilePath() ?? getFilePath()), both resolving to the wrong file. Make filePath follow the file onto the trait, matching changeFilePath(). Fixes phpstan/phpstan#14718 Co-Authored-By: Claude Code --- src/Analyser/Error.php | 2 +- tests/PHPStan/Analyser/ErrorTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Analyser/Error.php b/src/Analyser/Error.php index 107db06f08a..23d379b808a 100644 --- a/src/Analyser/Error.php +++ b/src/Analyser/Error.php @@ -117,7 +117,7 @@ public function removeTraitContext(): self $this->traitFilePath, $this->line, $this->canBeIgnored, - $this->filePath, + $this->traitFilePath, null, $this->tip, $this->nodeLine, diff --git a/tests/PHPStan/Analyser/ErrorTest.php b/tests/PHPStan/Analyser/ErrorTest.php index e59abafff36..f6aa16b8799 100644 --- a/tests/PHPStan/Analyser/ErrorTest.php +++ b/tests/PHPStan/Analyser/ErrorTest.php @@ -16,6 +16,20 @@ public function testError(): void $this->assertSame(10, $error->getLine()); } + public function testRemoveTraitContextUsesTraitFileAsFilePath(): void + { + $error = new Error('Message', 'trait.php (in context of class C)', 11, true, 'user.php', 'trait.php'); + $this->assertSame('user.php', $error->getFilePath()); + $this->assertSame('trait.php', $error->getTraitFilePath()); + + $withoutTraitContext = $error->removeTraitContext(); + $this->assertSame('trait.php', $withoutTraitContext->getFile()); + // filePath must follow the file onto the trait, otherwise editor URLs and + // inline @phpstan-ignore lookups point at the using-class file (#14718). + $this->assertSame('trait.php', $withoutTraitContext->getFilePath()); + $this->assertNull($withoutTraitContext->getTraitFilePath()); + } + public static function dataValidIdentifier(): iterable { yield ['a']; From 431e0e110f6ff817da27cc4eaa758649bba82887 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 29 May 2026 12:18:56 +0200 Subject: [PATCH 2/4] Add e2e test for trait error file path (#14718) Covers the behaviours from the issue against the fix: - inline @phpstan-ignore on a trait-deduplicated error suppresses it, consistently with an empty and a primed result cache - with the ignore removed, the error is reported in the trait file and a generated baseline records the trait file path, not the using-class file Co-Authored-By: Claude Code --- .github/workflows/e2e-tests.yml | 16 ++++++++++++++++ e2e/bug-14718/phpstan.neon | 4 ++++ e2e/bug-14718/src/Bar.php | 10 ++++++++++ e2e/bug-14718/src/Foo.php | 10 ++++++++++ e2e/bug-14718/src/FooTrait.php | 15 +++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 e2e/bug-14718/phpstan.neon create mode 100644 e2e/bug-14718/src/Bar.php create mode 100644 e2e/bug-14718/src/Foo.php create mode 100644 e2e/bug-14718/src/FooTrait.php diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index f5989b740b8..cc795e63757 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -297,6 +297,22 @@ jobs: ../bashunit -a contains 'FooTrait.php:10:Strict comparison using === between int<0, max> and false will always evaluate to false.' "$OUTPUT" ../bashunit -a contains 'FooTrait.php (in context of class E2EInTrait\Bar):18:Strict comparison using === between E2EInTrait\Bar and null will always evaluate to false.' "$OUTPUT" ../bashunit -a contains 'FooTrait.php (in context of class E2EInTrait\Foo):18:Strict comparison using === between E2EInTrait\Foo and null will always evaluate to false.' "$OUTPUT" + - script: | + cd e2e/bug-14718 + # https://github.com/phpstan/phpstan/issues/14718 + # an inline @phpstan-ignore on an error deduplicated into the trait must suppress it, + # consistently with both an empty and a primed result cache + ../../bin/phpstan clear-result-cache + ../bashunit -a exit_code "0" "../../bin/phpstan --error-format=raw" + ../bashunit -a exit_code "0" "../../bin/phpstan --error-format=raw" + # with the ignore removed, the error is reported in the trait file and a generated + # baseline records the trait file path, not the using-class file + sed -i 's# // @phpstan-ignore identical.alwaysFalse##' src/FooTrait.php + ../../bin/phpstan clear-result-cache + OUTPUT=$(../bashunit -a exit_code "1" "../../bin/phpstan --error-format=raw") + ../bashunit -a contains 'FooTrait.php:10:Strict comparison using === between int<0, max> and false will always evaluate to false.' "$OUTPUT" + ../../bin/phpstan --generate-baseline=baseline.neon + ../bashunit -a contains 'path: src/FooTrait.php' "$(cat baseline.neon)" - script: | cd e2e/result-cache-meta-extension composer install diff --git a/e2e/bug-14718/phpstan.neon b/e2e/bug-14718/phpstan.neon new file mode 100644 index 00000000000..c308dcf5421 --- /dev/null +++ b/e2e/bug-14718/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 8 + paths: + - src diff --git a/e2e/bug-14718/src/Bar.php b/e2e/bug-14718/src/Bar.php new file mode 100644 index 00000000000..11a8023e727 --- /dev/null +++ b/e2e/bug-14718/src/Bar.php @@ -0,0 +1,10 @@ + Date: Fri, 29 May 2026 15:19:07 +0200 Subject: [PATCH 3/4] Avoid literal ignore annotation in test comment The phrase tripped PHPStan's own ignore.parseError rule during self-analysis. Reword the comment. Co-Authored-By: Claude Code --- tests/PHPStan/Analyser/ErrorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/ErrorTest.php b/tests/PHPStan/Analyser/ErrorTest.php index f6aa16b8799..6f88d692f59 100644 --- a/tests/PHPStan/Analyser/ErrorTest.php +++ b/tests/PHPStan/Analyser/ErrorTest.php @@ -25,7 +25,7 @@ public function testRemoveTraitContextUsesTraitFileAsFilePath(): void $withoutTraitContext = $error->removeTraitContext(); $this->assertSame('trait.php', $withoutTraitContext->getFile()); // filePath must follow the file onto the trait, otherwise editor URLs and - // inline @phpstan-ignore lookups point at the using-class file (#14718). + // inline ignore-comment lookups point at the using-class file (#14718). $this->assertSame('trait.php', $withoutTraitContext->getFilePath()); $this->assertNull($withoutTraitContext->getTraitFilePath()); } From ebbd290a45532b736fa358a3fb4f1b30f3071a1f Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 29 May 2026 15:38:51 +0200 Subject: [PATCH 4/4] Update self-analysis baseline for relocated trait error The identical.alwaysFalse error in MbFunctionsReturnTypeExtensionTrait (used by StrSplitFunctionReturnTypeExtension) is now attributed to the trait file instead of the using class, so move its baseline entry. Co-Authored-By: Claude Code --- build/baseline-8.0.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/baseline-8.0.neon b/build/baseline-8.0.neon index e249b3ce3e6..29b3aedabd3 100644 --- a/build/baseline-8.0.neon +++ b/build/baseline-8.0.neon @@ -22,4 +22,4 @@ parameters: message: '#^Strict comparison using \=\=\= between list\ and false will always evaluate to false\.$#' identifier: identical.alwaysFalse count: 1 - path: ../src/Type/Php/StrSplitFunctionReturnTypeExtension.php + path: ../src/Type/Php/MbFunctionsReturnTypeExtensionTrait.php