diff options
| author | Taras Madan <tarasmadan@google.com> | 2025-01-22 16:07:17 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2025-01-23 10:42:36 +0000 |
| commit | 7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch) | |
| tree | e6fec4fd12ff807a16d847923f501075bf71d16c /vendor/honnef.co/go/tools/analysis/code/code.go | |
| parent | 475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff) | |
vendor: delete
Diffstat (limited to 'vendor/honnef.co/go/tools/analysis/code/code.go')
| -rw-r--r-- | vendor/honnef.co/go/tools/analysis/code/code.go | 593 |
1 files changed, 0 insertions, 593 deletions
diff --git a/vendor/honnef.co/go/tools/analysis/code/code.go b/vendor/honnef.co/go/tools/analysis/code/code.go deleted file mode 100644 index 7d20ea4c9..000000000 --- a/vendor/honnef.co/go/tools/analysis/code/code.go +++ /dev/null @@ -1,593 +0,0 @@ -// Package code answers structural and type questions about Go code. -package code - -import ( - "fmt" - "go/ast" - "go/build/constraint" - "go/constant" - "go/token" - "go/types" - "go/version" - "path/filepath" - "strings" - - "honnef.co/go/tools/analysis/facts/generated" - "honnef.co/go/tools/analysis/facts/purity" - "honnef.co/go/tools/analysis/facts/tokenfile" - "honnef.co/go/tools/go/ast/astutil" - "honnef.co/go/tools/go/types/typeutil" - "honnef.co/go/tools/knowledge" - "honnef.co/go/tools/pattern" - - "golang.org/x/tools/go/analysis" -) - -type Positioner interface { - Pos() token.Pos -} - -func IsOfStringConvertibleByteSlice(pass *analysis.Pass, expr ast.Expr) bool { - typ, ok := pass.TypesInfo.TypeOf(expr).Underlying().(*types.Slice) - if !ok { - return false - } - elem := types.Unalias(typ.Elem()) - if version.Compare(LanguageVersion(pass, expr), "go1.18") >= 0 { - // Before Go 1.18, one could not directly convert from []T (where 'type T byte') - // to string. See also https://github.com/golang/go/issues/23536. - elem = elem.Underlying() - } - return types.Identical(elem, types.Typ[types.Byte]) -} - -func IsOfPointerToTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool { - ptr, ok := types.Unalias(pass.TypesInfo.TypeOf(expr)).(*types.Pointer) - if !ok { - return false - } - return typeutil.IsTypeWithName(ptr.Elem(), name) -} - -func IsOfTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool { - return typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(expr), name) -} - -func IsInTest(pass *analysis.Pass, node Positioner) bool { - // FIXME(dh): this doesn't work for global variables with - // initializers - f := pass.Fset.File(node.Pos()) - return f != nil && strings.HasSuffix(f.Name(), "_test.go") -} - -// IsMain reports whether the package being processed is a package -// main. -func IsMain(pass *analysis.Pass) bool { - return pass.Pkg.Name() == "main" -} - -// IsMainLike reports whether the package being processed is a -// main-like package. A main-like package is a package that is -// package main, or that is intended to be used by a tool framework -// such as cobra to implement a command. -// -// Note that this function errs on the side of false positives; it may -// return true for packages that aren't main-like. IsMainLike is -// intended for analyses that wish to suppress diagnostics for -// main-like packages to avoid false positives. -func IsMainLike(pass *analysis.Pass) bool { - if pass.Pkg.Name() == "main" { - return true - } - for _, imp := range pass.Pkg.Imports() { - if imp.Path() == "github.com/spf13/cobra" { - return true - } - } - return false -} - -func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string { - info := pass.TypesInfo - sel := info.Selections[expr] - if sel == nil { - if x, ok := expr.X.(*ast.Ident); ok { - pkg, ok := info.ObjectOf(x).(*types.PkgName) - if !ok { - // This shouldn't happen - return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name) - } - return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name) - } - panic(fmt.Sprintf("unsupported selector: %v", expr)) - } - if v, ok := sel.Obj().(*types.Var); ok && v.IsField() { - return fmt.Sprintf("(%s).%s", typeutil.DereferenceR(sel.Recv()), sel.Obj().Name()) - } else { - return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name()) - } -} - -func IsNil(pass *analysis.Pass, expr ast.Expr) bool { - return pass.TypesInfo.Types[expr].IsNil() -} - -func BoolConst(pass *analysis.Pass, expr ast.Expr) bool { - val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val() - return constant.BoolVal(val) -} - -func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool { - // We explicitly don't support typed bools because more often than - // not, custom bool types are used as binary enums and the explicit - // comparison is desired. We err on the side of false negatives and - // treat aliases like other custom types. - - ident, ok := expr.(*ast.Ident) - if !ok { - return false - } - obj := pass.TypesInfo.ObjectOf(ident) - c, ok := obj.(*types.Const) - if !ok { - return false - } - basic, ok := c.Type().(*types.Basic) - if !ok { - return false - } - if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool { - return false - } - return true -} - -func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) { - tv := pass.TypesInfo.Types[expr] - if tv.Value == nil { - return 0, false - } - if tv.Value.Kind() != constant.Int { - return 0, false - } - return constant.Int64Val(tv.Value) -} - -func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) { - val := pass.TypesInfo.Types[expr].Value - if val == nil { - return "", false - } - if val.Kind() != constant.String { - return "", false - } - return constant.StringVal(val), true -} - -func CallName(pass *analysis.Pass, call *ast.CallExpr) string { - // See the comment in typeutil.FuncName for why this doesn't require special handling - // of aliases. - - fun := astutil.Unparen(call.Fun) - - // Instantiating a function cannot return another generic function, so doing this once is enough - switch idx := fun.(type) { - case *ast.IndexExpr: - fun = idx.X - case *ast.IndexListExpr: - fun = idx.X - } - - // (foo)[T] is not a valid instantiationg, so no need to unparen again. - - switch fun := fun.(type) { - case *ast.SelectorExpr: - fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func) - if !ok { - return "" - } - return typeutil.FuncName(fn) - case *ast.Ident: - obj := pass.TypesInfo.ObjectOf(fun) - switch obj := obj.(type) { - case *types.Func: - return typeutil.FuncName(obj) - case *types.Builtin: - return obj.Name() - default: - return "" - } - default: - return "" - } -} - -func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool { - // See the comment in typeutil.FuncName for why this doesn't require special handling - // of aliases. - - call, ok := node.(*ast.CallExpr) - if !ok { - return false - } - return CallName(pass, call) == name -} - -func IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool { - // See the comment in typeutil.FuncName for why this doesn't require special handling - // of aliases. - - call, ok := node.(*ast.CallExpr) - if !ok { - return false - } - q := CallName(pass, call) - for _, name := range names { - if q == name { - return true - } - } - return false -} - -func File(pass *analysis.Pass, node Positioner) *ast.File { - m := pass.ResultOf[tokenfile.Analyzer].(map[*token.File]*ast.File) - return m[pass.Fset.File(node.Pos())] -} - -// BuildConstraints returns the build constraints for file f. It considers both //go:build lines as well as -// GOOS and GOARCH in file names. -func BuildConstraints(pass *analysis.Pass, f *ast.File) (constraint.Expr, bool) { - var expr constraint.Expr - for _, cmt := range f.Comments { - if len(cmt.List) == 0 { - continue - } - for _, el := range cmt.List { - if el.Pos() > f.Package { - break - } - if line := el.Text; strings.HasPrefix(line, "//go:build") { - var err error - expr, err = constraint.Parse(line) - if err != nil { - expr = nil - } - break - } - } - } - - name := pass.Fset.PositionFor(f.Pos(), false).Filename - oexpr := constraintsFromName(name) - if oexpr != nil { - if expr == nil { - expr = oexpr - } else { - expr = &constraint.AndExpr{X: expr, Y: oexpr} - } - } - - return expr, expr != nil -} - -func constraintsFromName(name string) constraint.Expr { - name = filepath.Base(name) - name = strings.TrimSuffix(name, ".go") - name = strings.TrimSuffix(name, "_test") - var goos, goarch string - switch strings.Count(name, "_") { - case 0: - // No GOOS or GOARCH in the file name. - case 1: - _, c, _ := strings.Cut(name, "_") - if _, ok := knowledge.KnownGOOS[c]; ok { - goos = c - } else if _, ok := knowledge.KnownGOARCH[c]; ok { - goarch = c - } - default: - n := strings.LastIndex(name, "_") - if _, ok := knowledge.KnownGOOS[name[n+1:]]; ok { - // The file name is *_stuff_GOOS.go - goos = name[n+1:] - } else if _, ok := knowledge.KnownGOARCH[name[n+1:]]; ok { - // The file name is *_GOOS_GOARCH.go or *_stuff_GOARCH.go - goarch = name[n+1:] - _, c, _ := strings.Cut(name[:n], "_") - if _, ok := knowledge.KnownGOOS[c]; ok { - // The file name is *_GOOS_GOARCH.go - goos = c - } - } else { - // The file name could also be something like foo_windows_nonsense.go — and because nonsense - // isn't a known GOARCH, "windows" won't be interpreted as a GOOS, either. - } - } - - var expr constraint.Expr - if goos != "" { - expr = &constraint.TagExpr{Tag: goos} - } - if goarch != "" { - if expr == nil { - expr = &constraint.TagExpr{Tag: goarch} - } else { - expr = &constraint.AndExpr{X: expr, Y: &constraint.TagExpr{Tag: goarch}} - } - } - return expr -} - -// IsGenerated reports whether pos is in a generated file. It ignores -// //line directives. -func IsGenerated(pass *analysis.Pass, pos token.Pos) bool { - _, ok := Generator(pass, pos) - return ok -} - -// Generator returns the generator that generated the file containing -// pos. It ignores //line directives. -func Generator(pass *analysis.Pass, pos token.Pos) (generated.Generator, bool) { - file := pass.Fset.PositionFor(pos, false).Filename - m := pass.ResultOf[generated.Analyzer].(map[string]generated.Generator) - g, ok := m[file] - return g, ok -} - -// MayHaveSideEffects reports whether expr may have side effects. If -// the purity argument is nil, this function implements a purely -// syntactic check, meaning that any function call may have side -// effects, regardless of the called function's body. Otherwise, -// purity will be consulted to determine the purity of function calls. -func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity purity.Result) bool { - switch expr := expr.(type) { - case *ast.BadExpr: - return true - case *ast.Ellipsis: - return MayHaveSideEffects(pass, expr.Elt, purity) - case *ast.FuncLit: - // the literal itself cannot have side effects, only calling it - // might, which is handled by CallExpr. - return false - case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: - // types cannot have side effects - return false - case *ast.BasicLit: - return false - case *ast.BinaryExpr: - return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity) - case *ast.CallExpr: - if purity == nil { - return true - } - switch obj := typeutil.Callee(pass.TypesInfo, expr).(type) { - case *types.Func: - if _, ok := purity[obj]; !ok { - return true - } - case *types.Builtin: - switch obj.Name() { - case "len", "cap": - default: - return true - } - default: - return true - } - for _, arg := range expr.Args { - if MayHaveSideEffects(pass, arg, purity) { - return true - } - } - return false - case *ast.CompositeLit: - if MayHaveSideEffects(pass, expr.Type, purity) { - return true - } - for _, elt := range expr.Elts { - if MayHaveSideEffects(pass, elt, purity) { - return true - } - } - return false - case *ast.Ident: - return false - case *ast.IndexExpr: - return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity) - case *ast.IndexListExpr: - // In theory, none of the checks are necessary, as IndexListExpr only involves types. But there is no harm in - // being safe. - if MayHaveSideEffects(pass, expr.X, purity) { - return true - } - for _, idx := range expr.Indices { - if MayHaveSideEffects(pass, idx, purity) { - return true - } - } - return false - case *ast.KeyValueExpr: - return MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity) - case *ast.SelectorExpr: - return MayHaveSideEffects(pass, expr.X, purity) - case *ast.SliceExpr: - return MayHaveSideEffects(pass, expr.X, purity) || - MayHaveSideEffects(pass, expr.Low, purity) || - MayHaveSideEffects(pass, expr.High, purity) || - MayHaveSideEffects(pass, expr.Max, purity) - case *ast.StarExpr: - return MayHaveSideEffects(pass, expr.X, purity) - case *ast.TypeAssertExpr: - return MayHaveSideEffects(pass, expr.X, purity) - case *ast.UnaryExpr: - if MayHaveSideEffects(pass, expr.X, purity) { - return true - } - return expr.Op == token.ARROW || expr.Op == token.AND - case *ast.ParenExpr: - return MayHaveSideEffects(pass, expr.X, purity) - case nil: - return false - default: - panic(fmt.Sprintf("internal error: unhandled type %T", expr)) - } -} - -// LanguageVersion returns the version of the Go language that node has access to. This -// might differ from the version of the Go standard library. -func LanguageVersion(pass *analysis.Pass, node Positioner) string { - // As of Go 1.21, two places can specify the minimum Go version: - // - 'go' directives in go.mod and go.work files - // - individual files by using '//go:build' - // - // Individual files can upgrade to a higher version than the module version. Individual files - // can also downgrade to a lower version, but only if the module version is at least Go 1.21. - // - // The restriction on downgrading doesn't matter to us. All language changes before Go 1.22 will - // not type-check on versions that are too old, and thus never reach our analyzes. In practice, - // such ineffective downgrading will always be useless, as the compiler will not restrict the - // language features used, and doesn't ever rely on minimum versions to restrict the use of the - // standard library. However, for us, both choices (respecting or ignoring ineffective - // downgrading) have equal complexity, but only respecting it has a non-zero chance of reducing - // noisy positives. - // - // The minimum Go versions are exposed via go/ast.File.GoVersion and go/types.Package.GoVersion. - // ast.File's version is populated by the parser, whereas types.Package's version is populated - // from the Go version specified in the types.Config, which is set by our package loader, based - // on the module information provided by go/packages, via 'go list -json'. - // - // As of Go 1.21, standard library packages do not present themselves as modules, and thus do - // not have a version set on their types.Package. In this case, we fall back to the version - // provided by our '-go' flag. In most cases, '-go' defaults to 'module', which falls back to - // the Go version that Staticcheck was built with when no module information exists. In the - // future, the standard library will hopefully be a proper module (see - // https://github.com/golang/go/issues/61174#issuecomment-1622471317). In that case, the version - // of standard library packages will match that of the used Go version. At that point, - // Staticcheck will refuse to work with Go versions that are too new, to avoid misinterpreting - // code due to language changes. - // - // We also lack module information when building in GOPATH mode. In this case, the implied - // language version is at most Go 1.21, as per https://github.com/golang/go/issues/60915. We - // don't handle this yet, and it will not matter until Go 1.22. - // - // It is not clear how per-file downgrading behaves in GOPATH mode. On the one hand, no module - // version at all is provided, which should preclude per-file downgrading. On the other hand, - // https://github.com/golang/go/issues/60915 suggests that the language version is at most 1.21 - // in GOPATH mode, which would allow per-file downgrading. Again it doesn't affect us, as all - // relevant language changes before Go 1.22 will lead to type-checking failures and never reach - // us. - // - // Per-file upgrading is permitted in GOPATH mode. - - // If the file has its own Go version, we will return that. Otherwise, we default to - // the type checker's GoVersion, which is populated from either the Go module, or from - // our '-go' flag. - return pass.TypesInfo.FileVersions[File(pass, node)] -} - -// StdlibVersion returns the version of the Go standard library that node can expect to -// have access to. This might differ from the language version for versions of Go older -// than 1.21. -func StdlibVersion(pass *analysis.Pass, node Positioner) string { - // The Go version as specified in go.mod or via the '-go' flag - n := pass.Pkg.GoVersion() - - f := File(pass, node) - if f == nil { - panic(fmt.Sprintf("no file found for node with position %s", pass.Fset.PositionFor(node.Pos(), false))) - } - - if nf := f.GoVersion; nf != "" { - if version.Compare(n, "go1.21") == -1 { - // Before Go 1.21, the Go version set in go.mod specified the maximum language - // version available to the module. It wasn't uncommon to set the version to - // Go 1.20 but restrict usage of 1.20 functionality (both language and stdlib) - // to files tagged for 1.20, and supporting a lower version overall. As such, - // a file tagged lower than the module version couldn't expect to have access - // to the standard library of the version set in go.mod. - // - // At the same time, a file tagged higher than the module version, while not - // able to use newer language features, would still have been able to use a - // newer standard library. - // - // While Go 1.21's behavior has been backported to 1.19.11 and 1.20.6, users' - // expectations have not. - return nf - } else { - // Go 1.21 and newer refuse to build modules that depend on versions newer - // than the used version of the Go toolchain. This means that in a 1.22 module - // with a file tagged as 1.17, the file can expect to have access to 1.22's - // standard library (but not to 1.22 language features). A file tagged with a - // version higher than the minimum version has access to the newer standard - // library (and language features.) - // - // Do note that strictly speaking we're conflating the Go version and the - // module version in our check. Nothing is stopping a user from using Go 1.17 - // (which didn't implement the new rules for versions in go.mod) to build a Go - // 1.22 module, in which case a file tagged with go1.17 will not have acces to the 1.22 - // standard library. However, we believe that if a module requires 1.21 or - // newer, then the author clearly expects the new behavior, and doesn't care - // for the old one. Otherwise they would've specified an older version. - // - // In other words, the module version also specifies what it itself actually means, with - // >=1.21 being a minimum version for the toolchain, and <1.21 being a maximum version for - // the language. - - if version.Compare(nf, n) == 1 { - return nf - } - } - } - - return n -} - -var integerLiteralQ = pattern.MustParse(`(IntegerLiteral tv)`) - -func IntegerLiteral(pass *analysis.Pass, node ast.Node) (types.TypeAndValue, bool) { - m, ok := Match(pass, integerLiteralQ, node) - if !ok { - return types.TypeAndValue{}, false - } - return m.State["tv"].(types.TypeAndValue), true -} - -func IsIntegerLiteral(pass *analysis.Pass, node ast.Node, value constant.Value) bool { - tv, ok := IntegerLiteral(pass, node) - if !ok { - return false - } - return constant.Compare(tv.Value, token.EQL, value) -} - -// IsMethod reports whether expr is a method call of a named method with signature meth. -// If name is empty, it is not checked. -// For now, method expressions (Type.Method(recv, ..)) are not considered method calls. -func IsMethod(pass *analysis.Pass, expr *ast.SelectorExpr, name string, meth *types.Signature) bool { - if name != "" && expr.Sel.Name != name { - return false - } - sel, ok := pass.TypesInfo.Selections[expr] - if !ok || sel.Kind() != types.MethodVal { - return false - } - return types.Identical(sel.Type(), meth) -} - -func RefersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool { - found := false - fn := func(node ast.Node) bool { - ident2, ok := node.(*ast.Ident) - if !ok { - return true - } - if ident == pass.TypesInfo.ObjectOf(ident2) { - found = true - return false - } - return true - } - ast.Inspect(expr, fn) - return found -} |
