From a53c4e2248f9c2cfbb2d2c684c6d1c8a456f8036 Mon Sep 17 00:00:00 2001 From: Michele Locati Date: Thu, 10 Sep 2026 15:48:48 +0200 Subject: [PATCH] Let a trait-pathed ignore entry take precedence over the class-pathed ones When a class using a trait reports its own error with the same message and identifier as an error deduplicated directly into the trait, --generate-baseline records the class occurrence under the class path (count: 1) and the deduplicated error under the trait path (count: 1). On re-run the class-pathed entry also absorbed the trait context of that class, so it was reported as expected 1 time but occurred 2 times: the generated baseline failed by itself. An entry scoped to the trait file hides the deduplicated error for every using class, so it is now tried first: the class-pathed entries account only for the errors reported in the class files themselves. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/e2e-tests.yml | 17 +++++++++++++ .../.gitignore | 1 + .../phpstan.neon | 6 +++++ .../src/AMailer.php | 24 +++++++++++++++++++ .../src/BMailer.php | 10 ++++++++ .../src/SomeTrait.php | 22 +++++++++++++++++ .../with-baseline.neon | 3 +++ .../Ignore/IgnoredErrorHelperResult.php | 22 +++++++++++++++++ 8 files changed, 105 insertions(+) create mode 100644 e2e/baseline-trait-context-own-error/.gitignore create mode 100644 e2e/baseline-trait-context-own-error/phpstan.neon create mode 100644 e2e/baseline-trait-context-own-error/src/AMailer.php create mode 100644 e2e/baseline-trait-context-own-error/src/BMailer.php create mode 100644 e2e/baseline-trait-context-own-error/src/SomeTrait.php create mode 100644 e2e/baseline-trait-context-own-error/with-baseline.neon diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index ae29efb84c7..36aabc5886f 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -744,6 +744,23 @@ jobs: ../../bin/phpstan clear-result-cache ../bashunit -a exit_code "0" "../../bin/phpstan analyse --error-format=raw -c with-full-baseline.neon" ../bashunit -a exit_code "0" "../../bin/phpstan analyse --error-format=raw -c with-full-baseline.neon" + - script: | + cd e2e/baseline-trait-context-own-error + # A using class may report its own error with the same message and identifier + # as an error deduplicated directly into the trait: --generate-baseline records + # the class's own occurrence under the class path and the deduplicated error + # under the trait path. The trait-pathed entry hides the deduplicated error for + # every using class, so the class-pathed entry accounts only for the class's + # own occurrence: the generated baseline holds on re-run, with both an empty + # and a primed result cache. + ../../bin/phpstan clear-result-cache + ../../bin/phpstan --generate-baseline=baseline.neon + ../bashunit -a contains 'path: src/AMailer.php' "$(cat baseline.neon)" + ../bashunit -a contains 'path: src/SomeTrait.php' "$(cat baseline.neon)" + ../bashunit -a not_contains 'BMailer.php' "$(cat baseline.neon)" + ../../bin/phpstan clear-result-cache + ../bashunit -a exit_code "0" "../../bin/phpstan analyse --error-format=raw -c with-baseline.neon" + ../bashunit -a exit_code "0" "../../bin/phpstan analyse --error-format=raw -c with-baseline.neon" - script: | cd e2e/result-cache-meta-extension composer install diff --git a/e2e/baseline-trait-context-own-error/.gitignore b/e2e/baseline-trait-context-own-error/.gitignore new file mode 100644 index 00000000000..0fd5c01d90a --- /dev/null +++ b/e2e/baseline-trait-context-own-error/.gitignore @@ -0,0 +1 @@ +baseline.neon diff --git a/e2e/baseline-trait-context-own-error/phpstan.neon b/e2e/baseline-trait-context-own-error/phpstan.neon new file mode 100644 index 00000000000..5412eca9298 --- /dev/null +++ b/e2e/baseline-trait-context-own-error/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 4 + paths: + - src + parallel: + maximumNumberOfProcesses: 1 diff --git a/e2e/baseline-trait-context-own-error/src/AMailer.php b/e2e/baseline-trait-context-own-error/src/AMailer.php new file mode 100644 index 00000000000..36d565f13c8 --- /dev/null +++ b/e2e/baseline-trait-context-own-error/src/AMailer.php @@ -0,0 +1,24 @@ +getTraitContexts(); if (count($traitContexts) > 0) { $errorTraitFilePath = $error->getTraitFilePath(); + + // An entry scoped to the trait file hides the deduplicated error for every + // using class: it takes precedence over the class-scoped entries, which then + // account only for the errors reported in the class files themselves. This + // is what --generate-baseline records when a using class has its own + // occurrence of the same error (same message and identifier). + if ($errorTraitFilePath !== null) { + $normalizedTraitFilePath = $this->fileHelper->normalizePath($errorTraitFilePath); + if (isset($this->ignoreErrorsByFile[$normalizedTraitFilePath])) { + $matchingTraitFileIgnoreErrors = $ignoreErrorsByFileAndIdentifier[$normalizedTraitFilePath][$identifierKey] + ??= self::filterIgnoreErrorsByIdentifier($this->ignoreErrorsByFile[$normalizedTraitFilePath], $identifier); + foreach ($matchingTraitFileIgnoreErrors as $ignoreError) { + $i = $ignoreError['index']; + $ignore = $ignoreError['ignoreError']; + if (!$processIgnoreError($error, $i, $ignore)) { + $ignoredErrors[] = [$error, $ignore]; + continue 2; + } + } + } + } + $remainingContexts = $traitContexts; foreach (array_keys($traitContexts) as $contextFilePath) { $contextError = $error->asReportedInTraitContext($contextFilePath);