Skip to content

Fix FiberScope filtering, @var destructuring check, and deferred constant-condition reporting - #6164

Merged
ondrejmirtes merged 4 commits into
2.2.xfrom
review-bug-fixes
Jul 31, 2026
Merged

Fix FiberScope filtering, @var destructuring check, and deferred constant-condition reporting#6164
ondrejmirtes merged 4 commits into
2.2.xfrom
review-bug-fixes

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Four bug fixes from the resolve-type-rewrite branch review — these are the findings that apply to mainline (pre-existing, or arrived with the progressively merged chunks). Each commit adds a test that failed before the fix:

  • Filter FiberScope state in the falsey direction and carry both filter listsfilterByFalseyValue() called parent::filterByTruthyValue(), so a rule filtering the scope it received saw inverted narrowing on direct state reads (getVariableType(), toMutatingScope()); chained filterByTruthyValue()->filterByFalseyValue() additionally dropped the truthy filter from fiber-routed type asks.
  • Check destructured @var items against the native offset type — the destructuring branch wrapped the PHPDoc offset type in a plain TypeExpr, reporting a factually wrong varTag.nativeType error (unconditionally) instead of the flag-gated varTag.type.
  • Defer match arm comparison errors only for match(true)-style subjectsmatch ($i) { is_int($y) => … } silently lost the "Match arm comparison between int and true is always false." error whenever an ImpossibleCheckType marker existed for the call; only for a constant-boolean subject is the arm comparison the same fact those rules own.
  • Scope deferred constant-condition errors and markers per file — identical call conditions at the same print+line in two files collapsed into a single report, and a marker from one file suppressed an unrelated deferred error in another (e.g. a userland function shadowing a built-in type-check name). Trait entries keep cross-file merging.

Full test suite and self-analysis green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JjrRao9kSNLAipk2Go5Kek

ondrejmirtes and others added 4 commits July 31, 2026 14:46
… lists

FiberScope::filterByFalseyValue() called parent::filterByTruthyValue(),
so the filtered scope's expression types carried the TRUTHY narrowing:
direct state reads (getVariableType(), hasVariableType(), anything via
toMutatingScope()) returned the opposite answer, and synthetic-node type
asks were derived from the wrongly-narrowed state. The fiber getType()
path masked the bug for stored real nodes by re-deriving from the
recorded falseyValueExprs.

Both filter methods also copied only their own expression list onto the
derived scope, so chaining filterByTruthyValue()->filterByFalseyValue()
dropped the truthy filter from every fiber-routed type ask. Both lists
are now carried, matching pushInFunctionCall()/popInFunctionCall().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The destructuring branch of VarTagTypeRuleHelper::checkVarType() wrapped
the PHPDoc-derived offset type in a plain TypeExpr, so the native-type
check compared the @var type against the PHPDoc offset type: a factually
wrong varTag.nativeType error (reported unconditionally) where the
PHPDoc comparison should produce varTag.type (gated on
reportWrongPhpDocTypeInVarTag). Carry both flavours in a NativeTypeExpr,
mirroring the foreach key/value sites in WrongVariableNameInVarTagRule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The arm comparison error for a type-check call arm was always deferred
under the raw call's expression key and dropped whenever an
ImpossibleCheckType* marker existed for that call - so for an ordinary
subject, match ($i) { is_int($y) => ... } silently lost the
"Match arm comparison between int and true is always false." error that
is an independent finding from the call's own constant truthiness.

Only for a match(true)-style subject is the arm comparison the same
fact as the call being always-true/false; the deferral (and its no-error
twin) is now gated on the subject being a constant boolean, mirroring
the pre-collector standalone-truthiness deduplication.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Non-trait entries in FunctionCallConstantConditionRule were grouped
project-wide by printed expression and line only, then deduplicated by
message: the same condition at the same line in two different files
collapsed into a single report, and an ImpossibleCheckType* marker from
one file suppressed an unrelated deferred error at the matching
print+line in another file (e.g. a userland function shadowing a
built-in type-check name).

Non-trait bucket keys and the marker lookup now carry the collected
file path. Trait entries keep merging across analysed contexts and
matching markers from any context - trait names are unique
project-wide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ondrejmirtes
ondrejmirtes merged commit 55a7732 into 2.2.x Jul 31, 2026
413 of 415 checks passed
@ondrejmirtes
ondrejmirtes deleted the review-bug-fixes branch July 31, 2026 13:04
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