diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md index 45db766719f..f78f1bdf732 100644 --- a/gopls/doc/analyzers.md +++ b/gopls/doc/analyzers.md @@ -636,6 +636,8 @@ will be simplified to: This is one of the simplifications that "gofmt -s" applies. +This analyzer ignores generated code. + Default: on. Package documentation: [simplifycompositelit](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifycompositelit) @@ -662,6 +664,8 @@ will be simplified to: This is one of the simplifications that "gofmt -s" applies. +This analyzer ignores generated code. + Default: on. Package documentation: [simplifyrange](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyrange) @@ -680,6 +684,8 @@ will be simplified to: This is one of the simplifications that "gofmt -s" applies. +This analyzer ignores generated code. + Default: on. Package documentation: [simplifyslice](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyslice) diff --git a/gopls/internal/analysis/simplifycompositelit/doc.go b/gopls/internal/analysis/simplifycompositelit/doc.go index fe40596746e..bda74c7db3f 100644 --- a/gopls/internal/analysis/simplifycompositelit/doc.go +++ b/gopls/internal/analysis/simplifycompositelit/doc.go @@ -19,4 +19,6 @@ // []T{{}, {}} // // This is one of the simplifications that "gofmt -s" applies. +// +// This analyzer ignores generated code. package simplifycompositelit diff --git a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go index c651206b05f..1bdce1d658c 100644 --- a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go +++ b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/gopls/internal/util/astutil" "golang.org/x/tools/internal/analysisinternal" ) @@ -34,9 +35,21 @@ var Analyzer = &analysis.Analyzer{ } func run(pass *analysis.Pass) (interface{}, error) { + // Gather information whether file is generated or not + generated := make(map[*token.File]bool) + for _, file := range pass.Files { + if astutil.IsGenerated(file) { + generated[pass.Fset.File(file.Pos())] = true + } + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)} inspect.Preorder(nodeFilter, func(n ast.Node) { + if _, ok := generated[pass.Fset.File(n.Pos())]; ok { + return // skip checking if it's generated code + } + expr := n.(*ast.CompositeLit) outer := expr diff --git a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit_test.go b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit_test.go index a355616e3fe..4445a0cbb2f 100644 --- a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit_test.go +++ b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit_test.go @@ -13,5 +13,5 @@ import ( func Test(t *testing.T) { testdata := analysistest.TestData() - analysistest.RunWithSuggestedFixes(t, testdata, simplifycompositelit.Analyzer, "a") + analysistest.RunWithSuggestedFixes(t, testdata, simplifycompositelit.Analyzer, "a", "generatedcode") } diff --git a/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go b/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go new file mode 100644 index 00000000000..7b11dc5ba47 --- /dev/null +++ b/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +type T struct { + x, y int +} + +var _ = [42]T{ + T{}, // No simplification fix is offered in generated code. + T{1, 2}, // No simplification fix is offered in generated code. + T{3, 4}, // No simplification fix is offered in generated code. +} diff --git a/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go.golden b/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go.golden new file mode 100644 index 00000000000..7b11dc5ba47 --- /dev/null +++ b/gopls/internal/analysis/simplifycompositelit/testdata/src/generatedcode/generatedcode.go.golden @@ -0,0 +1,17 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +type T struct { + x, y int +} + +var _ = [42]T{ + T{}, // No simplification fix is offered in generated code. + T{1, 2}, // No simplification fix is offered in generated code. + T{3, 4}, // No simplification fix is offered in generated code. +} diff --git a/gopls/internal/analysis/simplifyrange/doc.go b/gopls/internal/analysis/simplifyrange/doc.go index f55ed56b35b..3d1145e0b09 100644 --- a/gopls/internal/analysis/simplifyrange/doc.go +++ b/gopls/internal/analysis/simplifyrange/doc.go @@ -27,4 +27,6 @@ // for range v {...} // // This is one of the simplifications that "gofmt -s" applies. +// +// This analyzer ignores generated code. package simplifyrange diff --git a/gopls/internal/analysis/simplifyrange/simplifyrange.go b/gopls/internal/analysis/simplifyrange/simplifyrange.go index 537e0e97081..ce9d450582b 100644 --- a/gopls/internal/analysis/simplifyrange/simplifyrange.go +++ b/gopls/internal/analysis/simplifyrange/simplifyrange.go @@ -14,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/gopls/internal/util/astutil" "golang.org/x/tools/internal/analysisinternal" ) @@ -29,11 +30,23 @@ var Analyzer = &analysis.Analyzer{ } func run(pass *analysis.Pass) (interface{}, error) { + // Gather information whether file is generated or not + generated := make(map[*token.File]bool) + for _, file := range pass.Files { + if astutil.IsGenerated(file) { + generated[pass.Fset.File(file.Pos())] = true + } + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ (*ast.RangeStmt)(nil), } inspect.Preorder(nodeFilter, func(n ast.Node) { + if _, ok := generated[pass.Fset.File(n.Pos())]; ok { + return // skip checking if it's generated code + } + var copy *ast.RangeStmt // shallow-copy the AST before modifying { x := *n.(*ast.RangeStmt) diff --git a/gopls/internal/analysis/simplifyrange/simplifyrange_test.go b/gopls/internal/analysis/simplifyrange/simplifyrange_test.go index fd927c56df1..973144c30e8 100644 --- a/gopls/internal/analysis/simplifyrange/simplifyrange_test.go +++ b/gopls/internal/analysis/simplifyrange/simplifyrange_test.go @@ -15,7 +15,7 @@ import ( func Test(t *testing.T) { testdata := analysistest.TestData() - analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "a") + analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "a", "generatedcode") if slices.Contains(build.Default.ReleaseTags, "go1.23") { // uses iter.Seq analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "rangeoverfunc") } diff --git a/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go b/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go new file mode 100644 index 00000000000..36b935c77eb --- /dev/null +++ b/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +import "log" + +func mgeneratedcode() { + maps := make(map[string]string) + for k, _ := range maps { // No simplification fix is offered in generated code. + log.Println(k) + } + for _ = range maps { // No simplification fix is offered in generated code. + } +} diff --git a/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go.golden b/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go.golden new file mode 100644 index 00000000000..36b935c77eb --- /dev/null +++ b/gopls/internal/analysis/simplifyrange/testdata/src/generatedcode/generatedcode.go.golden @@ -0,0 +1,18 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +import "log" + +func mgeneratedcode() { + maps := make(map[string]string) + for k, _ := range maps { // No simplification fix is offered in generated code. + log.Println(k) + } + for _ = range maps { // No simplification fix is offered in generated code. + } +} diff --git a/gopls/internal/analysis/simplifyslice/doc.go b/gopls/internal/analysis/simplifyslice/doc.go index 2fb4c461054..4c6808acd53 100644 --- a/gopls/internal/analysis/simplifyslice/doc.go +++ b/gopls/internal/analysis/simplifyslice/doc.go @@ -19,4 +19,6 @@ // s[a:] // // This is one of the simplifications that "gofmt -s" applies. +// +// This analyzer ignores generated code. package simplifyslice diff --git a/gopls/internal/analysis/simplifyslice/simplifyslice.go b/gopls/internal/analysis/simplifyslice/simplifyslice.go index 0c7cc3ff284..343fca8b185 100644 --- a/gopls/internal/analysis/simplifyslice/simplifyslice.go +++ b/gopls/internal/analysis/simplifyslice/simplifyslice.go @@ -10,10 +10,12 @@ import ( "fmt" "go/ast" "go/printer" + "go/token" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/gopls/internal/util/astutil" "golang.org/x/tools/internal/analysisinternal" ) @@ -37,11 +39,23 @@ var Analyzer = &analysis.Analyzer{ // x, y := b[:n], b[n:] func run(pass *analysis.Pass) (interface{}, error) { + // Gather information whether file is generated or not + generated := make(map[*token.File]bool) + for _, file := range pass.Files { + if astutil.IsGenerated(file) { + generated[pass.Fset.File(file.Pos())] = true + } + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ (*ast.SliceExpr)(nil), } inspect.Preorder(nodeFilter, func(n ast.Node) { + if _, ok := generated[pass.Fset.File(n.Pos())]; ok { + return // skip checking if it's generated code + } + expr := n.(*ast.SliceExpr) // - 3-index slices always require the 2nd and 3rd index if expr.Max != nil { diff --git a/gopls/internal/analysis/simplifyslice/simplifyslice_test.go b/gopls/internal/analysis/simplifyslice/simplifyslice_test.go index 969161e3c90..7fc5f9af451 100644 --- a/gopls/internal/analysis/simplifyslice/simplifyslice_test.go +++ b/gopls/internal/analysis/simplifyslice/simplifyslice_test.go @@ -13,5 +13,5 @@ import ( func Test(t *testing.T) { testdata := analysistest.TestData() - analysistest.RunWithSuggestedFixes(t, testdata, simplifyslice.Analyzer, "a", "typeparams") + analysistest.RunWithSuggestedFixes(t, testdata, simplifyslice.Analyzer, "a", "generatedcode", "typeparams") } diff --git a/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go b/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go new file mode 100644 index 00000000000..a291600d11f --- /dev/null +++ b/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go @@ -0,0 +1,72 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] // No simplification fix is offered in generated code. + _ = a[3:(len(a))] + _ = a[len(a)-1 : len(a)] // No simplification fix is offered in generated code. + _ = a[2:len(a):len(a)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] // No simplification fix is offered in generated code. + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(a):len(a)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] // No simplification fix is offered in generated code. + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + _ = s[2:len(s):len(s)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] // No simplification fix is offered in generated code. + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + _ = s[:len(s):len(s)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + _ = t.s[2:len(t.s):len(t.s)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] + _ = t.s[:len(t.s):len(t.s)] +) + +func _() { + s := s[0:len(s)] // No simplification fix is offered in generated code. + _ = s +} + +func m() { + maps := []int{} + _ = maps[1:len(maps)] // No simplification fix is offered in generated code. +} diff --git a/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go.golden b/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go.golden new file mode 100644 index 00000000000..a291600d11f --- /dev/null +++ b/gopls/internal/analysis/simplifyslice/testdata/src/generatedcode/generatedcode.go.golden @@ -0,0 +1,72 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated with somegen DO NOT EDIT. + +package testdata + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] // No simplification fix is offered in generated code. + _ = a[3:(len(a))] + _ = a[len(a)-1 : len(a)] // No simplification fix is offered in generated code. + _ = a[2:len(a):len(a)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] // No simplification fix is offered in generated code. + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(a):len(a)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] // No simplification fix is offered in generated code. + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + _ = s[2:len(s):len(s)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] // No simplification fix is offered in generated code. + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + _ = s[:len(s):len(s)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + _ = t.s[2:len(t.s):len(t.s)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] + _ = t.s[:len(t.s):len(t.s)] +) + +func _() { + s := s[0:len(s)] // No simplification fix is offered in generated code. + _ = s +} + +func m() { + maps := []int{} + _ = maps[1:len(maps)] // No simplification fix is offered in generated code. +} diff --git a/gopls/internal/doc/api.json b/gopls/internal/doc/api.json index 7de6006fb8a..322707dd085 100644 --- a/gopls/internal/doc/api.json +++ b/gopls/internal/doc/api.json @@ -524,17 +524,17 @@ }, { "Name": "\"simplifycompositelit\"", - "Doc": "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\n\t[]T{T{}, T{}}\n\nwill be simplified to:\n\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\n\t[]T{T{}, T{}}\n\nwill be simplified to:\n\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "Default": "true" }, { "Name": "\"simplifyrange\"", - "Doc": "check for range statement simplifications\n\nA range of the form:\n\n\tfor x, _ = range v {...}\n\nwill be simplified to:\n\n\tfor x = range v {...}\n\nA range of the form:\n\n\tfor _ = range v {...}\n\nwill be simplified to:\n\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for range statement simplifications\n\nA range of the form:\n\n\tfor x, _ = range v {...}\n\nwill be simplified to:\n\n\tfor x = range v {...}\n\nA range of the form:\n\n\tfor _ = range v {...}\n\nwill be simplified to:\n\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "Default": "true" }, { "Name": "\"simplifyslice\"", - "Doc": "check for slice simplifications\n\nA slice expression of the form:\n\n\ts[a:len(s)]\n\nwill be simplified to:\n\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for slice simplifications\n\nA slice expression of the form:\n\n\ts[a:len(s)]\n\nwill be simplified to:\n\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "Default": "true" }, { @@ -1186,19 +1186,19 @@ }, { "Name": "simplifycompositelit", - "Doc": "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\n\t[]T{T{}, T{}}\n\nwill be simplified to:\n\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\n\t[]T{T{}, T{}}\n\nwill be simplified to:\n\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifycompositelit", "Default": true }, { "Name": "simplifyrange", - "Doc": "check for range statement simplifications\n\nA range of the form:\n\n\tfor x, _ = range v {...}\n\nwill be simplified to:\n\n\tfor x = range v {...}\n\nA range of the form:\n\n\tfor _ = range v {...}\n\nwill be simplified to:\n\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for range statement simplifications\n\nA range of the form:\n\n\tfor x, _ = range v {...}\n\nwill be simplified to:\n\n\tfor x = range v {...}\n\nA range of the form:\n\n\tfor _ = range v {...}\n\nwill be simplified to:\n\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyrange", "Default": true }, { "Name": "simplifyslice", - "Doc": "check for slice simplifications\n\nA slice expression of the form:\n\n\ts[a:len(s)]\n\nwill be simplified to:\n\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + "Doc": "check for slice simplifications\n\nA slice expression of the form:\n\n\ts[a:len(s)]\n\nwill be simplified to:\n\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.\n\nThis analyzer ignores generated code.", "URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyslice", "Default": true }, diff --git a/gopls/internal/util/astutil/util.go b/gopls/internal/util/astutil/util.go index ac7515d1daf..b9cf9a03c1d 100644 --- a/gopls/internal/util/astutil/util.go +++ b/gopls/internal/util/astutil/util.go @@ -7,6 +7,7 @@ package astutil import ( "go/ast" "go/token" + "strings" "golang.org/x/tools/internal/typeparams" ) @@ -69,3 +70,25 @@ L: // unpack receiver type func NodeContains(n ast.Node, pos token.Pos) bool { return n.Pos() <= pos && pos <= n.End() } + +// IsGenerated check if a file is generated code +func IsGenerated(file *ast.File) bool { + // TODO: replace this implementation with calling function ast.IsGenerated when go1.21 is assured + for _, group := range file.Comments { + for _, comment := range group.List { + if comment.Pos() > file.Package { + break // after package declaration + } + // opt: check Contains first to avoid unnecessary array allocation in Split. + const prefix = "// Code generated " + if strings.Contains(comment.Text, prefix) { + for _, line := range strings.Split(comment.Text, "\n") { + if strings.HasPrefix(line, prefix) && strings.HasSuffix(line, " DO NOT EDIT.") { + return true + } + } + } + } + } + return false +}