Skip to content

Fix ignoreErrors entries in NEON config files, remove ignores made stale by --fix-ignored - #6909

Merged
ondrejmirtes merged 3 commits into
2.3.xfrom
fixable-ignore-errors-neon
Nov 2, 2026
Merged

ondrejmirtes merged 3 commits into
2.3.xfrom
fixable-ignore-errors-neon

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Continues #6906. --fix now also fixes ignore.unmatched and too high ignore.count in NEON config files, like phpstan-baseline.neon, and --fix-ignored cleans up the ignores it makes stale.

PHPStan\Neon\Printer
A format-preserving printer for nette/neon syntax trees, modelled on the printers of nikic/php-parser and phpstan/phpdoc-parser:

  • A tree is changed by replacing nodes with clones that keep the token positions of the original nodes. Unchanged code is printed as written, with comments and whitespace. Nodes created with new are printed fresh.
  • List items are compared with phpdoc-parser's Differ, so items can be removed, added and reordered. The comment at the end of an item's line and the comment lines right above an item belong to the item. Inline arrays and entity attributes keep their separators. Removing every entry prints ignoreErrors: [].
  • printChanges() returns only the changed byte ranges. Unchanged code is referred to by token ranges and is not copied, so the cost depends on the size of the change, not of the file.
  • nette/neon strips \r. The fixer puts CRLF back when every line break had it, and does not fix files with mixed line endings.

nette/neon stays at 3.3.4. Its parser and syntax tree are @internal, so one scoped ignoreErrors entry in build/phpstan.neon covers src/Neon, the fixer and the printer tests. nette/neon 4.0, which made the API public (nette/neon#52), requires PHP 8.4.

Performance
Measured on the WordPress level 10 NEON baseline (30,989 entries, 6.2 MB), with 1,240 entries that no longer match and 620 counts that are too high. Printing the whole file for each fix took 53.9 s in total. With printChanges() it takes 8.4 s, including a one-time 1.9 s parse, about 2 ms per fix. All 1,870 errors are fixed in one round, and the result equals the expected baseline byte for byte.

Removing neighbouring items
A removal edit starts at the end of the previous item, so the edits for two neighbouring items touch the same line and Patcher puts the second one into the next round. A list like messages: is therefore never left empty, like with the PHP fixer.

--fix-ignored
Fixing an ignored error leaves behind the ignore that matched it: an unmatched ignoreErrors entry, a count that is too high, or an unmatched @phpstan-ignore comment. These are reported errors, so --fix-ignored on its own left them in place and the next run failed. Later rounds now also fix fixable ignore.* errors in the files that earlier rounds changed. Stale ignores in other files were already there and are left alone. The main process passes the changed files to each round worker with an internal --fixed-ignored-in option.

Tests

  • tests/PHPStan/Neon/PrinterTest.php: each case checks the exact output, that it decodes to the same value, and that printChanges() gives the same result.
  • IgnoreErrorsConfigFixerTest: NEON cases for entries with comments, list items, lowering a count, %%, CRLF, duplicate entries that aren't fixed, and neighbouring removals.
  • e2e/fix-baseline now also fixes a NEON baseline.
  • New e2e/fix-ignored-entries covers a PHP baseline entry, a NEON count and an inline comment, plus a stale entry in an untouched file that must stay.
  • e2e/fix: its ignoreErrors entry moved into a copied ignore.neon, because --fix-ignored now removes it.

… files

PHPStan\Neon\Printer is a format-preserving printer of nette/neon syntax
trees, inspired by the printers of nikic/php-parser and
phpstan/phpdoc-parser. A tree is changed by replacing nodes with clones
that keep the token positions of the original nodes; unchanged code is
printed as it was written, with its comments and whitespace. Items of lists
are compared with phpdoc-parser's Differ. The comment at the end of an
item's line and the comment lines above an item belong to the item.

nette/neon 3 marks its parser and syntax tree @internal, the composer.json
pins it to one version, so the internalClass errors are ignored in the
printer and the fixer. nette/neon 4, which made the API public, requires
PHP 8.4.

printChanges() returns only the changed ranges, unchanged code is referred
to by token ranges and not copied, so a fix in the 6 MB NEON baseline of
WordPress level 10 (31k entries) takes about 2 ms after a 1.9 s parse.
1,870 broken entries are fixed in one round. Removals of neighbouring items
touch the same line and go in separate rounds, so a list is never left
empty.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19bac-7181-7568-be9c-c7ec28db8ebc
Fixing an ignored error leaves behind the ignore that matched it: an
unmatched ignoreErrors entry, a count that is too high, or an unmatched
@PHPStan-Ignore comment. Those are reported errors, so --fix-ignored on its
own did not fix them and the next run failed.

The following rounds of --fix-ignored now also fix the fixable ignore.*
errors in the files that the previous rounds changed. Stale ignores in other
files were there before and are left alone.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19bac-7181-7568-be9c-c7ec28db8ebc
…and 8.1

The PHP 7.4 downgrade does not rewrite the nullsafe operator, and WeakMap
does not exist there; the printer caches are arrays by spl_object_id() that
keep the object, so its ID is not reused. PCRE of PHP < 8.2 does not accept
a NUL byte in a pattern, only its escape.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19bac-7181-7568-be9c-c7ec28db8ebc
@ondrejmirtes
ondrejmirtes merged commit 583156a into 2.3.x Nov 2, 2026
345 of 346 checks passed
@ondrejmirtes
ondrejmirtes deleted the fixable-ignore-errors-neon branch November 2, 2026 11:49
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.

1 participant