internal/astutil: add EqualSyntax, moved from modernize Change-Id: Ia08518c031dce2f82d17049f911946fc03356014 Reviewed-on: https://go-review.googlesource.com/c/tools/+/708915 Reviewed-by: Robert Findley <rfindley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/go/analysis/passes/modernize/bloop.go b/go/analysis/passes/modernize/bloop.go index 5d7c9e5..9578468 100644 --- a/go/analysis/passes/modernize/bloop.go +++ b/go/analysis/passes/modernize/bloop.go
@@ -18,6 +18,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -241,7 +242,7 @@ isZeroIntLiteral(info, assign.Rhs[0]) && is[*ast.IncDecStmt](loop.Post) && loop.Post.(*ast.IncDecStmt).Tok == token.INC && - equalSyntax(loop.Post.(*ast.IncDecStmt).X, assign.Lhs[0]) { + astutil.EqualSyntax(loop.Post.(*ast.IncDecStmt).X, assign.Lhs[0]) { return info.Defs[assign.Lhs[0].(*ast.Ident)].(*types.Var) } return nil
diff --git a/go/analysis/passes/modernize/forvar.go b/go/analysis/passes/modernize/forvar.go index 2c3cd29..9a24856 100644 --- a/go/analysis/passes/modernize/forvar.go +++ b/go/analysis/passes/modernize/forvar.go
@@ -13,6 +13,7 @@ "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" + "golang.org/x/tools/internal/astutil" ) var ForVarAnalyzer = &analysis.Analyzer{ @@ -52,8 +53,8 @@ } isLoopVarRedecl := func(assign *ast.AssignStmt) bool { for i, lhs := range assign.Lhs { - if !(equalSyntax(lhs, assign.Rhs[i]) && - (equalSyntax(lhs, loop.Key) || equalSyntax(lhs, loop.Value))) { + if !(astutil.EqualSyntax(lhs, assign.Rhs[i]) && + (astutil.EqualSyntax(lhs, loop.Key) || astutil.EqualSyntax(lhs, loop.Value))) { return false } }
diff --git a/go/analysis/passes/modernize/maps.go b/go/analysis/passes/modernize/maps.go index 16747ed..d6a77c4 100644 --- a/go/analysis/passes/modernize/maps.go +++ b/go/analysis/passes/modernize/maps.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" ) @@ -99,7 +100,7 @@ if assign, ok := curPrev.Node().(*ast.AssignStmt); ok && len(assign.Lhs) == 1 && len(assign.Rhs) == 1 && - equalSyntax(assign.Lhs[0], m) { + astutil.EqualSyntax(assign.Lhs[0], m) { // Have: m = rhs; for k, v := range x { m[k] = v } var newMap bool @@ -235,8 +236,8 @@ assign := rng.Body.List[0].(*ast.AssignStmt) if index, ok := assign.Lhs[0].(*ast.IndexExpr); ok && - equalSyntax(rng.Key, index.Index) && - equalSyntax(rng.Value, assign.Rhs[0]) && + astutil.EqualSyntax(rng.Key, index.Index) && + astutil.EqualSyntax(rng.Value, assign.Rhs[0]) && is[*types.Map](typeparams.CoreType(info.TypeOf(index.X))) && types.Identical(info.TypeOf(index), info.TypeOf(rng.Value)) { // m[k], v
diff --git a/go/analysis/passes/modernize/minmax.go b/go/analysis/passes/modernize/minmax.go index 350a3c4..3896b28 100644 --- a/go/analysis/passes/modernize/minmax.go +++ b/go/analysis/passes/modernize/minmax.go
@@ -18,6 +18,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -92,10 +93,10 @@ // For pattern 1, check that: // - lhs = lhs2 // - {rhs,rhs2} = {a,b} - if equalSyntax(lhs, lhs2) { - if equalSyntax(rhs, a) && equalSyntax(rhs2, b) { + if astutil.EqualSyntax(lhs, lhs2) { + if astutil.EqualSyntax(rhs, a) && astutil.EqualSyntax(rhs2, b) { sign = +sign - } else if equalSyntax(rhs2, a) && equalSyntax(rhs, b) { + } else if astutil.EqualSyntax(rhs2, a) && astutil.EqualSyntax(rhs, b) { sign = -sign } else { return @@ -149,10 +150,10 @@ lhs0 := fassign.Lhs[0] rhs0 := fassign.Rhs[0] - if equalSyntax(lhs, lhs0) { - if equalSyntax(rhs, a) && (equalSyntax(rhs0, b) || equalSyntax(lhs0, b)) { + if astutil.EqualSyntax(lhs, lhs0) { + if astutil.EqualSyntax(rhs, a) && (astutil.EqualSyntax(rhs0, b) || astutil.EqualSyntax(lhs0, b)) { sign = +sign - } else if (equalSyntax(rhs0, a) || equalSyntax(lhs0, a)) && equalSyntax(rhs, b) { + } else if (astutil.EqualSyntax(rhs0, a) || astutil.EqualSyntax(lhs0, a)) && astutil.EqualSyntax(rhs, b) { sign = -sign } else { return @@ -166,9 +167,9 @@ // Permit lhs0 to stand for rhs0 in the matching, // but don't actually reduce to lhs0 = min(lhs0, rhs) // since the "=" could be a ":=". Use min(rhs0, rhs). - if equalSyntax(lhs0, a) { + if astutil.EqualSyntax(lhs0, a) { a = rhs0 - } else if equalSyntax(lhs0, b) { + } else if astutil.EqualSyntax(lhs0, b) { b = rhs0 } @@ -411,9 +412,9 @@ y := cmp.Y // right operand // Check operand order and adjust sign accordingly - if equalSyntax(t, x) && equalSyntax(f, y) { + if astutil.EqualSyntax(t, x) && astutil.EqualSyntax(f, y) { sign = +sign - } else if equalSyntax(t, y) && equalSyntax(f, x) { + } else if astutil.EqualSyntax(t, y) && astutil.EqualSyntax(f, x) { sign = -sign } else { return false
diff --git a/go/analysis/passes/modernize/modernize.go b/go/analysis/passes/modernize/modernize.go index 58814f0..6367012 100644 --- a/go/analysis/passes/modernize/modernize.go +++ b/go/analysis/passes/modernize/modernize.go
@@ -20,7 +20,6 @@ "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" - "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/moreiters" "golang.org/x/tools/internal/stdlib" "golang.org/x/tools/internal/versions" @@ -67,12 +66,6 @@ } } -// equalSyntax reports whether x and y are syntactically equal (ignoring comments). -func equalSyntax(x, y ast.Expr) bool { - sameName := func(x, y *ast.Ident) bool { return x.Name == y.Name } - return astutil.Equal(x, y, sameName) -} - // formatExprs formats a comma-separated list of expressions. func formatExprs(fset *token.FileSet, exprs []ast.Expr) string { var buf strings.Builder
diff --git a/go/analysis/passes/modernize/rangeint.go b/go/analysis/passes/modernize/rangeint.go index 3e07f7b..62c9740 100644 --- a/go/analysis/passes/modernize/rangeint.go +++ b/go/analysis/passes/modernize/rangeint.go
@@ -18,6 +18,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -85,7 +86,7 @@ if compare, ok := loop.Cond.(*ast.BinaryExpr); ok && compare.Op == token.LSS && - equalSyntax(compare.X, init.Lhs[0]) { + astutil.EqualSyntax(compare.X, init.Lhs[0]) { // Have: for i = 0; i < limit; ... {} limit := compare.Y @@ -126,7 +127,7 @@ if inc, ok := loop.Post.(*ast.IncDecStmt); ok && inc.Tok == token.INC && - equalSyntax(compare.X, inc.X) { + astutil.EqualSyntax(compare.X, inc.X) { // Have: for i = 0; i < limit; i++ {} // Find references to i within the loop body.
diff --git a/go/analysis/passes/modernize/slices.go b/go/analysis/passes/modernize/slices.go index e2dc6f8..42b01d2 100644 --- a/go/analysis/passes/modernize/slices.go +++ b/go/analysis/passes/modernize/slices.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" + "golang.org/x/tools/internal/astutil" ) // Warning: this analyzer is not safe to enable by default. @@ -261,12 +262,12 @@ switch e := e.(type) { case *ast.SliceExpr: // x[:0:0], x[:len(x):len(x)], x[:k:k] - if e.Slice3 && e.High != nil && e.Max != nil && equalSyntax(e.High, e.Max) { // x[:k:k] + if e.Slice3 && e.High != nil && e.Max != nil && astutil.EqualSyntax(e.High, e.Max) { // x[:k:k] res = e empty = isZeroIntLiteral(info, e.High) // x[:0:0] if call, ok := e.High.(*ast.CallExpr); ok && typeutil.Callee(info, call) == builtinLen && - equalSyntax(call.Args[0], e.X) { + astutil.EqualSyntax(call.Args[0], e.X) { res = e.X // x[:len(x):len(x)] -> x } return
diff --git a/go/analysis/passes/modernize/slicescontains.go b/go/analysis/passes/modernize/slicescontains.go index 447e30b..5480cd9 100644 --- a/go/analysis/passes/modernize/slicescontains.go +++ b/go/analysis/passes/modernize/slicescontains.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -87,12 +88,12 @@ // isSliceElem reports whether e denotes the // current slice element (elem or s[i]). isSliceElem := func(e ast.Expr) bool { - if rng.Value != nil && equalSyntax(e, rng.Value) { + if rng.Value != nil && astutil.EqualSyntax(e, rng.Value) { return true // "elem" } if x, ok := e.(*ast.IndexExpr); ok && - equalSyntax(x.X, rng.X) && - equalSyntax(x.Index, rng.Key) { + astutil.EqualSyntax(x.X, rng.X) && + astutil.EqualSyntax(x.Index, rng.Key) { return true // "s[i]" } return false @@ -322,7 +323,7 @@ if prevAssign, ok := prevStmt.(*ast.AssignStmt); ok && len(prevAssign.Lhs) == 1 && len(prevAssign.Rhs) == 1 && - equalSyntax(prevAssign.Lhs[0], assign.Lhs[0]) && + astutil.EqualSyntax(prevAssign.Lhs[0], assign.Lhs[0]) && is[*ast.Ident](assign.Rhs[0]) && info.Uses[assign.Rhs[0].(*ast.Ident)] == builtinTrue {
diff --git a/go/analysis/passes/modernize/slicesdelete.go b/go/analysis/passes/modernize/slicesdelete.go index f3c5504..6310372 100644 --- a/go/analysis/passes/modernize/slicesdelete.go +++ b/go/analysis/passes/modernize/slicesdelete.go
@@ -15,6 +15,7 @@ "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" + "golang.org/x/tools/internal/astutil" ) // Warning: this analyzer is not safe to enable by default (not nil-preserving). @@ -140,7 +141,7 @@ slice2, ok2 := call.Args[1].(*ast.SliceExpr) if ok1 && slice1.Low == nil && !slice1.Slice3 && ok2 && slice2.High == nil && !slice2.Slice3 && - equalSyntax(slice1.X, slice2.X) && noEffects(info, slice1.X) && + astutil.EqualSyntax(slice1.X, slice2.X) && noEffects(info, slice1.X) && increasingSliceIndices(info, slice1.High, slice2.Low) { // Have append(s[:a], s[b:]...) where we can verify a < b. report(file, call, slice1, slice2) @@ -176,5 +177,5 @@ ai, ak := split(a) bi, bk := split(b) - return equalSyntax(ai, bi) && constant.Compare(ak, token.LSS, bk) + return astutil.EqualSyntax(ai, bi) && constant.Compare(ak, token.LSS, bk) }
diff --git a/go/analysis/passes/modernize/sortslice.go b/go/analysis/passes/modernize/sortslice.go index 3950fc9..3b6b8da 100644 --- a/go/analysis/passes/modernize/sortslice.go +++ b/go/analysis/passes/modernize/sortslice.go
@@ -14,6 +14,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -79,7 +80,7 @@ isIndex := func(e ast.Expr, v *types.Var) bool { index, ok := e.(*ast.IndexExpr) return ok && - equalSyntax(index.X, s) && + astutil.EqualSyntax(index.X, s) && is[*ast.Ident](index.Index) && info.Uses[index.Index.(*ast.Ident)] == v }
diff --git a/go/analysis/passes/modernize/stditerators.go b/go/analysis/passes/modernize/stditerators.go index 1af3a59..e32c297 100644 --- a/go/analysis/passes/modernize/stditerators.go +++ b/go/analysis/passes/modernize/stditerators.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/goplsexport" "golang.org/x/tools/internal/stdlib" "golang.org/x/tools/internal/typesinternal/typeindex" @@ -133,7 +134,7 @@ // call to x.At(i)? if call, ok := assign.Rhs[0].(*ast.CallExpr); ok && typeutil.Callee(info, call) == atMethod && - equalSyntax(ast.Unparen(call.Fun).(*ast.SelectorExpr).X, x) && + astutil.EqualSyntax(ast.Unparen(call.Fun).(*ast.SelectorExpr).X, x) && is[*ast.Ident](call.Args[0]) && info.Uses[call.Args[0].(*ast.Ident)] == i { // Have: { elem := x.At(i); ... } @@ -282,7 +283,7 @@ atSel := ast.Unparen(atCall.Fun).(*ast.SelectorExpr) // Check receivers of Len, At calls match (syntactically). - if !equalSyntax(lenSel.X, atSel.X) { + if !astutil.EqualSyntax(lenSel.X, atSel.X) { continue nextCall }
diff --git a/go/analysis/passes/modernize/stringscutprefix.go b/go/analysis/passes/modernize/stringscutprefix.go index 199c0b5..4422bdd 100644 --- a/go/analysis/passes/modernize/stringscutprefix.go +++ b/go/analysis/passes/modernize/stringscutprefix.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -123,7 +124,7 @@ // check whether the obj1 uses the exact the same argument with strings.HasPrefix // shadow variables won't be valid because we only access the first statement (ditto Suffix). - if equalSyntax(s0, s) && equalSyntax(pre0, pre) { + if astutil.EqualSyntax(s0, s) && astutil.EqualSyntax(pre0, pre) { after := analysisinternal.FreshName(info.Scopes[ifStmt], ifStmt.Pos(), varName) _, prefix, importEdits := analysisinternal.AddImport( info, @@ -201,8 +202,8 @@ fixMessage = "Replace TrimSuffix with CutSuffix" } - if equalSyntax(lhs, bin.X) && equalSyntax(call.Args[0], bin.Y) || - (equalSyntax(lhs, bin.Y) && equalSyntax(call.Args[0], bin.X)) { + if astutil.EqualSyntax(lhs, bin.X) && astutil.EqualSyntax(call.Args[0], bin.Y) || + (astutil.EqualSyntax(lhs, bin.Y) && astutil.EqualSyntax(call.Args[0], bin.X)) { okVarName := analysisinternal.FreshName(info.Scopes[ifStmt], ifStmt.Pos(), "ok") // Have one of: // if rest := TrimPrefix(s, prefix); rest != s { (ditto Suffix)
diff --git a/go/analysis/passes/modernize/waitgroup.go b/go/analysis/passes/modernize/waitgroup.go index d7290ae..bbae38a 100644 --- a/go/analysis/passes/modernize/waitgroup.go +++ b/go/analysis/passes/modernize/waitgroup.go
@@ -17,6 +17,7 @@ "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/analysisinternal/generated" typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typesinternal/typeindex" ) @@ -107,13 +108,13 @@ var doneStmt ast.Stmt if deferStmt, ok := list[0].(*ast.DeferStmt); ok && typeutil.Callee(info, deferStmt.Call) == syncWaitGroupDone && - equalSyntax(ast.Unparen(deferStmt.Call.Fun).(*ast.SelectorExpr).X, addCallRecv) { + astutil.EqualSyntax(ast.Unparen(deferStmt.Call.Fun).(*ast.SelectorExpr).X, addCallRecv) { doneStmt = deferStmt // "defer wg.Done()" } else if lastStmt, ok := list[len(list)-1].(*ast.ExprStmt); ok { if doneCall, ok := lastStmt.X.(*ast.CallExpr); ok && typeutil.Callee(info, doneCall) == syncWaitGroupDone && - equalSyntax(ast.Unparen(doneCall.Fun).(*ast.SelectorExpr).X, addCallRecv) { + astutil.EqualSyntax(ast.Unparen(doneCall.Fun).(*ast.SelectorExpr).X, addCallRecv) { doneStmt = lastStmt // "wg.Done()" } }
diff --git a/internal/astutil/equal.go b/internal/astutil/equal.go index c945de0..210f392 100644 --- a/internal/astutil/equal.go +++ b/internal/astutil/equal.go
@@ -26,6 +26,14 @@ return equal(reflect.ValueOf(x), reflect.ValueOf(y), identical) } +// EqualSyntax reports whether x and y are equal. +// Identifiers are considered equal if they are spelled the same. +// Comments are ignored. +func EqualSyntax(x, y ast.Expr) bool { + sameName := func(x, y *ast.Ident) bool { return x.Name == y.Name } + return Equal(x, y, sameName) +} + func equal(x, y reflect.Value, identical func(x, y *ast.Ident) bool) bool { // Ensure types are the same if x.Type() != y.Type() {