diff --git a/go.mod b/go.mod index 2a32770..858d9f4 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,13 @@ module github.com/moderneinc/recipes-go go 1.25.0 -require github.com/openrewrite/rewrite/rewrite-go v0.0.29 +require github.com/openrewrite/rewrite/rewrite-go v0.0.30 require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.6.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect golang.org/x/mod v0.35.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4df8e50..8b82697 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,16 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/openrewrite/rewrite/rewrite-go v0.0.29 h1:Fo6ECB7n1ylCqO1FvFM5aAvxd8PEcpAvykShJukKiSE= -github.com/openrewrite/rewrite/rewrite-go v0.0.29/go.mod h1:YquZz5hJMNrl7quFsOhlgyc5+UTV05XyFBSLJITdtvw= +github.com/openrewrite/rewrite/rewrite-go v0.0.30 h1:jAliVCxsa9IwQt+ZdDuHAXwNDDh0+CsOdnOLwcvJvCY= +github.com/openrewrite/rewrite/rewrite-go v0.0.30/go.mod h1:6fC3xnUD4/MGZsuWVcEwWlaBsxZTP1ueCWf69rKLeQQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/recipes/errorhandling/use_errors_as.go b/recipes/errorhandling/use_errors_as.go index a998bb9..8a92940 100644 --- a/recipes/errorhandling/use_errors_as.go +++ b/recipes/errorhandling/use_errors_as.go @@ -124,8 +124,8 @@ func matchCommaOkTypeAssert(swi *golang.StatementWithInit) (string, java.Express return "", nil, nil } - // The value must be a TypeCast (type assertion) - tc, ok := ma.Values[0].Element.(*java.TypeCast) + // The value must be a type assertion + ta, ok := ma.Values[0].Element.(*golang.TypeAssertion) if !ok { return "", nil, nil } @@ -153,17 +153,17 @@ func matchCommaOkTypeAssert(swi *golang.StatementWithInit) (string, java.Express // The expression being asserted must be an error. // Check type info first; fall back to name heuristic. - if !looksLikeError(tc.Expr) { + if !looksLikeError(ta.Left.Element) { return "", nil, nil } // Extract the type from the type assertion (inside the ControlParentheses) - if tc.Clazz == nil { + if ta.AssertedType == nil { return "", nil, nil } - typeExpr := tc.Clazz.Tree.Element + typeExpr := ta.AssertedType.Tree.Element - return targetIdent.Name, typeExpr, tc.Expr + return targetIdent.Name, typeExpr, ta.Left.Element } // Reports whether the expression is assignable to error, deciding from the diff --git a/recipes/migration/jsonv2/relocate_raw_message.go b/recipes/migration/jsonv2/relocate_raw_message.go index 72d3526..39c02dd 100644 --- a/recipes/migration/jsonv2/relocate_raw_message.go +++ b/recipes/migration/jsonv2/relocate_raw_message.go @@ -80,6 +80,9 @@ func (v *relocateRawMessageVisitor) VisitCompilationUnit(cu *golang.CompilationU return drainQueuedImports(v, cu, p) } +// The type the rewritten nodes carry. +var jsontextValueType = &java.JavaTypeClass{FullyQualifiedName: "encoding/json/jsontext.Value", Kind: "Class"} + // Rewrites a json.RawMessage type reference to jsontext.Value, keeping the // original whitespace. func (v *relocateRawMessageVisitor) VisitFieldAccess(fa *java.FieldAccess, p any) java.J { @@ -95,10 +98,46 @@ func (v *relocateRawMessageVisitor) VisitFieldAccess(fa *java.FieldAccess, p any Before: fa.Name.Before, Element: &java.Identifier{Prefix: fa.Name.Element.Prefix, Name: "Value"}, } - c.Type = nil + c.Type = jsontextValueType return &c } +// A field keeps the type it was attributed at parse, so the declaration has to +// be re-attributed too: left alone it still reads as encoding/json.RawMessage, +// and RemoveImport rightly keeps an import the tree says is in use. +func (v *relocateRawMessageVisitor) VisitVariableDeclarations(vd *java.VariableDeclarations, p any) java.J { + vd = v.GoVisitor.VisitVariableDeclarations(vd, p).(*java.VariableDeclarations) + if !isJsontextValue(vd.TypeExpr) { + return vd + } + c := *vd + c.Variables = make([]java.RightPadded[*java.VariableDeclarator], len(vd.Variables)) + copy(c.Variables, vd.Variables) + for i, rp := range c.Variables { + if rp.Element == nil || rp.Element.Name == nil || rp.Element.Name.Type == jsontextValueType { + continue + } + declarator := *rp.Element + name := *declarator.Name + name.Type = jsontextValueType + declarator.Name = &name + c.Variables[i] = java.RightPadded[*java.VariableDeclarator]{ + Element: &declarator, After: rp.After, Markers: rp.Markers, + } + } + return &c +} + +// isJsontextValue reports whether expr is the `jsontext.Value` this recipe writes. +func isJsontextValue(expr java.Expression) bool { + fa, ok := expr.(*java.FieldAccess) + if !ok || fa.Name.Element == nil || fa.Name.Element.Name != "Value" { + return false + } + target, ok := fa.Target.(*java.Identifier) + return ok && target.Name == "jsontext" +} + // Rewrites a json.RawMessage(x) conversion to jsontext.Value(x). func (v *relocateRawMessageVisitor) VisitMethodInvocation(mi *java.MethodInvocation, p any) java.J { mi = v.GoVisitor.VisitMethodInvocation(mi, p).(*java.MethodInvocation) @@ -116,6 +155,7 @@ func (v *relocateRawMessageVisitor) VisitMethodInvocation(mi *java.MethodInvocat After: mi.Select.After, } c.Name = &java.Identifier{Prefix: mi.Name.Prefix, Name: "Value"} + c.MethodType = nil return &c } diff --git a/recipes/redundancy/remove_redundant_interface_assertion.go b/recipes/redundancy/remove_redundant_interface_assertion.go index 779afc5..276fa17 100644 --- a/recipes/redundancy/remove_redundant_interface_assertion.go +++ b/recipes/redundancy/remove_redundant_interface_assertion.go @@ -46,14 +46,14 @@ type removeRedundantInterfaceAssertionVisitor struct { visitor.GoVisitor } -func (v *removeRedundantInterfaceAssertionVisitor) VisitTypeCast(tc *java.TypeCast, p any) java.J { - tc = v.GoVisitor.VisitTypeCast(tc, p).(*java.TypeCast) +func (v *removeRedundantInterfaceAssertionVisitor) VisitTypeAssertion(ta *golang.TypeAssertion, p any) java.J { + ta = v.GoVisitor.VisitTypeAssertion(ta, p).(*golang.TypeAssertion) - if tc.Clazz == nil { - return tc + if ta.AssertedType == nil { + return ta } - inner := tc.Clazz.Tree.Element + inner := ta.AssertedType.Tree.Element isRedundant := false @@ -70,14 +70,14 @@ func (v *removeRedundantInterfaceAssertionVisitor) VisitTypeCast(tc *java.TypeCa } if !isRedundant { - return tc + return ta } - // Replace the type assertion with just the inner expression. - // The Expr already carries the correct prefix (the space between the + // Replace the type assertion with just the asserted expression. + // That expression already carries the correct prefix (the space between the // preceding token and the expression, e.g. the space after "=" in "_ = x.(any)"). - // We prepend tc.Prefix whitespace in case the TypeCast itself had leading space. - return prependExprPrefix(tc.Expr, tc.Prefix) + // We prepend ta.Prefix whitespace in case the assertion itself had leading space. + return prependExprPrefix(ta.Left.Element, ta.Prefix) } // prependExprPrefix prepends extra whitespace to an expression's existing prefix. diff --git a/recipes/redundancy/remove_redundant_sprintf.go b/recipes/redundancy/remove_redundant_sprintf.go index 026f288..a879c2a 100644 --- a/recipes/redundancy/remove_redundant_sprintf.go +++ b/recipes/redundancy/remove_redundant_sprintf.go @@ -12,6 +12,7 @@ import ( "github.com/openrewrite/rewrite/rewrite-go/pkg/recipe" recipegolang "github.com/openrewrite/rewrite/rewrite-go/pkg/recipe/golang" "github.com/openrewrite/rewrite/rewrite-go/pkg/template" + "github.com/openrewrite/rewrite/rewrite-go/pkg/tree/golang" "github.com/openrewrite/rewrite/rewrite-go/pkg/tree/java" "github.com/openrewrite/rewrite/rewrite-go/pkg/visitor" ) @@ -88,7 +89,7 @@ func withLeadingPrefix(e java.Expression, p java.Space) java.Expression { return n.WithPrefix(p) case *java.ArrayAccess: return n.WithPrefix(p) - case *java.TypeCast: + case *golang.TypeAssertion: return n.WithPrefix(p) } return e diff --git a/recipes/style/check_template_execute_error.go b/recipes/style/check_template_execute_error.go index 283ba07..528be9a 100644 --- a/recipes/style/check_template_execute_error.go +++ b/recipes/style/check_template_execute_error.go @@ -200,9 +200,13 @@ func buildIfInitErrCheck(mi *java.MethodInvocation) *golang.StatementWithInit { } return &golang.StatementWithInit{ - ID: uuid.New(), - Prefix: prefix, - Init: java.RightPadded[java.Statement]{Element: initAssign}, + ID: uuid.New(), + Prefix: prefix, + Init: java.RightPadded[java.Statement]{ + Element: initAssign, + // The printer emits the `;` between init and condition from this marker. + Markers: java.Markers{ID: uuid.New(), Entries: []java.Marker{golang.NewSemicolon()}}, + }, Statement: innerIf, } } diff --git a/recipes/style/use_comma_ok_type_assertion.go b/recipes/style/use_comma_ok_type_assertion.go index 8ce7e7c..77ab4e6 100644 --- a/recipes/style/use_comma_ok_type_assertion.go +++ b/recipes/style/use_comma_ok_type_assertion.go @@ -57,9 +57,9 @@ func (v *useCommaOkTypeAssertionVisitor) VisitBlock(block *java.Block, p any) ja continue } - // RHS must be a TypeCast (type assertion) - _, isCast := assign.Value.Element.(*java.TypeCast) - if !isCast { + // RHS must be a type assertion + _, isAssertion := assign.Value.Element.(*golang.TypeAssertion) + if !isAssertion { newStmts = append(newStmts, rp) continue }