Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions rewrite-go/pkg/parser/go_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1546,10 +1546,10 @@ func (ctx *parseContext) mapForStmt(stmt *ast.ForStmt) *java.ForLoop {
}

// mapRangeStmt maps a for-range statement.
func (ctx *parseContext) mapRangeStmt(stmt *ast.RangeStmt) *java.ForEachLoop {
func (ctx *parseContext) mapRangeStmt(stmt *ast.RangeStmt) *golang.RangeLoop {
prefix := ctx.prefixAndSkip(stmt.Pos(), len("for"))

control := java.ForEachControl{ID: uuid.New()}
loop := &golang.RangeLoop{ID: uuid.New(), Prefix: prefix}

if stmt.Key != nil {
// Has key variable
Expand All @@ -1565,18 +1565,18 @@ func (ctx *parseContext) mapRangeStmt(stmt *ast.RangeStmt) *java.ForEachLoop {
ctx.skip(1) // ","
}
keyRP := java.RightPadded[java.Expression]{Element: key, After: keyAfter}
control.Key = &keyRP
loop.Key = &keyRP

value := ctx.mapExpr(stmt.Value)
// Value.After captures space before operator
opPrefix := ctx.prefix(stmt.TokPos)
valueRP := java.RightPadded[java.Expression]{Element: value, After: opPrefix}
control.Value = &valueRP
loop.Value = &valueRP
} else {
// for k := range expr {} — no value
opPrefix := ctx.prefix(stmt.TokPos)
keyRP := java.RightPadded[java.Expression]{Element: key, After: opPrefix}
control.Key = &keyRP
loop.Key = &keyRP
}

// Parse operator (:= or =)
Expand All @@ -1590,24 +1590,17 @@ func (ctx *parseContext) mapRangeStmt(stmt *ast.RangeStmt) *java.ForEachLoop {

// Space between operator and "range"
rangePrefix := ctx.prefix(stmt.Range)
control.Operator = java.LeftPadded[java.AssignOp]{Before: rangePrefix, Element: op}
loop.Operator = java.LeftPadded[java.AssignOp]{Before: rangePrefix, Element: op}
} else {
// for range expr {} — no variable
control.Prefix = ctx.prefix(stmt.Range)
// for range expr {} — no variable; the space before `range` lives in Operator.Before
loop.Operator = java.LeftPadded[java.AssignOp]{Before: ctx.prefix(stmt.Range)}
}
ctx.skip(len("range"))

iterable := ctx.mapExpr(stmt.X)
control.Iterable = iterable
loop.Iterable = ctx.mapExpr(stmt.X)
loop.Body = java.RightPadded[java.Statement]{Element: ctx.mapBlockStmt(stmt.Body), After: java.EmptySpace}

body := ctx.mapBlockStmt(stmt.Body)

return &java.ForEachLoop{
ID: uuid.New(),
Prefix: prefix,
Control: control,
Body: body,
}
return loop
}

// mapIncDecStmt maps an increment/decrement statement (x++ or x--).
Expand Down
36 changes: 14 additions & 22 deletions rewrite-go/pkg/printer/go_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,38 +553,30 @@ func (p *GoPrinter) VisitForControl(control *java.ForControl, param any) java.J
return control
}

func (p *GoPrinter) VisitForEachLoop(forEach *java.ForEachLoop, param any) java.J {
func (p *GoPrinter) VisitRangeLoop(loop *golang.RangeLoop, param any) java.J {
out := param.(*PrintOutputCapture)
p.beforeSyntax(forEach.Prefix, forEach.Markers, out)
p.beforeSyntax(loop.Prefix, loop.Markers, out)
out.Append("for")
p.Visit(&forEach.Control, out)
p.Visit(forEach.Body, out)
p.afterSyntax(forEach.Markers, out)
return forEach
}

func (p *GoPrinter) VisitForEachControl(control *java.ForEachControl, param any) java.J {
out := param.(*PrintOutputCapture)
p.beforeSyntax(control.Prefix, control.Markers, out)
if control.Key != nil {
p.Visit(control.Key.Element, out)
p.visitSpace(control.Key.After, out)
if control.Value != nil {
if loop.Key != nil {
p.Visit(loop.Key.Element, out)
p.visitSpace(loop.Key.After, out)
if loop.Value != nil {
out.Append(",")
p.Visit(control.Value.Element, out)
p.visitSpace(control.Value.After, out)
p.Visit(loop.Value.Element, out)
p.visitSpace(loop.Value.After, out)
}
if control.Operator.Element == java.AssignOpDefine {
if loop.Operator.Element == java.AssignOpDefine {
out.Append(":=")
} else {
out.Append("=")
}
p.visitSpace(control.Operator.Before, out)
}
p.visitSpace(loop.Operator.Before, out)
out.Append("range")
p.Visit(control.Iterable, out)
p.afterSyntax(control.Markers, out)
return control
p.Visit(loop.Iterable, out)
p.Visit(loop.Body.Element, out)
p.afterSyntax(loop.Markers, out)
return loop
}

// VisitAnnotation prints an Annotation in struct-tag form
Expand Down
37 changes: 37 additions & 0 deletions rewrite-go/pkg/rpc/go_receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,43 @@ func (r *GoReceiver) VisitGoVariadic(vr *golang.Variadic, p any) java.J {
return vr
}

func (r *GoReceiver) VisitRangeLoop(l *golang.RangeLoop, p any) java.J {
q := p.(*ReceiveQueue)
c := *l // shallow copy to avoid mutating remoteObjects baseline
l = &c
// key (right-padded, nullable)
var beforeKey any
if l.Key != nil {
beforeKey = *l.Key
}
if result := q.Receive(beforeKey, func(v any) any { return receiveRightPadded(r, q, v) }); result != nil {
rp := coerceToExpressionRP(result)
l.Key = &rp
} else {
l.Key = nil
}
// value (right-padded, nullable)
var beforeValue any
if l.Value != nil {
beforeValue = *l.Value
}
if result := q.Receive(beforeValue, func(v any) any { return receiveRightPadded(r, q, v) }); result != nil {
rp := coerceToExpressionRP(result)
l.Value = &rp
} else {
l.Value = nil
}
// operator (left-padded AssignOp enum)
l.Operator = receiveLeftPaddedEnum(r, q, l.Operator, parseAssignOpDefaulting)
// iterable
l.Iterable = receiveValue(q, l.Iterable, func(e java.Expression) any { return r.Visit(e, q) })
// body (right-padded Statement)
if result := q.Receive(l.Body, func(v any) any { return receiveRightPadded(r, q, v) }); result != nil {
l.Body = coerceToStatementRP(result)
}
return l
}

// VisitFallthrough mirrors GolangReceiver.visitFallthrough — the node has no
// payload beyond the framework-handled id/prefix/markers, so this override
// is intentionally a no-op. Present for sender/receiver symmetry.
Expand Down
32 changes: 32 additions & 0 deletions rewrite-go/pkg/rpc/go_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,38 @@ func (s *GoSender) VisitGoVariadic(vr *golang.Variadic, p any) java.J {
return vr
}

func (s *GoSender) VisitRangeLoop(l *golang.RangeLoop, p any) java.J {
q := p.(*SendQueue)
// key (right-padded, nullable)
q.GetAndSend(l, func(v any) any {
k := v.(*golang.RangeLoop).Key
if k == nil {
return nil
}
return *k
}, func(v any) { sendRightPadded(s, v, q) })
// value (right-padded, nullable)
q.GetAndSend(l, func(v any) any {
val := v.(*golang.RangeLoop).Value
if val == nil {
return nil
}
return *val
}, func(v any) { sendRightPadded(s, v, q) })
// operator (left-padded AssignOp, sent as faithful enum-constant name)
q.GetAndSend(l, func(v any) any {
op := v.(*golang.RangeLoop).Operator
return java.LeftPadded[string]{Before: op.Before, Element: op.Element.String(), Markers: op.Markers}
}, func(v any) { sendLeftPadded(s, v, q) })
// iterable
q.GetAndSend(l, func(v any) any { return v.(*golang.RangeLoop).Iterable },
func(v any) { s.Visit(v.(java.Tree), q) })
// body (right-padded Statement)
q.GetAndSend(l, func(v any) any { return v.(*golang.RangeLoop).Body },
func(v any) { sendRightPadded(s, v, q) })
return l
}

// VisitFallthrough mirrors GolangSender.visitFallthrough — the node has no
// payload beyond the framework-handled id/prefix/markers, so this override
// is intentionally a no-op. Present for sender/receiver symmetry.
Expand Down
51 changes: 0 additions & 51 deletions rewrite-go/pkg/rpc/java_receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,57 +552,6 @@ func (r *JavaReceiver) VisitForControl(fc *java.ForControl, p any) java.J {
return fc
}

func (r *JavaReceiver) VisitForEachLoop(f *java.ForEachLoop, p any) java.J {
q := p.(*ReceiveQueue)
c := *f // shallow copy to avoid mutating remoteObjects baseline
f = &c
ctrl := &f.Control
if result := q.Receive(ctrl, func(v any) any { return r.Visit(v.(java.Tree), q) }); result != nil {
f.Control = *result.(*java.ForEachControl)
}
// body - Java sends RightPadded<Statement> wrapping the Block
if bodyResult := q.Receive(nil, func(v any) any { return receiveRightPadded(r, q, v) }); bodyResult != nil {
rp := coerceToStatementRP(bodyResult)
if blk, ok := rp.Element.(*java.Block); ok {
f.Body = blk
}
}
return f
}

func (r *JavaReceiver) VisitForEachControl(fc *java.ForEachControl, p any) java.J {
q := p.(*ReceiveQueue)
c := *fc // shallow copy to avoid mutating remoteObjects baseline
fc = &c
// key (right-padded, nullable)
var beforeKey any
if fc.Key != nil {
beforeKey = *fc.Key
}
if result := q.Receive(beforeKey, func(v any) any { return receiveRightPadded(r, q, v) }); result != nil {
rp := coerceToExpressionRP(result)
fc.Key = &rp
} else {
fc.Key = nil
}
// value (right-padded, nullable)
var beforeValue any
if fc.Value != nil {
beforeValue = *fc.Value
}
if result := q.Receive(beforeValue, func(v any) any { return receiveRightPadded(r, q, v) }); result != nil {
rp := coerceToExpressionRP(result)
fc.Value = &rp
} else {
fc.Value = nil
}
// operator (left-padded AssignOp enum)
fc.Operator = receiveLeftPaddedEnum(r, q, fc.Operator, parseAssignOpDefaulting)
// iterable
fc.Iterable = receiveValue(q, fc.Iterable, func(e java.Expression) any { return r.Visit(e, q) })
return fc
}

func (r *JavaReceiver) VisitSwitch(sw *java.Switch, p any) java.J {
q := p.(*ReceiveQueue)
c := *sw // shallow copy to avoid mutating remoteObjects baseline
Expand Down
39 changes: 0 additions & 39 deletions rewrite-go/pkg/rpc/java_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,45 +386,6 @@ func (s *JavaSender) VisitForControl(fc *java.ForControl, p any) java.J {
return fc
}

func (s *JavaSender) VisitForEachLoop(f *java.ForEachLoop, p any) java.J {
q := p.(*SendQueue)
q.GetAndSend(f, func(v any) any {
ctrl := v.(*java.ForEachLoop).Control
return &ctrl
}, func(v any) { s.Visit(v.(java.Tree), q) })
q.GetAndSend(f, func(v any) any {
return java.RightPadded[java.Statement]{Element: v.(*java.ForEachLoop).Body, After: java.EmptySpace}
}, func(v any) { sendRightPadded(s, v, q) })
return f
}

func (s *JavaSender) VisitForEachControl(fc *java.ForEachControl, p any) java.J {
q := p.(*SendQueue)
// Go sends: key (right-padded), value (right-padded), operator (left-padded string), iterable
// Java GolangReceiver override reads this format
q.GetAndSend(fc, func(v any) any {
k := v.(*java.ForEachControl).Key
if k == nil {
return nil
}
return *k
}, func(v any) { sendRightPadded(s, v, q) })
q.GetAndSend(fc, func(v any) any {
val := v.(*java.ForEachControl).Value
if val == nil {
return nil
}
return *val
}, func(v any) { sendRightPadded(s, v, q) })
q.GetAndSend(fc, func(v any) any {
op := v.(*java.ForEachControl).Operator
return java.LeftPadded[string]{Before: op.Before, Element: op.Element.String(), Markers: op.Markers}
}, func(v any) { sendLeftPadded(s, v, q) })
q.GetAndSend(fc, func(v any) any { return v.(*java.ForEachControl).Iterable },
func(v any) { s.Visit(v.(java.Tree), q) })
return fc
}

func (s *JavaSender) VisitSwitch(sw *java.Switch, p any) java.J {
q := p.(*SendQueue)
// selector - wrap tag in ControlParentheses for Java's J.Switch model
Expand Down
6 changes: 2 additions & 4 deletions rewrite-go/pkg/rpc/value_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ func init() {
RegisterValueType(reflect.TypeOf((*java.MethodDeclaration)(nil)), "org.openrewrite.java.tree.J$MethodDeclaration")
RegisterValueType(reflect.TypeOf((*java.ForLoop)(nil)), "org.openrewrite.java.tree.J$ForLoop")
RegisterValueType(reflect.TypeOf((*java.ForControl)(nil)), "org.openrewrite.java.tree.J$ForLoop$Control")
RegisterValueType(reflect.TypeOf((*java.ForEachLoop)(nil)), "org.openrewrite.java.tree.J$ForEachLoop")
RegisterValueType(reflect.TypeOf((*java.ForEachControl)(nil)), "org.openrewrite.java.tree.J$ForEachLoop$Control")
RegisterValueType(reflect.TypeOf((*golang.RangeLoop)(nil)), "org.openrewrite.golang.tree.Go$RangeLoop")
RegisterValueType(reflect.TypeOf((*java.Switch)(nil)), "org.openrewrite.java.tree.J$Switch")
RegisterValueType(reflect.TypeOf((*java.Case)(nil)), "org.openrewrite.java.tree.J$Case")
RegisterValueType(reflect.TypeOf((*java.Break)(nil)), "org.openrewrite.java.tree.J$Break")
Expand Down Expand Up @@ -186,8 +185,7 @@ func init() {
RegisterFactory("org.openrewrite.java.tree.J$MethodDeclaration", func() any { return &java.MethodDeclaration{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$ForLoop", func() any { return &java.ForLoop{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$ForLoop$Control", func() any { return &java.ForControl{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$ForEachLoop", func() any { return &java.ForEachLoop{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$ForEachLoop$Control", func() any { return &java.ForEachControl{ID: uuid.New()} })
RegisterFactory("org.openrewrite.golang.tree.Go$RangeLoop", func() any { return &golang.RangeLoop{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$Switch", func() any { return &java.Switch{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$Case", func() any { return &java.Case{ID: uuid.New()} })
RegisterFactory("org.openrewrite.java.tree.J$Break", func() any { return &java.Break{ID: uuid.New()} })
Expand Down
13 changes: 6 additions & 7 deletions rewrite-go/pkg/template/comparator.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,19 +219,18 @@ func (c *patternComparator) matchProperties(pattern, candidate java.J) bool {
return false
}
return c.matchOptionalRightPaddedStmt(p.Update, cand.Update)
case *java.ForEachLoop:
cand := candidate.(*java.ForEachLoop)
return c.matchNode(&p.Control, &cand.Control) &&
c.matchNode(p.Body, cand.Body)
case *java.ForEachControl:
cand := candidate.(*java.ForEachControl)
case *golang.RangeLoop:
cand := candidate.(*golang.RangeLoop)
if !c.matchOptionalRightPaddedExpr2(p.Key, cand.Key) {
return false
}
if !c.matchOptionalRightPaddedExpr2(p.Value, cand.Value) {
return false
}
return c.matchNode(p.Iterable, cand.Iterable)
if !c.matchNode(p.Iterable, cand.Iterable) {
return false
}
return c.matchNode(p.Body.Element, cand.Body.Element)
case *java.Switch:
cand := candidate.(*java.Switch)
if !c.matchOptionalRightPaddedStmt(p.Init, cand.Init) {
Expand Down
2 changes: 1 addition & 1 deletion rewrite-go/pkg/template/go_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func getLeadingPrefix(j java.J) java.Space {
return n.Prefix
case *java.ForLoop:
return n.Prefix
case *java.ForEachLoop:
case *golang.RangeLoop:
return n.Prefix
case *java.Switch:
return n.Prefix
Expand Down
2 changes: 1 addition & 1 deletion rewrite-go/pkg/template/substitution.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func setPrefix(j java.J, prefix java.Space) java.J {
return n.WithPrefix(prefix)
case *java.ForLoop:
return n.WithPrefix(prefix)
case *java.ForEachLoop:
case *golang.RangeLoop:
return n.WithPrefix(prefix)
case *java.Switch:
return n.WithPrefix(prefix)
Expand Down
Loading