Skip to content

Add a make lint-diff convention check and a pre-commit step in CLAUDE.md - #6178

Closed
SanderMuller wants to merge 1 commit into
phpstan:2.2.xfrom
SanderMuller:add-lint-diff
Closed

Add a make lint-diff convention check and a pre-commit step in CLAUDE.md#6178
SanderMuller wants to merge 1 commit into
phpstan:2.2.xfrom
SanderMuller:add-lint-diff

Conversation

@SanderMuller

Copy link
Copy Markdown
Contributor

This adds make lint-diff and a short verification step in CLAUDE.md that uses it, so the conventions that keep coming up on review get caught before pushing rather than after CI.

build/lint-diff.php scans the lines a branch adds (against 2.2.x) and prints each candidate with its file and line:

  • instanceof of a Type that has a method alternative on the Type interface (instanceof StringType to ->isString(), instanceof EnumCaseObjectType to ->getEnumCaseObject()). Structural classes with no such method (NeverType, TemplateType, UnionType, MixedType) are deliberately not flagged, since instanceof is idiomatic for those. Curating to the method-having classes keeps it precise: measured against the last 60 commits on 2.2.x it fires on 1.
  • get_class(), which is brittle for type dispatch.
  • a new inline @phpstan-ignore (fix the root cause; the baseline is for pre-existing errors).

It is advisory: it prints candidates and exits 0, so it never blocks legitimate structural instanceof. --strict exits non-zero for anyone who wants it in a gate. A reviewed exception is acknowledged with phpstan-lint-ok on the line, and test-data fixtures are skipped.

CLAUDE.md gains one workflow step (run make lint-diff and resolve every finding) plus a note that make phpstan is not the whole static-analysis gate: the Mutation Testing job runs a stricter analysis that catches things it does not, for example an incomplete #[RequiresPhp('^8.1')] version constraint.

build/lint-diff.php is make phpstan-clean.

…DE.md

`make lint-diff` (build/lint-diff.php) scans the lines a branch adds for the
conventions that recur on review and prints each with its file and line:
instanceof of a Type that has a method alternative (StringType -> isString(),
EnumCaseObjectType -> getEnumCaseObject()), get_class() type dispatch, and a new
inline @PHPStan-Ignore. Structural classes with no method alternative (NeverType,
TemplateType, UnionType, MixedType) are not flagged, because instanceof is
idiomatic for those. It is advisory (exit 0; --strict to fail); a reviewed
exception is acknowledged with phpstan-lint-ok on the line.

CLAUDE.md gains a "run make lint-diff and resolve every finding" step so the
check is part of the workflow, plus a note that make phpstan is not the whole
static-analysis gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ondrejmirtes

Copy link
Copy Markdown
Member

I doňt understant the need for this when we have make phpstan, and custom PHPStan rules.

@ondrejmirtes

Copy link
Copy Markdown
Member

Also we can write more custom rules.

@SanderMuller

Copy link
Copy Markdown
Contributor Author

I doňt understant the need for this when we have make phpstan, and custom PHPStan rules.

It would save time reviewing bot PRs like these: #6116
I think this PR would've saved @staabm time correction actions the bot did

@SanderMuller

Copy link
Copy Markdown
Contributor Author

Also we can write more custom rules.

Good idea to replace some of this with custom rules, looking into that

@SanderMuller
SanderMuller marked this pull request as draft August 3, 2026 18:07
@SanderMuller

Copy link
Copy Markdown
Contributor Author

You're right, and it goes further than I first realised: PHPStan already has this exact rule. ApiInstanceofTypeRule (phpstanApi.instanceofType), compiled from the #[InstanceofDeprecated] attributes on the Type classes, already flags instanceof StringType, instanceof EnumCaseObjectType, and the rest in our own make phpstan (215 of them are in the baseline). So both the tool and the custom-rule idea reinvent something that already ships with PHPStan. I'm closing this.

It also reframes the #6116 case that motivated me. make phpstan did flag the bot's instanceof EnumCaseObjectType; the bot masked it (an inline ignore, then a baseline entry: "Baseline the EnumCaseObjectType instanceof errors instead of ignoring") rather than switching to getEnumCaseObject(), which is what your review corrected. It was never a missing check, it was a masked one.

The only thing worth keeping out of all this is a doc line, not a rule: a note in CLAUDE.md that you fix a rule error you just introduced rather than baselining or ignoring it. That is the actual lesson. Happy to skip even that if you'd rather.

Thanks for the pushback.

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.

2 participants