aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/honnef.co/go/tools/analysis/code/code.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/honnef.co/go/tools/analysis/code/code.go
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/honnef.co/go/tools/analysis/code/code.go')
-rw-r--r--vendor/honnef.co/go/tools/analysis/code/code.go358
1 files changed, 358 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/analysis/code/code.go b/vendor/honnef.co/go/tools/analysis/code/code.go
new file mode 100644
index 000000000..db7debc22
--- /dev/null
+++ b/vendor/honnef.co/go/tools/analysis/code/code.go
@@ -0,0 +1,358 @@
+// Package code answers structural and type questions about Go code.
+package code
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "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/pattern"
+
+ "golang.org/x/exp/typeparams"
+ "golang.org/x/tools/go/analysis"
+)
+
+type Positioner interface {
+ Pos() token.Pos
+}
+
+func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
+ return typeutil.IsType(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.
+
+ 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 {
+ 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 *typeparams.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 {
+ 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 {
+ 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())]
+}
+
+// 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 *typeparams.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))
+ }
+}
+
+func IsGoVersion(pass *analysis.Pass, minor int) bool {
+ f, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter)
+ if !ok {
+ panic("requested Go version, but analyzer has no version flag")
+ }
+ version := f.Get().(int)
+ return version >= minor
+}
+
+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)
+}