diff options
Diffstat (limited to 'src/cmd/vendor/golang.org/x/tools/go/analysis/passes')
11 files changed, 253 insertions, 37 deletions
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go index f2ca95aa9e..3417232ce3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go @@ -173,7 +173,7 @@ func run(pass *analysis.Pass) (interface{}, error) { Files: for _, fname := range sfiles { - content, tf, err := analysisutil.ReadFile(pass.Fset, fname) + content, tf, err := analysisutil.ReadFile(pass, fname) if err != nil { return nil, err } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go index 55bdad78b7..51ba2a91e5 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go @@ -89,7 +89,7 @@ func checkOtherFile(pass *analysis.Pass, filename string) error { // We cannot use the Go parser, since this may not be a Go source file. // Read the raw bytes instead. - content, tf, err := analysisutil.ReadFile(pass.Fset, filename) + content, tf, err := analysisutil.ReadFile(pass, filename) if err != nil { return err } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go index 0001ba5363..19ef6b9bce 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go @@ -83,7 +83,7 @@ func checkGoFile(pass *analysis.Pass, f *ast.File) { } func checkOtherFile(pass *analysis.Pass, filename string) error { - content, tf, err := analysisutil.ReadFile(pass.Fset, filename) + content, tf, err := analysisutil.ReadFile(pass, filename) if err != nil { return err } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go index 8f39159c0f..8f6e7db6a2 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go @@ -31,7 +31,7 @@ values should be referred to through a pointer.` var Analyzer = &analysis.Analyzer{ Name: "copylocks", Doc: Doc, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/copylocks", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/copylock", Requires: []*analysis.Analyzer{inspect.Analyzer}, RunDespiteErrors: true, Run: run, @@ -240,7 +240,10 @@ func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath { return nil } } - return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type, nil) + if tv, ok := pass.TypesInfo.Types[x]; ok && tv.IsValue() { + return lockPath(pass.Pkg, tv.Type, nil) + } + return nil } // lockPath returns a typePath describing the location of a lock value diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go index 2691f189aa..b205402388 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go @@ -70,11 +70,7 @@ func checkGoFile(pass *analysis.Pass, f *ast.File) { check := newChecker(pass, pass.Fset.File(f.Package).Name(), f) for _, group := range f.Comments { - // A +build comment is ignored after or adjoining the package declaration. - if group.End()+1 >= f.Package { - check.inHeader = false - } - // A //go:build comment is ignored after the package declaration + // A //go:build or a //go:debug comment is ignored after the package declaration // (but adjoining it is OK, in contrast to +build comments). if group.Pos() >= f.Package { check.inHeader = false @@ -90,7 +86,7 @@ func checkGoFile(pass *analysis.Pass, f *ast.File) { func checkOtherFile(pass *analysis.Pass, filename string) error { // We cannot use the Go parser, since is not a Go source file. // Read the raw bytes instead. - content, tf, err := analysisutil.ReadFile(pass.Fset, filename) + content, tf, err := analysisutil.ReadFile(pass, filename) if err != nil { return err } @@ -104,8 +100,7 @@ type checker struct { pass *analysis.Pass filename string file *ast.File // nil for non-Go file - inHeader bool // in file header (before package declaration) - inStar bool // currently in a /* */ comment + inHeader bool // in file header (before or adjoining package declaration) } func newChecker(pass *analysis.Pass, filename string, file *ast.File) *checker { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go index 0b3ded47ea..6eff3a20fe 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go @@ -48,7 +48,7 @@ func run(pass *analysis.Pass) (interface{}, error) { } for _, fname := range sfiles { - content, tf, err := analysisutil.ReadFile(pass.Fset, fname) + content, tf, err := analysisutil.ReadFile(pass, fname) if err != nil { return nil, err } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go index 89291602a5..f7f071dc8b 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go @@ -14,6 +14,7 @@ import ( "go/types" "os" + "golang.org/x/tools/go/analysis" "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/analysisinternal" ) @@ -60,12 +61,16 @@ func HasSideEffects(info *types.Info, e ast.Expr) bool { // ReadFile reads a file and adds it to the FileSet // so that we can report errors against it using lineStart. -func ReadFile(fset *token.FileSet, filename string) ([]byte, *token.File, error) { - content, err := os.ReadFile(filename) +func ReadFile(pass *analysis.Pass, filename string) ([]byte, *token.File, error) { + readFile := pass.ReadFile + if readFile == nil { + readFile = os.ReadFile + } + content, err := readFile(filename) if err != nil { return nil, nil, err } - tf := fset.AddFile(filename, -1, len(content)) + tf := pass.Fset.AddFile(filename, -1, len(content)) tf.SetLinesForContent(content) return content, tf, nil } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go index 1ee16126ad..85da8346f7 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go @@ -11,14 +11,53 @@ // // The check applies to calls of the formatting functions such as // [fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of -// those functions. +// those functions such as [log.Printf]. It reports a variety of +// mistakes such as syntax errors in the format string and mismatches +// (of number and type) between the verbs and their arguments. // -// In this example, the %d format operator requires an integer operand: +// See the documentation of the fmt package for the complete set of +// format operators and their operand types. +// +// # Examples +// +// The %d format operator requires an integer operand. +// Here it is incorrectly applied to a string: // // fmt.Printf("%d", "hello") // fmt.Printf format %d has arg "hello" of wrong type string // -// See the documentation of the fmt package for the complete set of -// format operators and their operand types. +// A call to Printf must have as many operands as there are "verbs" in +// the format string, not too few: +// +// fmt.Printf("%d") // fmt.Printf format reads arg 1, but call has 0 args +// +// nor too many: +// +// fmt.Printf("%d", 1, 2) // fmt.Printf call needs 1 arg, but has 2 args +// +// Explicit argument indexes must be no greater than the number of +// arguments: +// +// fmt.Printf("%[3]d", 1, 2) // fmt.Printf call has invalid argument index 3 +// +// The checker also uses a heuristic to report calls to Print-like +// functions that appear to have been intended for their Printf-like +// counterpart: +// +// log.Print("%d", 123) // log.Print call has possible formatting directive %d +// +// # Inferred printf wrappers +// +// Functions that delegate their arguments to fmt.Printf are +// considered "printf wrappers"; calls to them are subject to the same +// checking. In this example, logf is a printf wrapper: +// +// func logf(level int, format string, args ...any) { +// if enabled(level) { +// log.Printf(format, args...) +// } +// } +// +// logf(3, "invalid request: %v") // logf format reads arg 1, but call has 0 args // // To enable printf checking on a function that is not found by this // analyzer's heuristics (for example, because control is obscured by @@ -26,14 +65,19 @@ // // func MyPrintf(format string, args ...any) { // if false { -// _ = fmt.Sprintf(format, args...) // enable printf checker +// _ = fmt.Sprintf(format, args...) // enable printf checking // } // ... // } // -// The -funcs flag specifies a comma-separated list of names of additional -// known formatting functions or methods. If the name contains a period, -// it must denote a specific function using one of the following forms: +// # Specifying printf wrappers by flag +// +// The -funcs flag specifies a comma-separated list of names of +// additional known formatting functions or methods. (This legacy flag +// is rarely used due to the automatic inference described above.) +// +// If the name contains a period, it must denote a specific function +// using one of the following forms: // // dir/pkg.Function // dir/pkg.Type.Method diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go new file mode 100644 index 0000000000..75d8697759 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go @@ -0,0 +1,159 @@ +// 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. + +// Package stdversion reports uses of standard library symbols that are +// "too new" for the Go version in force in the referring file. +package stdversion + +import ( + "go/ast" + "go/build" + "go/types" + "regexp" + + "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/internal/typesinternal" + "golang.org/x/tools/internal/versions" +) + +const Doc = `report uses of too-new standard library symbols + +The stdversion analyzer reports references to symbols in the standard +library that were introduced by a Go release higher than the one in +force in the referring file. (Recall that the file's Go version is +defined by the 'go' directive its module's go.mod file, or by a +"//go:build go1.X" build tag at the top of the file.) + +The analyzer does not report a diagnostic for a reference to a "too +new" field or method of a type that is itself "too new", as this may +have false positives, for example if fields or methods are accessed +through a type alias that is guarded by a Go version constraint. +` + +var Analyzer = &analysis.Analyzer{ + Name: "stdversion", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdversion", + RunDespiteErrors: true, + Run: run, +} + +func run(pass *analysis.Pass) (any, error) { + // Prior to go1.22, versions.FileVersion returns only the + // toolchain version, which is of no use to us, so + // disable this analyzer on earlier versions. + if !slicesContains(build.Default.ReleaseTags, "go1.22") { + return nil, nil + } + + // Don't report diagnostics for modules marked before go1.21, + // since at that time the go directive wasn't clearly + // specified as a toolchain requirement. + // + // TODO(adonovan): after go1.21, call GoVersion directly. + pkgVersion := any(pass.Pkg).(interface{ GoVersion() string }).GoVersion() + if !versions.AtLeast(pkgVersion, "go1.21") { + return nil, nil + } + + // disallowedSymbols returns the set of standard library symbols + // in a given package that are disallowed at the specified Go version. + type key struct { + pkg *types.Package + version string + } + memo := make(map[key]map[types.Object]string) // records symbol's minimum Go version + disallowedSymbols := func(pkg *types.Package, version string) map[types.Object]string { + k := key{pkg, version} + disallowed, ok := memo[k] + if !ok { + disallowed = typesinternal.TooNewStdSymbols(pkg, version) + memo[k] = disallowed + } + return disallowed + } + + // Scan the syntax looking for references to symbols + // that are disallowed by the version of the file. + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.File)(nil), + (*ast.Ident)(nil), + } + var fileVersion string // "" => no check + inspect.Preorder(nodeFilter, func(n ast.Node) { + switch n := n.(type) { + case *ast.File: + if isGenerated(n) { + // Suppress diagnostics in generated files (such as cgo). + fileVersion = "" + } else { + fileVersion = versions.Lang(versions.FileVersion(pass.TypesInfo, n)) + // (may be "" if unknown) + } + + case *ast.Ident: + if fileVersion != "" { + if obj, ok := pass.TypesInfo.Uses[n]; ok && obj.Pkg() != nil { + disallowed := disallowedSymbols(obj.Pkg(), fileVersion) + if minVersion, ok := disallowed[origin(obj)]; ok { + noun := "module" + if fileVersion != pkgVersion { + noun = "file" + } + pass.ReportRangef(n, "%s.%s requires %v or later (%s is %s)", + obj.Pkg().Name(), obj.Name(), minVersion, noun, fileVersion) + } + } + } + } + }) + return nil, nil +} + +// Reduced from x/tools/gopls/internal/golang/util.go. Good enough for now. +// TODO(adonovan): use ast.IsGenerated in go1.21. +func isGenerated(f *ast.File) bool { + for _, group := range f.Comments { + for _, comment := range group.List { + if matched := generatedRx.MatchString(comment.Text); matched { + return true + } + } + } + return false +} + +// Matches cgo generated comment as well as the proposed standard: +// +// https://golang.org/s/generatedcode +var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`) + +// origin returns the original uninstantiated symbol for obj. +func origin(obj types.Object) types.Object { + switch obj := obj.(type) { + case *types.Var: + return obj.Origin() + case *types.Func: + return obj.Origin() + case *types.TypeName: + if named, ok := obj.Type().(*types.Named); ok { // (don't unalias) + return named.Origin().Obj() + } + } + return obj +} + +// TODO(adonovan): use go1.21 slices.Contains. +func slicesContains[S ~[]E, E comparable](slice S, x E) bool { + for _, elem := range slice { + if elem == x { + return true + } + } + return false +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go index 16a4b3e551..c77182daef 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go @@ -59,14 +59,13 @@ func describe(typ, inType types.Type, inName string) string { return name } -func typeName(typ types.Type) string { - typ = aliases.Unalias(typ) - // TODO(adonovan): don't discard alias type, return its name. - if v, _ := typ.(*types.Basic); v != nil { - return v.Name() - } - if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil { // Named, TypeParam - return v.Obj().Name() +func typeName(t types.Type) string { + type hasTypeName interface{ Obj() *types.TypeName } // Alias, Named, TypeParam + switch t := t.(type) { + case *types.Basic: + return t.Name() + case hasTypeName: + return t.Obj().Name() } return "" } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go index 39d0d9e429..f5e760ca26 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go @@ -447,6 +447,18 @@ func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) { } } +type tokenRange struct { + p, e token.Pos +} + +func (r tokenRange) Pos() token.Pos { + return r.p +} + +func (r tokenRange) End() token.Pos { + return r.e +} + func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) { // Want functions with 0 results and 1 parameter. if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || @@ -464,12 +476,11 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) { if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 { // Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters. // We have currently decided to also warn before compilation/package loading. This can help users in IDEs. - // TODO(adonovan): use ReportRangef(tparams). - pass.Reportf(fn.Pos(), "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix) + at := tokenRange{tparams.Opening, tparams.Closing} + pass.ReportRangef(at, "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix) } if !isTestSuffix(fn.Name.Name[len(prefix):]) { - // TODO(adonovan): use ReportRangef(fn.Name). - pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix) + pass.ReportRangef(fn.Name, "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix) } } |