Skip to content

ExpressionResolver::docblockForExpression reads the resolved symbol's docblock through one resolve()?->getDocumentation() call - #480

Merged
Firehed merged 5 commits into
mainfrom
step/25
Sep 1, 2026
Merged

ExpressionResolver::docblockForExpression reads the resolved symbol's docblock through one resolve()?->getDocumentation() call#480
Firehed merged 5 commits into
mainfrom
step/25

Conversation

@Firehed

@Firehed Firehed commented Sep 1, 2026

Copy link
Copy Markdown
Owner

step-25 acceptance

  • The wrapper method does not exist; foreach docblock inference is one line — resolve()?->getDocumentation(). Proof: grep docblockForExpression src tests returns nothing; src/Resolution/ExpressionResolver.php line ~238.
  • @return list<T> and @var docblock inference works on FuncCall, MethodCall, NullsafeMethodCall, StaticCall, PropertyFetch, NullsafePropertyFetch, StaticPropertyFetch, ClassConstFetch, and ConstFetch the same way it works on $this->items(). Proof: HoverHandlerTest::testForeachElementTypeIsResolvedFromEveryNodeKind — data-provider with one row per node kind.
  • A test covers each of those node kinds. Same data-provider above.

Design notes

  • ResolvedSymbol::getDocumentation() returns the raw docblock now. The tag-stripping used to sit in HasSymbolLocation; foreach element-type inference needs @var/@return/@phpstan-* tags intact. Description-only formatting for hover, signature help, and completion detail is where the step-30 presenter lands — the existing disallow on getDocumentation() already points at it. Between now and then, hover markup will include @tag lines verbatim.
  • One src caller added to the getDocumentation allowIn: src/Resolution/ExpressionResolver.php — the inference call this step introduces. Authorized loosen.
  • ConstantInfo::fromGlobalDeclaration was silently reading a null docblock for every const declarator and define() call. php-parser attaches the doc comment to the outer Stmt, not to the declarator/expression. Fixed to consult the parent first — required for the ConstFetch case of the acceptance clause above.
  • Fixture's ConstFetch uses a leading-backslash reference because resolveConstFetch does not yet apply PHP's namespaced-first, global-fallback rule (that's step-28).

🤖 Written by AI, reviewed by human.

Firehed and others added 5 commits September 1, 2026 13:07
step-25 requires docblock element-type inference to work on the nine
callable-, member-, and constant-shaped expression node kinds the same
way it works on $this->items(). The data provider names each kind so
the next commit's refactor is verified against a fixed list. Currently
red for StaticCall, StaticPropertyFetch, ClassConstFetch, and ConstFetch;
the other five already resolve.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
php-parser attaches doc comments to the outer statement — Stmt\Const_ for a
const declarator, Stmt\Expression for a define() call — so a declarator or
expression asked directly for its own comment reads null. Every global
constant built through fromGlobalDeclaration lost its docblock silently
because of this. Consulting the parent first, then the node itself, is what
lets @var docblock inference resolve foreach over a global constant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Description-only stripping loses the @var, @return, and @phpstan-* tags
that arrayElementType — the next commit's foreach inference — reads. The
raw docblock is what every ResolvedSymbol consumer that infers types needs,
so getDocumentation is the raw text now. Description-stripping for display
is where the step-30 presenter lands; the disallow rule on this method
already anticipates that landing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
docblockForExpression branched on MethodCall/PropertyFetch/FuncCall by hand
— five node kinds covered, four (StaticCall, StaticPropertyFetch,
ClassConstFetch, ConstFetch) silently absent. Every ResolvedSymbol now
answers the raw docblock through getDocumentation(), so foreachElementType
routes through one resolve() call and every node kind resolve() answers
participates uniformly. The wrapper is deleted; the disallow on
ResolvedSymbol::getDocumentation() gains one src entry — the inference
caller — with the display presenter tracked by step-30 still to come.

The fixture's ConstFetch case names the constant with a leading backslash
because resolveConstFetch does not yet apply PHP's namespaced-first,
global-fallback rule (build-manifest step-28); the bare name would look up
FOREACH_USER_CONSTANT rather than Fixtures\Hover\FOREACH_USER_CONSTANT.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.39%. Comparing base (80b7bbc) to head (431d732).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #480      +/-   ##
============================================
+ Coverage     99.35%   99.39%   +0.03%     
+ Complexity     1873     1863      -10     
============================================
  Files           132      132              
  Lines          4805     4789      -16     
============================================
- Hits           4774     4760      -14     
+ Misses           31       29       -2     

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

@Firehed
Firehed marked this pull request as ready for review September 1, 2026 20:59
@Firehed
Firehed merged commit 4dfafb5 into main Sep 1, 2026
9 checks passed
@Firehed
Firehed deleted the step/25 branch September 1, 2026 21:03
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