Skip to content

Add change variable type quick-fix code action - #8645

Merged
tgodzik merged 10 commits into
scalameta:main-v2from
zielinsky:metalsv2/change-variable-type-quick-fix
Aug 7, 2026
Merged

Add change variable type quick-fix code action#8645
tgodzik merged 10 commits into
scalameta:main-v2from
zielinsky:metalsv2/change-variable-type-quick-fix

Conversation

@zielinsky

@zielinsky zielinsky commented Jul 5, 2026

Copy link
Copy Markdown
Member

Part of #8502

Summary by CodeRabbit

  • New Features

    • Added a Java quick fix that changes a variable’s declared type to match its assigned value.
    • Supports primitive, reference, numeric, array, generic, annotated, imported, qualified, and conditional-expression types.
    • Preserves appropriate imports and type visibility in generated edits.
  • Bug Fixes

    • Improved handling of legacy array syntax and declaration edge cases.
    • Refined availability for compatible types, selections, and unsupported declarations.
  • Tests

    • Added comprehensive coverage for supported conversions and unavailable quick-fix scenarios.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bf9e687-f87a-4325-8b09-13c485321b4d

📥 Commits

Reviewing files that changed from the base of the PR and between d70c1d7 and d9cc97f.

📒 Files selected for processing (11)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-interfaces/src/main/java/scala/meta/pc/CodeActionId.java
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
🚧 Files skipped from review as they are similar to previous changes (9)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala
  • mtags-interfaces/src/main/java/scala/meta/pc/CodeActionId.java
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala

📝 Walkthrough

Walkthrough

Adds a compiler-backed ChangeVariableType Java code action. It detects incompatible-type diagnostics, infers initializer types through the Java compiler, generates replacement and import edits, and validates Java declaration constraints. An LSP suite covers supported and unsupported cases.

Changes

ChangeVariableType code action

Layer / File(s) Summary
Diagnostic and source-range contracts
mtags-java/.../JavacDiagnostic.scala, metals/.../JavaTrees.scala, mtags-java/.../Positions.scala
Adds incompatible-type diagnostic detection, Java variable name lookup, and token-based handling for names and legacy array syntax.
Type shortening support infrastructure
mtags-java/.../JavaTypeShortener.scala, mtags-java/.../JavaImplementAbstractMembersProvider.scala
Adds wildcard-import and member-type resolution through forPath. JavaImplementAbstractMembersProvider uses the factory.
Compiler-backed edit provider
mtags-java/.../JavaChangeVariableTypeProvider.scala
Computes replacement and import edits. Validates variables, inferred types, array dimensions, declaration structure, annotations, and renderability.
Code action wiring and LSP validation
mtags-interfaces/.../CodeActionId.java, metals/.../ChangeVariableType.scala, mtags-java/.../JavaPresentationCompiler.scala, metals/.../CodeActionProvider.scala, tests/unit/.../ChangeVariableTypeLspSuite.scala
Adds the action identifier, registers and advertises compiler support, filters diagnostics, returns compiler-generated edits, and tests supported and unsupported Java cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: tgodzik

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CodeActionProvider
  participant JavaPresentationCompiler
  participant JavaChangeVariableTypeProvider
  participant Javac

  Client->>CodeActionProvider: request ChangeVariableType actions
  CodeActionProvider->>JavaPresentationCompiler: request diagnostic-scoped edits
  JavaPresentationCompiler->>JavaChangeVariableTypeProvider: pass diagnostic range
  JavaChangeVariableTypeProvider->>Javac: infer initializer type
  Javac-->>JavaChangeVariableTypeProvider: return inferred type
  JavaChangeVariableTypeProvider-->>JavaPresentationCompiler: return replacement and import edits
  JavaPresentationCompiler-->>CodeActionProvider: return code action
  CodeActionProvider-->>Client: return ChangeVariableType action
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding a change-variable-type quick-fix code action.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zielinsky zielinsky changed the title Implement change variable type quick-fix Add change variable type quick-fix code action Jul 5, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala (1)

243-251: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication: node.getType() computed twice.

typ (lines 243-249) and typeRange (line 250) each independently call Option(node.getType()). Hoisting it into a single val avoids the repeated call/wrap.

♻️ Proposed refactor
+        val typeTree = Option(node.getType())
         treeRange(node).map { range =>
           JavaVariable(
             tree = node,
             name = variableName,
             range = range,
             nameRange = findNameRange(
               lineMap,
               text,
               range.startOffset,
               range.endOffset,
               variableName,
             ).getOrElse(range),
-            typ = Option(node.getType()) match {
+            typ = typeTree match {
               case Some(t) => t.toString()
               case None =>
                 // This can happen if a variable is declared with inferred type.
                 // This is going to change in Java 27, see https://bugs.openjdk.org/browse/JDK-8268850
                 "var"
             },
-            typeRange = Option(node.getType()).flatMap(treeRange),
+            typeRange = typeTree.flatMap(treeRange),
             initializerRange = Option(node.getInitializer()).flatMap(treeRange),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala` around
lines 243 - 251, The Java tree parsing logic computes node.getType() twice when
building the result in JavaTrees, once for typ and again for typeRange. Hoist
the type lookup into a single local val inside the parsing block, then derive
both typ and typeRange from that shared value to avoid the repeated Option
wrapping and keep the logic in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala`:
- Around line 103-106: SourceVisibility.from is using raw-text regex scans via
packageDeclaration and importDeclaration, which can mis-detect commented-out
package/import lines as real declarations. Update the visibility check to avoid
scanning unprocessed source text: either strip comments and string literals
before applying these regexes, or use the parsed compilation unit data from
javaTrees to derive package/import visibility. Ensure visibleName and renderType
only treat genuinely declared imports/packages as visible.

In `@metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala`:
- Around line 250-251: The variable range extraction in JavaTrees currently
treats legacy array declarators like regular types, causing
ChangeVariableType.isSingleDeclaration to reject declarations such as int arr[]
= ... because node.getType() includes the post-name brackets. Update the
handling around node.getType() in JavaTrees so legacy array declarators are
detected explicitly and either normalized or excluded before producing
typeRange, and make sure the downstream single-declaration check in
ChangeVariableType can still identify valid cases.

In `@mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala`:
- Around line 44-65: The IncompatibleTypes extractor in JavacDiagnostic
currently matches only the localized English diagnostic text, so the “Change
variable type” action can break when javac wording or locale changes. Update
IncompatibleTypes.unapply to guard on the javac code as well, using the
diagnostic key compiler.err.prob.found.req similarly to CannotFindSymbol, and
keep the existing regex parsing as the fallback for extracting found and
required types.

---

Nitpick comments:
In `@metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala`:
- Around line 243-251: The Java tree parsing logic computes node.getType() twice
when building the result in JavaTrees, once for typ and again for typeRange.
Hoist the type lookup into a single local val inside the parsing block, then
derive both typ and typeRange from that shared value to avoid the repeated
Option wrapping and keep the logic in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 884fb923-2f33-48cc-8baf-93644f80160c

📥 Commits

Reviewing files that changed from the base of the PR and between 3af7125 and 66546ac.

📒 Files selected for processing (5)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala

Comment thread metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala Outdated
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch 3 times, most recently from 72c5cf3 to 112af68 Compare July 8, 2026 23:03
@zielinsky
zielinsky marked this pull request as ready for review July 8, 2026 23:23
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from 112af68 to b7c4729 Compare July 8, 2026 23:23
@zielinsky
zielinsky requested a review from tgodzik July 8, 2026 23:24
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch 2 times, most recently from ae8f3a0 to e7f3d3e Compare July 8, 2026 23:33
@zielinsky
zielinsky requested review from tgodzik and removed request for tgodzik July 9, 2026 00:11

@tgodzik tgodzik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One thing I am wondering is whether we shouldn't do it via the presentation compiler. Will the printed types here good enough? Could you add some more complex types? Maybe something that needs an import (var being given a method return value that is not available in the current scope) or maybe generic types?

@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from c88f84d to 1ffab8e Compare July 9, 2026 22:22
@zielinsky

Copy link
Copy Markdown
Member Author

@tgodzik We take the replacement type directly from javac's compiler.err.prob.found.req diagnostic, so this doesn't need to go through the presentation compiler. I added coverage for an unimported method return type and a generic return type.

  diagnostic <- params.getContext().getDiagnostics().asScala.toSeq
  foundType <- changedType(diagnostic, variable, initializerRange).toSeq
  replacement = renderType(foundType, sourceVisibility, legacyDimensions)
  
  private def changedType(
      diagnostic: l.Diagnostic,
      variable: JavaVariable,
      initializerRange: JavaRange,
  ): Option[String] =
    for {
      mismatch <- JavacDiagnostic.IncompatibleTypes.unapply(diagnostic)
      if isRenderableType(mismatch.found)
      if isInitializerMismatch(diagnostic, initializerRange)
      if sameType(mismatch.required, variable.typ)
    } yield mismatch.found

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (5)
tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala (1)

30-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import CodeAction instead of using the fully qualified name.

Line 6 imports org.eclipse.lsp4j.Diagnostic. Line 30 uses the fully qualified org.eclipse.lsp4j.CodeAction. Use one style.

♻️ Proposed change
+import org.eclipse.lsp4j.CodeAction
 import org.eclipse.lsp4j.Diagnostic
-  private val onlyChangeType: org.eclipse.lsp4j.CodeAction => Boolean =
+  private val onlyChangeType: CodeAction => Boolean =
     _.getTitle() == ChangeVariableType.title
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala`
around lines 30 - 31, Update the onlyChangeType declaration to use an imported
org.eclipse.lsp4j.CodeAction type, matching the existing Diagnostic import
style, and remove the fully qualified type reference.
mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala (1)

334-334: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm that the metals action declares maybeCodeActionId.

CodeActionProvider.actionsForParams filters actions with _.maybeCodeActionId.forall(supportedCodeActions.contains). The new ChangeVariableType action in metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala does not override maybeCodeActionId, so advertising the id here has no gating effect. Override maybeCodeActionId in the action if you want the compiler capability to control availability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala`
at line 334, Update ChangeVariableType to override maybeCodeActionId with
CodeActionId.ChangeVariableType, so CodeActionProvider.actionsForParams can gate
it using supportedCodeActions. Keep the ChangeVariableType advertisement in
JavaPresentationCompiler aligned with this declaration.
mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala (2)

369-401: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm nested-class handling in visibleName.

visibleName splits on the last dot. For a nested class such as a.Outer.Inner, packageName becomes a.Outer, so the import and package checks fail and the fully qualified name remains. That output still compiles, so this is not a defect. Add a test for a nested class initializer type if you want to lock the behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`
around lines 369 - 401, Add a regression test covering a nested-class
initializer type such as a.Outer.Inner and assert that visibleName preserves the
fully qualified name under the current SourceVisibility logic. Do not alter
visibleName or its visibility checks, since the existing output remains
compilable.

70-110: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add a cancellation check before the second compilation.

inferInitializerType triggers a full second compile of the patched source. textEdits checks cancellation only before the first nodeAtPosition call. ChangeVariableType.contribute sends one request per matching diagnostic, so a file with many incompatible-type diagnostics causes two compilations per diagnostic. A cancellation check before the second compile reduces wasted work.

♻️ Proposed change
     val adjustedParams = CompilerOffsetParams(
       params.uri(),
       adjustedText,
       adjustedOffset,
       params.token(),
       params.outlineFiles()
     )
 
+    params.checkCanceled()
     compiler.nodeAtPosition(adjustedParams).flatMap {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`
around lines 70 - 110, In inferInitializerType, check the request cancellation
state immediately before the second compiler.nodeAtPosition call on the patched
source. Reuse the existing cancellation mechanism and abort without compiling
when cancellation has been requested; leave the initializer type inference flow
unchanged otherwise.
metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala (1)

30-57: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Filter diagnostics by the cursor position before sending compiler requests.

Every matching diagnostic in the file produces one compilers.codeAction request, and each request runs two compilations inside JavaChangeVariableTypeProvider. All requests use the same cursor position, so diagnosticMatchesInitializer discards results for diagnostics that do not enclose that position. Restrict matchingDiagnostics to diagnostics whose range contains position to avoid the extra work.

♻️ Proposed change
     val matchingDiagnostics =
-      params.getContext().getDiagnostics().asScala.toSeq.collect {
-        case diagnostic
-            if JavacDiagnostic.IncompatibleTypes
-              .unapply(diagnostic)
-              .isDefined =>
-          diagnostic
-      }
+      params
+        .getContext()
+        .getDiagnostics()
+        .asScala
+        .toSeq
+        .filter(diagnostic =>
+          JavacDiagnostic.IncompatibleTypes.unapply(diagnostic).isDefined
+        )

Add a range containment check on position in the same filter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala`
around lines 30 - 57, Update matchingDiagnostics in the ChangeVariableType
action to retain only incompatible-type diagnostics whose range contains
position, combining this check with the existing
JavacDiagnostic.IncompatibleTypes filter before invoking compilers.codeAction.
Keep the subsequent request and edit-building flow unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`:
- Around line 211-227: The isSingleDeclaration method incorrectly uses raw text
scanning to detect comma-separated declarators, so comments can hide sibling
variables. In
mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
lines 211-227, inspect sibling VariableTree nodes from the enclosing tree and
use that structure to determine whether the declaration is single, removing the
comma scan. In
tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
lines 221-234, add a checkActionsOnly case covering a comment before the comma,
such as int a = 1 /* c */, b = "test";.

---

Nitpick comments:
In
`@metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala`:
- Around line 30-57: Update matchingDiagnostics in the ChangeVariableType action
to retain only incompatible-type diagnostics whose range contains position,
combining this check with the existing JavacDiagnostic.IncompatibleTypes filter
before invoking compilers.codeAction. Keep the subsequent request and
edit-building flow unchanged.

In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`:
- Around line 369-401: Add a regression test covering a nested-class initializer
type such as a.Outer.Inner and assert that visibleName preserves the fully
qualified name under the current SourceVisibility logic. Do not alter
visibleName or its visibility checks, since the existing output remains
compilable.
- Around line 70-110: In inferInitializerType, check the request cancellation
state immediately before the second compiler.nodeAtPosition call on the patched
source. Reuse the existing cancellation mechanism and abort without compiling
when cancellation has been requested; leave the initializer type inference flow
unchanged otherwise.

In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala`:
- Line 334: Update ChangeVariableType to override maybeCodeActionId with
CodeActionId.ChangeVariableType, so CodeActionProvider.actionsForParams can gate
it using supportedCodeActions. Keep the ChangeVariableType advertisement in
JavaPresentationCompiler aligned with this declaration.

In
`@tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala`:
- Around line 30-31: Update the onlyChangeType declaration to use an imported
org.eclipse.lsp4j.CodeAction type, matching the existing Diagnostic import
style, and remove the fully qualified type reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a799aacc-f716-4481-8d9a-553faadb0717

📥 Commits

Reviewing files that changed from the base of the PR and between 112af68 and 4825916.

📒 Files selected for processing (8)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-interfaces/src/main/java/scala/meta/pc/CodeActionId.java
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaPresentationCompiler.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
🚧 Files skipped from review as they are similar to previous changes (3)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavacDiagnostic.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala (1)

273-315: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Legacy-array-suffix trimming is duplicated across two files. Both files implement the identical algorithm (detect a [] suffix after the variable name with the same LegacyArrayDimensions regex, then trim the type range back to the last non-whitespace character before the name) using near-identical code on different local range types. This duplication risks the two call sites (hover/navigation ranges versus the quick-fix edit range) silently diverging if a fix is applied in only one place.

  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala#L273-L315: extract typeRange/onlyLegacyArrayDimensions/lastNonWhitespaceBefore and the LegacyArrayDimensions regex into Positions (mirroring the findNameOffset extraction already done in this PR), operating on plain offsets/LineMap/text instead of the file-local JavaRange.
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala#L134-L186: replace trimLegacyArraySuffix and Context.lastNonWhitespaceBefore with calls to the same shared Positions utility, removing the local LegacyArrayDimensions regex.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala` around
lines 273 - 315, Extract the shared legacy-array-suffix detection and trimming
logic into Positions, using plain offsets, LineMap, and text and following the
existing findNameOffset extraction pattern. In
metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala lines 273-315,
replace typeRange, onlyLegacyArrayDimensions, lastNonWhitespaceBefore, and
LegacyArrayDimensions with the shared utility while preserving JavaRange
conversion. In
mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
lines 134-186, replace trimLegacyArraySuffix and Context.lastNonWhitespaceBefore
with Positions calls and remove the local regex.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`:
- Around line 199-215: The isSingleDeclaration check incorrectly treats mixed
declarators as separate because it compares type-tree identity. Compare sibling
and current VariableTree type source ranges using the existing Context.startOf
helper, preserving the sibling exclusion, and add an LSP test covering int a,
b[]; to verify the quick-fix is blocked. Use Metals MCP tools to compile and run
the relevant tests.

---

Nitpick comments:
In `@metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala`:
- Around line 273-315: Extract the shared legacy-array-suffix detection and
trimming logic into Positions, using plain offsets, LineMap, and text and
following the existing findNameOffset extraction pattern. In
metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala lines 273-315,
replace typeRange, onlyLegacyArrayDimensions, lastNonWhitespaceBefore, and
LegacyArrayDimensions with the shared utility while preserving JavaRange
conversion. In
mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
lines 134-186, replace trimLegacyArraySuffix and Context.lastNonWhitespaceBefore
with Positions calls and remove the local regex.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e84d799f-c85c-4734-8cdf-98ec632f72fc

📥 Commits

Reviewing files that changed from the base of the PR and between 4825916 and 228f0cf.

📒 Files selected for processing (7)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
🚧 Files skipped from review as they are similar to previous changes (2)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala

@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from 228f0cf to 7d9a747 Compare August 3, 2026 12:08
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from 7d9a747 to f6855fb Compare August 3, 2026 12:10

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala`:
- Around line 132-160: Update JavaTypeShortener.forPath to collect packages from
non-static wildcard imports and pass them into JavaTypeShortener, then update
shortenTopLevel to treat types from those packages as already accessible before
creating explicit imports. Preserve existing handling for java.lang, the current
package, claimed names, and explicit imports, and validate the change with the
requested wildcard-import scenarios.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bbe719c1-3ca5-47fc-9939-47a20e09463f

📥 Commits

Reviewing files that changed from the base of the PR and between 228f0cf and 7d9a747.

📒 Files selected for processing (7)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
🚧 Files skipped from review as they are similar to previous changes (4)
  • mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala

@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from 4a6d7ac to aa3973b Compare August 4, 2026 08:01

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala`:
- Around line 195-201: Update renderInferredType to return None immediately when
initializerType.getKind is TypeKind.INTERSECTION, before calling
shortener.shorten or creating the rendered type. Preserve the existing
annotation removal and isRenderableType validation for all other initializer
types.

In `@mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala`:
- Around line 92-96: Update shortenTopLevel’s wildcard-import branch so it does
not shorten a type when multiple wildcard imports can resolve the same
simpleName, including conflicts with enclosing-type members; retain the
qualified name or use compiler scope/import resolution to create an explicit
import, and only claim simpleName when resolution is unambiguous.
- Around line 162-186: The current implementation only collects member type
names from the immediate enclosing class via the single call to
enclosingClass(path).map(collectMemberTypeNames), which misses type declarations
from outer enclosing scopes. Modify the collection logic to recursively traverse
all enclosing classes up the hierarchy rather than stopping at the first one,
gathering member types from every ClassTree in the parent chain until reaching
the root. Keep the overall flow of combining topLevelTypeNames with the
collected names but ensure memberTypeNames now includes contributions from all
nesting levels.

In `@mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala`:
- Around line 21-28: Update findNameOffset to resolve declarator names using
Java token boundaries or the compiler-provided name position instead of scanning
raw text for the first identifier match. Ensure matches inside comments and
literals are skipped, preserving the correct offset for JavaTrees and
trimLegacyArraySuffix.
- Around line 40-44: Update the hasLegacyArraySuffix detection in
Positions.scala so it identifies legacy array dimensions using compiler or token
positions rather than matching the raw substring between nameEnd and typeEnd.
Allow comments and annotations between the variable name and dimensions, and
preserve the full javac type range so the resulting edit never includes or
deletes the variable name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b2381b2-7a2e-40b1-9e81-f4887a102869

📥 Commits

Reviewing files that changed from the base of the PR and between 228f0cf and aa3973b.

📒 Files selected for processing (7)
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala
  • metals/src/main/scala/scala/meta/internal/parsing/JavaTrees.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaChangeVariableTypeProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala
  • mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala
  • tests/unit/src/test/scala/tests/codeactions/ChangeVariableTypeLspSuite.scala
🚧 Files skipped from review as they are similar to previous changes (2)
  • mtags-java/src/main/scala/scala/meta/internal/jpc/JavaImplementAbstractMembersProvider.scala
  • metals/src/main/scala/scala/meta/internal/metals/codeactions/ChangeVariableType.scala

Comment thread mtags-java/src/main/scala/scala/meta/internal/jpc/JavaTypeShortener.scala Outdated
Comment thread mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala Outdated
Comment thread mtags-java/src/main/scala/scala/meta/internal/jpc/Positions.scala Outdated
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from aa3973b to 565de14 Compare August 4, 2026 09:57
@zielinsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@zielinsky

zielinsky commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from f2ad7a4 to f366630 Compare August 4, 2026 11:40
@zielinsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch 2 times, most recently from f6b555a to 54c17bd Compare August 4, 2026 13:42
@zielinsky
zielinsky force-pushed the metalsv2/change-variable-type-quick-fix branch from 54c17bd to d9cc97f Compare August 5, 2026 08:27
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

1 similar comment
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@tgodzik tgodzik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@tgodzik
tgodzik merged commit e4f1b11 into scalameta:main-v2 Aug 7, 2026
18 checks passed
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