Skip to content

Commit 2089dce

Browse files
ChanDirMarker (#7432)
1 parent 55a227d commit 2089dce

5 files changed

Lines changed: 129 additions & 11 deletions

File tree

rewrite-go/pkg/parser/go_parser.go

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ func (ctx *parseContext) mapFieldListAsParams(fl *ast.FieldList) tree.Container[
760760
} else {
761761
closeParen := ctx.prefix(fl.Closing)
762762
ctx.skip(1) // ")"
763-
if len(closeParen.Comments) > 0 {
763+
if !closeParen.IsEmpty() {
764764
elements = append(elements, tree.RightPadded[tree.Statement]{
765765
Element: &tree.Empty{ID: uuid.New()},
766766
After: closeParen,
@@ -2193,8 +2193,14 @@ func (ctx *parseContext) mapSliceExpr(expr *ast.SliceExpr) tree.Expression {
21932193
// mapMapType maps a map type expression like `map[K]V`.
21942194
func (ctx *parseContext) mapMapType(expr *ast.MapType) tree.Expression {
21952195
prefix := ctx.prefixAndSkip(expr.Map, len("map"))
2196-
lbrackPrefix := ctx.prefix(expr.Map + token.Pos(len("map")))
2197-
ctx.skip(1) // "["
2196+
lbrackOff := ctx.findNext('[')
2197+
var lbrackPrefix tree.Space
2198+
if lbrackOff >= 0 {
2199+
lbrackPrefix = ctx.prefix(ctx.file.Pos(lbrackOff))
2200+
ctx.skip(1) // "["
2201+
} else {
2202+
ctx.skip(1) // "["
2203+
}
21982204
key := ctx.mapTypeExpr(expr.Key)
21992205
rbrackOff := ctx.findNext(']')
22002206
var rbrackPrefix tree.Space
@@ -2216,26 +2222,60 @@ func (ctx *parseContext) mapMapType(expr *ast.MapType) tree.Expression {
22162222
// mapChanType maps a channel type expression.
22172223
func (ctx *parseContext) mapChanType(expr *ast.ChanType) tree.Expression {
22182224
prefix := ctx.prefix(expr.Begin)
2225+
var markers tree.Markers
22192226

22202227
var dir tree.ChanDir
22212228
switch expr.Dir {
22222229
case ast.SEND:
22232230
dir = tree.ChanSendOnly
2224-
ctx.skip(len("chan<-"))
2231+
ctx.skip(len("chan"))
2232+
arrowOff := ctx.findNext('<')
2233+
var dirMarkerBefore tree.Space
2234+
if arrowOff >= 0 {
2235+
dirMarkerBefore = ctx.prefix(ctx.file.Pos(arrowOff))
2236+
ctx.cursor = arrowOff
2237+
}
2238+
ctx.skip(2) // "<-"
2239+
if !dirMarkerBefore.IsEmpty() {
2240+
markers = tree.Markers{
2241+
ID: uuid.New(),
2242+
Entries: []tree.Marker{tree.ChanDirMarker{
2243+
Ident: uuid.New(),
2244+
Before: dirMarkerBefore,
2245+
}},
2246+
}
2247+
}
22252248
case ast.RECV:
22262249
dir = tree.ChanRecvOnly
2227-
ctx.skip(len("<-chan"))
2250+
ctx.skip(2) // "<-"
2251+
chanOff := ctx.findNext('c')
2252+
var dirMarkerBefore tree.Space
2253+
if chanOff >= 0 && chanOff+4 <= len(ctx.src) && string(ctx.src[chanOff:chanOff+4]) == "chan" {
2254+
dirMarkerBefore = ctx.prefix(ctx.file.Pos(chanOff))
2255+
ctx.cursor = chanOff
2256+
}
2257+
ctx.skip(len("chan"))
2258+
if !dirMarkerBefore.IsEmpty() {
2259+
markers = tree.Markers{
2260+
ID: uuid.New(),
2261+
Entries: []tree.Marker{tree.ChanDirMarker{
2262+
Ident: uuid.New(),
2263+
Before: dirMarkerBefore,
2264+
}},
2265+
}
2266+
}
22282267
default:
22292268
dir = tree.ChanBidi
22302269
ctx.skip(len("chan"))
22312270
}
22322271

22332272
value := ctx.mapTypeExpr(expr.Value)
22342273
return &tree.Channel{
2235-
ID: uuid.New(),
2236-
Prefix: prefix,
2237-
Dir: dir,
2238-
Value: value,
2274+
ID: uuid.New(),
2275+
Prefix: prefix,
2276+
Markers: markers,
2277+
Dir: dir,
2278+
Value: value,
22392279
}
22402280
}
22412281

rewrite-go/pkg/printer/go_printer.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,13 +800,23 @@ func (p *GoPrinter) VisitPointerType(pt *tree.PointerType, param any) tree.J {
800800
func (p *GoPrinter) VisitChannel(ch *tree.Channel, param any) tree.J {
801801
out := param.(*PrintOutputCapture)
802802
p.beforeSyntax(ch.Prefix, ch.Markers, out)
803+
804+
var dirMarker tree.Space
805+
if marker := tree.FindMarker[tree.ChanDirMarker](ch.Markers); marker != nil {
806+
dirMarker = marker.Before
807+
}
808+
803809
switch ch.Dir {
804810
case tree.ChanBidi:
805811
out.Append("chan")
806812
case tree.ChanSendOnly:
807-
out.Append("chan<-")
813+
out.Append("chan")
814+
p.visitSpace(dirMarker, out)
815+
out.Append("<-")
808816
case tree.ChanRecvOnly:
809-
out.Append("<-chan")
817+
out.Append("<-")
818+
p.visitSpace(dirMarker, out)
819+
out.Append("chan")
810820
}
811821
p.Visit(ch.Value, out)
812822
p.afterSyntax(ch.Markers, out)

rewrite-go/pkg/tree/go.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,16 @@ type ImportBlock struct {
511511

512512
func (b ImportBlock) ID() uuid.UUID { return b.Ident }
513513

514+
// ChanDirMarker stores whitespace around the direction operator in a channel type.
515+
// For send channels (`chan <- T`), Before holds the space before `<-`.
516+
// For recv channels (`<- chan T`), Before holds the space before `chan`.
517+
type ChanDirMarker struct {
518+
Ident uuid.UUID
519+
Before Space
520+
}
521+
522+
func (c ChanDirMarker) ID() uuid.UUID { return c.Ident }
523+
514524
// MultiAssignment represents a multi-value assignment: `x, y = 1, 2` or `x, y := f()`.
515525
type MultiAssignment struct {
516526
ID uuid.UUID
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.golang.tree;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.openrewrite.marker.Marker;
21+
import org.openrewrite.java.tree.Space;
22+
23+
import java.util.UUID;
24+
25+
@Value
26+
@EqualsAndHashCode(callSuper = false)
27+
public class ChanDirMarker implements Marker {
28+
UUID id;
29+
Space before;
30+
31+
public ChanDirMarker(UUID id, Space before) {
32+
this.id = id;
33+
this.before = before;
34+
}
35+
36+
@Override
37+
public UUID getId() {
38+
return id;
39+
}
40+
41+
@Override
42+
public ChanDirMarker withId(UUID id) {
43+
return new ChanDirMarker(id, this.before);
44+
}
45+
}

rewrite-go/test/concurrency_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,16 @@ func TestParseSendStatement(t *testing.T) {
5454
}
5555
`))
5656
}
57+
58+
func TestParseConcurrencyIntenseWhitespace(t *testing.T) {
59+
NewRecipeSpec().RewriteRun(t,
60+
Golang(`
61+
package main
62+
63+
func f(send chan <- int, recv <- chan int) {
64+
send <- 42
65+
v := <-recv
66+
_ = v
67+
}
68+
`))
69+
}

0 commit comments

Comments
 (0)