Skip to content

Commit b4caec2

Browse files
committed
Scala support
1 parent eb38a9b commit b4caec2

75 files changed

Lines changed: 13195 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@
5353
"Bash(../../gradlew dependencies --configuration runtimeClasspath)",
5454
"Bash(../../gradlew dependencies --configuration implementation)",
5555
"Bash(../../gradlew dependencies)",
56-
"Bash(../../gradlew dependencies --configuration compileClasspath)"
56+
"Bash(../../gradlew dependencies --configuration compileClasspath)",
57+
"Bash(javac:*)",
58+
"Bash(java:*)",
59+
"Bash(scalac:*)",
60+
"Bash(scala:*)"
5761
],
5862
"deny": []
5963
}

CLAUDE.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ When you need to know something about OpenRewrite:
88
- Refer to the rewrite-docs folder (if available)
99
- Consult Architecture Decision Records in `doc/adr/` for design decisions
1010

11+
## CRITICAL PRINCIPLES - NEVER VIOLATE THESE
12+
13+
### Never Regress from Rich Types to J.Unknown
14+
**ABSOLUTE RULE**: Once a syntax element has been mapped to a rich type (J.* or S.*), NEVER revert it back to J.Unknown. This is a fundamental architectural principle. J.Unknown should only be used for:
15+
1. Syntax we haven't implemented yet
16+
2. Temporary placeholders during initial development
17+
3. Truly unparseable or corrupted code
18+
19+
If you find yourself wanting to use J.Unknown for something already mapped, you're doing it wrong. Instead:
20+
- Create a new S.* type if needed
21+
- Use markers to preserve special behavior
22+
- Extend existing J.* types with Scala-specific markers
23+
- Find a way to map it to existing rich types
24+
25+
Going back to J.Unknown breaks type safety, loses semantic information, and makes the AST less useful for recipes.
26+
1127
## Project Overview
1228

1329
OpenRewrite is an automated refactoring ecosystem for source code that eliminates technical debt through AST-based transformations. The project uses a visitor pattern architecture where **Recipes** define transformations and **TreeVisitors** traverse and modify Abstract Syntax Trees (ASTs).

DebugAssignment.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
var (a, b) = (1, 2)
3+
println(s"a = $a, b = $b")
4+
5+
// This is the problematic line
6+
(a, b) = (3, 4)
7+
println(s"After assignment: a = $a, b = $b")
8+
}

DebugSpans.class

1.57 KB
Binary file not shown.

DebugSpans.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
public class DebugSpans {
2+
public static void main(String[] args) {
3+
String code = "object Test {\n var (a, b) = (1, 2)\n (a, b) = (3, 4)\n}";
4+
System.out.println("Source code:");
5+
System.out.println(code);
6+
System.out.println("\nLine 3: '(a, b) = (3, 4)'");
7+
8+
// Find the position of the second tuple
9+
int lineStart = code.indexOf(" (a, b)");
10+
System.out.println("Start of line 3: position " + lineStart);
11+
12+
for (int i = lineStart; i < lineStart + 20 && i < code.length(); i++) {
13+
char c = code.charAt(i);
14+
System.out.printf(" pos %2d: '%s'\n", i, c == '\n' ? "\\n" : String.valueOf(c));
15+
}
16+
}
17+
}

DebugTupleSpans.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import org.openrewrite.scala.ScalaParser;
2+
import org.openrewrite.scala.tree.S;
3+
import org.openrewrite.java.tree.J;
4+
5+
public class DebugTupleSpans {
6+
public static void main(String[] args) {
7+
String code = "object Test {\n var (a, b) = (1, 2)\n (a, b) = (3, 4)\n}";
8+
System.out.println("Source code:");
9+
System.out.println(code);
10+
System.out.println("\nCharacter positions:");
11+
for (int i = 0; i < code.length(); i++) {
12+
char c = code.charAt(i);
13+
System.out.printf("%2d: '%s'\n", i, c == '\n' ? "\\n" : String.valueOf(c));
14+
}
15+
}
16+
}

PartialTest.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class Test { def add(x: Int, y: Int): Int = x + y; val addFive = add(5, _) }

TestComplexType.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import org.openrewrite.java.tree.*;
2+
import org.openrewrite.marker.Markers;
3+
import java.util.Collections;
4+
5+
public class TestComplexType {
6+
public static void main(String[] args) {
7+
// Create a simple identifier "Int"
8+
J.Identifier intId = new J.Identifier(
9+
Tree.randomId(),
10+
Space.EMPTY,
11+
Markers.EMPTY,
12+
Collections.emptyList(),
13+
"Int",
14+
null,
15+
null
16+
);
17+
18+
System.out.println("Identifier simpleName: " + intId.getSimpleName());
19+
System.out.println("Identifier toString: " + intId);
20+
}
21+
}

TestDebugTuple.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test {
2+
var (a, b) = (1, 2)
3+
(a, b) = (3, 4)
4+
}

TestSpanDebug.scala

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import dotty.tools.dotc.ast.untpd
2+
import dotty.tools.dotc.core.Contexts.*
3+
import dotty.tools.dotc.parsing.Parsers.Parser
4+
import dotty.tools.dotc.util.SourceFile
5+
import dotty.tools.dotc.CompilationUnit
6+
import dotty.tools.dotc.core.Contexts.Context
7+
import dotty.tools.dotc.Driver
8+
import dotty.tools.dotc.config.Settings
9+
10+
object TestSpanDebug {
11+
def main(args: Array[String]): Unit = {
12+
val code = """object Test {
13+
var (a, b) = (1, 2)
14+
(a, b) = (3, 4)
15+
}"""
16+
17+
// Setup compiler context
18+
val driver = new Driver
19+
given Context = driver.initCtx.fresh
20+
21+
val source = SourceFile.virtual("test.scala", code)
22+
val unit = CompilationUnit(source)
23+
val parser = Parser(source)
24+
val tree = parser.parse()
25+
26+
def showTree(t: untpd.Tree, indent: Int = 0): Unit = {
27+
val prefix = " " * indent
28+
val span = t.span
29+
val spanStr = if (span.exists) {
30+
val start = span.start
31+
val end = span.end
32+
val content = if (start >= 0 && end <= code.length) {
33+
code.substring(start, end).replace("\n", "\\n")
34+
} else "???"
35+
s"[${start}-${end}] '$content'"
36+
} else "[no span]"
37+
38+
println(s"${prefix}${t.getClass.getSimpleName}: $spanStr")
39+
40+
t match {
41+
case pkg: untpd.PackageDef =>
42+
pkg.stats.foreach(showTree(_, indent + 1))
43+
case mod: untpd.ModuleDef =>
44+
mod.impl.body.foreach(showTree(_, indent + 1))
45+
case vd: untpd.ValDef =>
46+
println(s"${prefix} name: ${vd.name}")
47+
if (vd.tpt != null) showTree(vd.tpt, indent + 1)
48+
if (vd.rhs != null) showTree(vd.rhs, indent + 1)
49+
case asg: untpd.Assign =>
50+
println(s"${prefix} LHS:")
51+
showTree(asg.lhs, indent + 2)
52+
println(s"${prefix} RHS:")
53+
showTree(asg.rhs, indent + 2)
54+
case tuple: untpd.Tuple =>
55+
println(s"${prefix} Elements:")
56+
tuple.trees.foreach(showTree(_, indent + 2))
57+
case _ =>
58+
// Show children
59+
t.productIterator.foreach {
60+
case child: untpd.Tree => showTree(child, indent + 1)
61+
case list: List[_] => list.foreach {
62+
case tree: untpd.Tree => showTree(tree, indent + 1)
63+
case _ =>
64+
}
65+
case _ =>
66+
}
67+
}
68+
}
69+
70+
showTree(tree)
71+
}
72+
}

0 commit comments

Comments
 (0)