Skip to content

Fix global constant discovery after class name tokens - #6756

Open
LubuSeb wants to merge 3 commits into
phpstan:2.3.xfrom
LubuSeb:codex/fix-class-token-symbol-scan
Open

LubuSeb wants to merge 3 commits into
phpstan:2.3.xfrom
LubuSeb:codex/fix-class-token-symbol-scan

Conversation

@LubuSeb

@LubuSeb LubuSeb commented Oct 16, 2026 •

Copy link
Copy Markdown

When a global constant follows stdClass::class inside a function, the directory symbol scan can mistake the class name token for a declaration and lose the constant. The same can happen with identifiers beginning with class. This change checks the keyword boundary before entering type scope in both the PHP cleaner and the Turbo scanner.

The regression covers ::class, className(), and a real anonymous class. The native differential corpora also include the trigger. Thanks to @Victor-emil for identifying the cause and suggesting the boundary check in phpstan/phpstan#15482.

Validation: the focused PHPUnit test passes (3 tests); the source-locator suite passes (78 tests, 159 assertions, 1 skip); and the issue's two-file example analyzes without errors using this branch. PHP syntax and git diff --check pass. I could not run the native differential scripts locally because the Turbo extension is not installed.

Fixes phpstan/phpstan#15482

@LubuSeb

LubuSeb commented Oct 16, 2026

Copy link
Copy Markdown
Author

CI note: the result-cache-parse-error E2E check is failing on this PR, but the same check failed on the untouched 2.3.x base commit before this PR. Its final assertion expects a circular-reference error and gets exit 0. I have not changed that E2E case or the relevant result-cache code.

The PHP 7.4 call syntax and Turbo version-pin failures from my first push have been corrected in the current head.

@LubuSeb
LubuSeb force-pushed the codex/fix-class-token-symbol-scan branch 3 times, most recently from 4e6c817 to 1abd49e Compare October 23, 2026 19:30
@staabm
staabm force-pushed the codex/fix-class-token-symbol-scan branch from 1abd49e to dd7bc45 Compare October 24, 2026 06:22
@staabm
staabm force-pushed the codex/fix-class-token-symbol-scan branch from dd7bc45 to 083179b Compare October 24, 2026 07:05
@staabm

staabm commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

//cc @SanderMuller

@staabm

staabm commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

the issue phpstan/phpstan#15482 talks about unexpected rule errors. we should have a test which asserts these unexpected rule errors no longer appear

@SanderMuller

Copy link
Copy Markdown
Contributor

I ran the reporter's three files from phpstan/phpstan#15482 as a project. 2.3.x reports Constant BAR not found., and this branch reports no errors. The branch also fixes the same loss after enum_exists(), is_subclass_of() and $o?->class.

Two shapes still lose the constant on this branch:

function foo(): string
{
    return bar(class: 'x');
}

const BAR = 'bar';

and return stdClass:: class; (with a space). The second idea in the issue fixes both: when $typeLevel drops to 0 or below, set it back to 0 and leave the type.

if ($typeLevel <= 0) {
	$typeLevel = 0;
	$inType = false;
}

With that change on top of this branch, all 8 shapes I tried find BAR. On 17,955 PHP files from this repository and its vendor, it finds the same symbols as this branch. It does not help at the top level of a file, outside any braces, because no closing brace follows there. I did not run the full gate on it, and SymbolScan.h would need the same change.

About a test for the rule errors: a ConstantRule test cannot show this bug. With both files analysed, it gives the same result on 2.3.x and on this branch. An e2e project with the reporter's three files fails on 2.3.x and passes here. So e2e/bug-15482 with a step that runs ../../bin/phpstan analyze would cover it.

What I checked:

  • The new SymbolFinderInFilesTest case fails without the PhpFileCleaner change.
  • On those 17,955 files, the symbols found change for one file only: the new fixture.
  • With the extension built from this branch, php-file-cleaner-corpus.php and symbol-finder-corpus.php print ALL OK. make tests passes with and without the extension (22,639 tests). Self-analysis passes, and phpcs passes on the changed files that make cs checks.
  • Performance: the PHP cleaner over the 17,955 files was 0.5% to 2% slower than on 2.3.x, in two sets of 3 runs each. That is about the noise level. I did not measure the native cleaner.
  • The red tempest and phpstan-laravel integration jobs are also red on Do not delete a container that another process has not journaled yet #6797 from the same day, so they do not come from this change.

@LubuSeb

LubuSeb commented Oct 24, 2026

Copy link
Copy Markdown
Author

Thanks for checking this. I added an E2E case using the reporter's three-file layout and covered the named class argument and spaced class constant variants in the scanner fixture. Both the PHP and native scanners now reset the type state when its depth reaches zero. The E2E analysis and targeted symbol-finder tests pass locally. The update is in 5988ead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Global constant "not found" when an earlier function body in its file uses ::class

3 participants