Skip to content

ADFA-5048 (2/5): Resolve a selection to an expression or a statement range - #1818

Open
Daniel-ADFA wants to merge 1 commit into
refactor/ADFA-5048-shared-extract-method-sheetfrom
feat/ADFA-5048-extraction-region
Open

ADFA-5048 (2/5): Resolve a selection to an expression or a statement range#1818
Daniel-ADFA wants to merge 1 commit into
refactor/ADFA-5048-shared-extract-method-sheetfrom
feat/ADFA-5048-extraction-region

Conversation

@Daniel-ADFA

@Daniel-ADFA Daniel-ADFA commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Stack 2 of 5 for ADFA-5048. Base: #1817. No caller yet; PR 5 wires it up.

Adds ExtractionRegion, which turns a selection into the thing the analysis works on: either one expression or a run of whole statements inside one block.

  • resolveExtractionRegions(task, root, positions, fileText, start, end) resolves a caret or a range to candidate regions.
  • snapToStatements widens a partial selection to whole statements and refuses a constructor delegation (this(...) / super(...)), which cannot move.
  • statementContaining picks the narrowest positive-width statement whose parent is a block. The width guard is load-bearing: javac's analyze() synthesises a default constructor whose super() call has zero width at the class start, and without it a local-class selection resolved to that synthetic node instead of the statement.
  • absorbTrailingSemicolon keeps the emitted call site from leaving a stray ;.

candidateExpressionsAt, isExtractionPosition and isLegalExtractionTarget gain a hoisted flag (default true) so the statement path can reuse them without hoisting, and isConstructorDelegation becomes internal for the same reason.

Resolves a selection to the one region extract method acts on: an expression
candidate at the cursor, or a run of sibling statements in one BlockTree
snapped outward to whole statements. Restricting a range to siblings in one
block excludes the hard cases -- half an if and half its else, a range
straddling a lambda -- by construction rather than by later filtering.

isExtractionPosition and isLegalExtractionTarget gain a defaulted
`hoisted: Boolean = true`, and extract method passes false:

- The conditional-evaluation refusal exists because extract *variable* lifts
  a declaration above the enclosing statement, so hoisting a ternary branch,
  a && right operand or a loop condition changes when it runs. Extract method
  substitutes a call in place: `while (extracted(it))` evaluates exactly as
  `while (it.hasNext())` did.
- Extract variable also refuses an expression statement's whole expression,
  because replacing it with a *name* leaves a bare `v;`. A call is a
  statement, so extract method keeps that target; without it a bare cursor in
  `foo(a, b);` offers nothing.

The default keeps every shipped extract-variable path byte-identical.

statementContaining deliberately does not walk up from deepestPathAt:
analyze() synthesises a default constructor whose generated super() carries
the class declaration's own start position with zero width, and that
synthetic node is narrower than everything real at that offset. Requiring
positive width excludes it without having to recognise it.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

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