code.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Package code answers structural and type questions about Go code.
  2. package code
  3. import (
  4. "flag"
  5. "fmt"
  6. "go/ast"
  7. "go/constant"
  8. "go/token"
  9. "go/types"
  10. "strings"
  11. "honnef.co/go/tools/analysis/facts"
  12. "honnef.co/go/tools/go/ast/astutil"
  13. "honnef.co/go/tools/go/types/typeutil"
  14. "honnef.co/go/tools/pattern"
  15. "golang.org/x/exp/typeparams"
  16. "golang.org/x/tools/go/analysis"
  17. )
  18. type Positioner interface {
  19. Pos() token.Pos
  20. }
  21. func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
  22. return typeutil.IsType(pass.TypesInfo.TypeOf(expr), name)
  23. }
  24. func IsInTest(pass *analysis.Pass, node Positioner) bool {
  25. // FIXME(dh): this doesn't work for global variables with
  26. // initializers
  27. f := pass.Fset.File(node.Pos())
  28. return f != nil && strings.HasSuffix(f.Name(), "_test.go")
  29. }
  30. // IsMain reports whether the package being processed is a package
  31. // main.
  32. func IsMain(pass *analysis.Pass) bool {
  33. return pass.Pkg.Name() == "main"
  34. }
  35. // IsMainLike reports whether the package being processed is a
  36. // main-like package. A main-like package is a package that is
  37. // package main, or that is intended to be used by a tool framework
  38. // such as cobra to implement a command.
  39. //
  40. // Note that this function errs on the side of false positives; it may
  41. // return true for packages that aren't main-like. IsMainLike is
  42. // intended for analyses that wish to suppress diagnostics for
  43. // main-like packages to avoid false positives.
  44. func IsMainLike(pass *analysis.Pass) bool {
  45. if pass.Pkg.Name() == "main" {
  46. return true
  47. }
  48. for _, imp := range pass.Pkg.Imports() {
  49. if imp.Path() == "github.com/spf13/cobra" {
  50. return true
  51. }
  52. }
  53. return false
  54. }
  55. func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
  56. info := pass.TypesInfo
  57. sel := info.Selections[expr]
  58. if sel == nil {
  59. if x, ok := expr.X.(*ast.Ident); ok {
  60. pkg, ok := info.ObjectOf(x).(*types.PkgName)
  61. if !ok {
  62. // This shouldn't happen
  63. return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
  64. }
  65. return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
  66. }
  67. panic(fmt.Sprintf("unsupported selector: %v", expr))
  68. }
  69. if v, ok := sel.Obj().(*types.Var); ok && v.IsField() {
  70. return fmt.Sprintf("(%s).%s", typeutil.DereferenceR(sel.Recv()), sel.Obj().Name())
  71. } else {
  72. return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
  73. }
  74. }
  75. func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
  76. return pass.TypesInfo.Types[expr].IsNil()
  77. }
  78. func BoolConst(pass *analysis.Pass, expr ast.Expr) bool {
  79. val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
  80. return constant.BoolVal(val)
  81. }
  82. func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {
  83. // We explicitly don't support typed bools because more often than
  84. // not, custom bool types are used as binary enums and the
  85. // explicit comparison is desired.
  86. ident, ok := expr.(*ast.Ident)
  87. if !ok {
  88. return false
  89. }
  90. obj := pass.TypesInfo.ObjectOf(ident)
  91. c, ok := obj.(*types.Const)
  92. if !ok {
  93. return false
  94. }
  95. basic, ok := c.Type().(*types.Basic)
  96. if !ok {
  97. return false
  98. }
  99. if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
  100. return false
  101. }
  102. return true
  103. }
  104. func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
  105. tv := pass.TypesInfo.Types[expr]
  106. if tv.Value == nil {
  107. return 0, false
  108. }
  109. if tv.Value.Kind() != constant.Int {
  110. return 0, false
  111. }
  112. return constant.Int64Val(tv.Value)
  113. }
  114. func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
  115. val := pass.TypesInfo.Types[expr].Value
  116. if val == nil {
  117. return "", false
  118. }
  119. if val.Kind() != constant.String {
  120. return "", false
  121. }
  122. return constant.StringVal(val), true
  123. }
  124. func CallName(pass *analysis.Pass, call *ast.CallExpr) string {
  125. fun := astutil.Unparen(call.Fun)
  126. // Instantiating a function cannot return another generic function, so doing this once is enough
  127. switch idx := fun.(type) {
  128. case *ast.IndexExpr:
  129. fun = idx.X
  130. case *typeparams.IndexListExpr:
  131. fun = idx.X
  132. }
  133. // (foo)[T] is not a valid instantiationg, so no need to unparen again.
  134. switch fun := fun.(type) {
  135. case *ast.SelectorExpr:
  136. fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
  137. if !ok {
  138. return ""
  139. }
  140. return typeutil.FuncName(fn)
  141. case *ast.Ident:
  142. obj := pass.TypesInfo.ObjectOf(fun)
  143. switch obj := obj.(type) {
  144. case *types.Func:
  145. return typeutil.FuncName(obj)
  146. case *types.Builtin:
  147. return obj.Name()
  148. default:
  149. return ""
  150. }
  151. default:
  152. return ""
  153. }
  154. }
  155. func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool {
  156. call, ok := node.(*ast.CallExpr)
  157. if !ok {
  158. return false
  159. }
  160. return CallName(pass, call) == name
  161. }
  162. func IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool {
  163. call, ok := node.(*ast.CallExpr)
  164. if !ok {
  165. return false
  166. }
  167. q := CallName(pass, call)
  168. for _, name := range names {
  169. if q == name {
  170. return true
  171. }
  172. }
  173. return false
  174. }
  175. func File(pass *analysis.Pass, node Positioner) *ast.File {
  176. m := pass.ResultOf[facts.TokenFile].(map[*token.File]*ast.File)
  177. return m[pass.Fset.File(node.Pos())]
  178. }
  179. // IsGenerated reports whether pos is in a generated file, It ignores
  180. // //line directives.
  181. func IsGenerated(pass *analysis.Pass, pos token.Pos) bool {
  182. _, ok := Generator(pass, pos)
  183. return ok
  184. }
  185. // Generator returns the generator that generated the file containing
  186. // pos. It ignores //line directives.
  187. func Generator(pass *analysis.Pass, pos token.Pos) (facts.Generator, bool) {
  188. file := pass.Fset.PositionFor(pos, false).Filename
  189. m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
  190. g, ok := m[file]
  191. return g, ok
  192. }
  193. // MayHaveSideEffects reports whether expr may have side effects. If
  194. // the purity argument is nil, this function implements a purely
  195. // syntactic check, meaning that any function call may have side
  196. // effects, regardless of the called function's body. Otherwise,
  197. // purity will be consulted to determine the purity of function calls.
  198. func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity facts.PurityResult) bool {
  199. switch expr := expr.(type) {
  200. case *ast.BadExpr:
  201. return true
  202. case *ast.Ellipsis:
  203. return MayHaveSideEffects(pass, expr.Elt, purity)
  204. case *ast.FuncLit:
  205. // the literal itself cannot have side effects, only calling it
  206. // might, which is handled by CallExpr.
  207. return false
  208. case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
  209. // types cannot have side effects
  210. return false
  211. case *ast.BasicLit:
  212. return false
  213. case *ast.BinaryExpr:
  214. return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity)
  215. case *ast.CallExpr:
  216. if purity == nil {
  217. return true
  218. }
  219. switch obj := typeutil.Callee(pass.TypesInfo, expr).(type) {
  220. case *types.Func:
  221. if _, ok := purity[obj]; !ok {
  222. return true
  223. }
  224. case *types.Builtin:
  225. switch obj.Name() {
  226. case "len", "cap":
  227. default:
  228. return true
  229. }
  230. default:
  231. return true
  232. }
  233. for _, arg := range expr.Args {
  234. if MayHaveSideEffects(pass, arg, purity) {
  235. return true
  236. }
  237. }
  238. return false
  239. case *ast.CompositeLit:
  240. if MayHaveSideEffects(pass, expr.Type, purity) {
  241. return true
  242. }
  243. for _, elt := range expr.Elts {
  244. if MayHaveSideEffects(pass, elt, purity) {
  245. return true
  246. }
  247. }
  248. return false
  249. case *ast.Ident:
  250. return false
  251. case *ast.IndexExpr:
  252. return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity)
  253. case *typeparams.IndexListExpr:
  254. // In theory, none of the checks are necessary, as IndexListExpr only involves types. But there is no harm in
  255. // being safe.
  256. if MayHaveSideEffects(pass, expr.X, purity) {
  257. return true
  258. }
  259. for _, idx := range expr.Indices {
  260. if MayHaveSideEffects(pass, idx, purity) {
  261. return true
  262. }
  263. }
  264. return false
  265. case *ast.KeyValueExpr:
  266. return MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity)
  267. case *ast.SelectorExpr:
  268. return MayHaveSideEffects(pass, expr.X, purity)
  269. case *ast.SliceExpr:
  270. return MayHaveSideEffects(pass, expr.X, purity) ||
  271. MayHaveSideEffects(pass, expr.Low, purity) ||
  272. MayHaveSideEffects(pass, expr.High, purity) ||
  273. MayHaveSideEffects(pass, expr.Max, purity)
  274. case *ast.StarExpr:
  275. return MayHaveSideEffects(pass, expr.X, purity)
  276. case *ast.TypeAssertExpr:
  277. return MayHaveSideEffects(pass, expr.X, purity)
  278. case *ast.UnaryExpr:
  279. if MayHaveSideEffects(pass, expr.X, purity) {
  280. return true
  281. }
  282. return expr.Op == token.ARROW || expr.Op == token.AND
  283. case *ast.ParenExpr:
  284. return MayHaveSideEffects(pass, expr.X, purity)
  285. case nil:
  286. return false
  287. default:
  288. panic(fmt.Sprintf("internal error: unhandled type %T", expr))
  289. }
  290. }
  291. func IsGoVersion(pass *analysis.Pass, minor int) bool {
  292. f, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter)
  293. if !ok {
  294. panic("requested Go version, but analyzer has no version flag")
  295. }
  296. version := f.Get().(int)
  297. return version >= minor
  298. }
  299. var integerLiteralQ = pattern.MustParse(`(IntegerLiteral tv)`)
  300. func IntegerLiteral(pass *analysis.Pass, node ast.Node) (types.TypeAndValue, bool) {
  301. m, ok := Match(pass, integerLiteralQ, node)
  302. if !ok {
  303. return types.TypeAndValue{}, false
  304. }
  305. return m.State["tv"].(types.TypeAndValue), true
  306. }
  307. func IsIntegerLiteral(pass *analysis.Pass, node ast.Node, value constant.Value) bool {
  308. tv, ok := IntegerLiteral(pass, node)
  309. if !ok {
  310. return false
  311. }
  312. return constant.Compare(tv.Value, token.EQL, value)
  313. }