Skip to content

Add steps 23-26 for remaining M×N problems - #473

Merged
Firehed merged 3 commits into
mainfrom
manifest/add-mxn-steps
Aug 31, 2026
Merged

Add steps 23-26 for remaining M×N problems#473
Firehed merged 3 commits into
mainfrom
manifest/add-mxn-steps

Conversation

@Firehed

@Firehed Firehed commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Cursory scan surfaced four M×N problems in src/Resolution/ not captured by the current manifest. Adding them as steps 23-26 ahead of the retire step (now step-27).

  • step-23 — collapse SymbolResolver::resolveCallable node fan-out. The method-call path skips late-bound return-type resolution that the static-call path applies, so hover on $obj->foo(): static shows static and Foo::bar(): static shows the receiver class.
  • step-24 — one member-lookup helper in ExpressionResolver. Late-binding runs for methods but not properties, so $obj->prop typed static in the parent returns raw static.
  • step-25docblockForExpression re-implements resolve+docblock for three node kinds and misses four others; foreach element-type inference from a @return list<T> docblock works on $this->items() but not Foo::items().
  • step-26self/static/parent literals recur across MemberAccessDetector's text and AST paths; ScopeFinder::resolveClassName and LateBindingKeyword already exist as homes.

Ordering puts the callable/member dispatch cluster (23-24) first since 24 depends on 23 having ExpressionResolver as the sole callable answerer.

PR body written by AI, reviewed by a human.

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.46%. Comparing base (bfe79bd) to head (53cad0c).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #473   +/-   ##
=========================================
  Coverage     99.46%   99.46%           
  Complexity     1892     1892           
=========================================
  Files           133      133           
  Lines          4897     4897           
=========================================
  Hits           4871     4871           
  Misses           26       26           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@Firehed
Firehed merged commit 2be9b2b into main Aug 31, 2026
9 checks passed
@Firehed
Firehed deleted the manifest/add-mxn-steps branch August 31, 2026 16:49
Firehed added a commit that referenced this pull request Sep 1, 2026
Follow-up to #473. Scope grew after a second scan surfaced five more M×N
patterns not captured by the current manifest, and after the realisation
that step-21's baseline hard-lock made new tighten rules impractical.

## Tightens to steps 25 and 26

- **step-25** — enumerates the nine node kinds that must gain docblock
coverage; notes that if `docblockForExpression` becomes a one-liner it
is deleted and inlined rather than kept as a wrapper.
- **step-26** — names `Domain\LateBindingKeyword` as the sole home
(drops the "or `ScopeFinder::resolveClassName`, whichever" hedge); adds
a `tests/Architecture/` test that fails if the three keyword literals
appear in `src/` outside the enum.

## New steps 27-31 for uncaptured M×N findings

Each ships a `disallowedMethodCalls` tighten modelled on #478's pattern.

- **step-27** — union/intersection receivers: every member-lookup site
in `ExpressionResolver` and `MemberAccessDetector` picks
`getResolvableClassNames()[0]` while
`SymbolResolver::getAccessibleMembers` iterates. Failure: `function
f(A|B $x) { $x->onlyB(); }` — completion offers `onlyB`, hover returns
null. Tighten pins `getResolvableClassNames()` to the shared helper.
- **step-28** — `resolveConstFetch` uses one lookup, `resolveFuncCall`
iterates `NameContext::candidates`. Failure: `namespace App; const X =
1; echo X;` — hover on `X` returns null. Tighten pins
`SymbolSource::lookupConstant` to `ExpressionResolver`.
- **step-29** — `SymbolCandidates` reads `->docblock` +
`DocblockParser::extractDescription` directly, bypassing
`HasSymbolLocation::getDocumentation()`. Preventive today; a future
tag-strip in `getDocumentation()` would silently miss completion detail.
Tighten pins `DocblockParser::extractDescription` to
`HasSymbolLocation`.
- **step-30** — `HoverHandler`, `SignatureHelpHandler`, and
`CompletionItemFactory` each compose `format()` + `getDocumentation()`
themselves; a `ResolvedSymbolPresenter` consolidates the shape. Tighten
pins the two methods to the presenter.
- **step-31** — `$this` typing has an AST path in `ExpressionResolver`
and a text-fallback side-channel in `MemberAccessDetector` (via a
`resolvedType` node attribute). Deletes the attribute; routes both
through one helper. Tighten pins
`TextFallbackHelper::resolveEnclosingClassName` to that helper.

## Relaxes bin/check-baseline-shrink NOW

Step-21's script failed if either baseline file existed. That made new
tighten rules with residuals impossible to add. Restored the pre-step-21
shrink-only-when-present logic (comparing to `origin/main`). The hard
"must-not-exist" lock moves to the retire step, by which point every
interim tighten will have drained its residual.

## Beefed-up retire (step-32)

- Restores the hard lock on `bin/check-baseline-shrink`.
- Adds a `tests/Architecture/HandlerDependenciesRule.php` that fails if
any handler in `src/Handler/` names `ParserService`, `SymbolIndex`,
`MemberResolver`, or `SymbolSource` directly — code-enforcing the
"handlers are formatters, not resolvers" invariant that CLAUDE.md
documents but nothing enforces today.
- Updates `CLAUDE.md` Guardrails and Architecture Invariants to reflect
final state: baselines permanently gone, freeze paragraph deleted, the
running list of scoped `disallowedMethodCalls` tightens named as the
seams they pin.

## Considered and dropped from the scan

- Two regex classifiers for call kind
(`TextFallbackHelper::parseCallPattern` vs `CompletionClassifier`) —
verifier confirmed they answer different questions on the same input; no
feature asymmetry possible.
- Two text regex walkers (`DefaultTextSymbolExtractor` vs
`TextFallbackHelper::findClassLikeDeclarations`) —
`DefaultTextSymbolExtractor` is a consumer of the second; already
unified.

_PR body written by AI, reviewed by a human._
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