lintdsl.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // Package lintdsl provides helpers for implementing static analysis
  2. // checks. Dot-importing this package is encouraged.
  3. package lintdsl
  4. import (
  5. "bytes"
  6. "flag"
  7. "fmt"
  8. "go/ast"
  9. "go/constant"
  10. "go/printer"
  11. "go/token"
  12. "go/types"
  13. "strings"
  14. "golang.org/x/tools/go/analysis"
  15. "honnef.co/go/tools/facts"
  16. "honnef.co/go/tools/lint"
  17. "honnef.co/go/tools/ssa"
  18. )
  19. type packager interface {
  20. Package() *ssa.Package
  21. }
  22. func CallName(call *ssa.CallCommon) string {
  23. if call.IsInvoke() {
  24. return ""
  25. }
  26. switch v := call.Value.(type) {
  27. case *ssa.Function:
  28. fn, ok := v.Object().(*types.Func)
  29. if !ok {
  30. return ""
  31. }
  32. return lint.FuncName(fn)
  33. case *ssa.Builtin:
  34. return v.Name()
  35. }
  36. return ""
  37. }
  38. func IsCallTo(call *ssa.CallCommon, name string) bool { return CallName(call) == name }
  39. func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name }
  40. func FilterDebug(instr []ssa.Instruction) []ssa.Instruction {
  41. var out []ssa.Instruction
  42. for _, ins := range instr {
  43. if _, ok := ins.(*ssa.DebugRef); !ok {
  44. out = append(out, ins)
  45. }
  46. }
  47. return out
  48. }
  49. func IsExample(fn *ssa.Function) bool {
  50. if !strings.HasPrefix(fn.Name(), "Example") {
  51. return false
  52. }
  53. f := fn.Prog.Fset.File(fn.Pos())
  54. if f == nil {
  55. return false
  56. }
  57. return strings.HasSuffix(f.Name(), "_test.go")
  58. }
  59. func IsPointerLike(T types.Type) bool {
  60. switch T := T.Underlying().(type) {
  61. case *types.Interface, *types.Chan, *types.Map, *types.Signature, *types.Pointer:
  62. return true
  63. case *types.Basic:
  64. return T.Kind() == types.UnsafePointer
  65. }
  66. return false
  67. }
  68. func IsIdent(expr ast.Expr, ident string) bool {
  69. id, ok := expr.(*ast.Ident)
  70. return ok && id.Name == ident
  71. }
  72. // isBlank returns whether id is the blank identifier "_".
  73. // If id == nil, the answer is false.
  74. func IsBlank(id ast.Expr) bool {
  75. ident, _ := id.(*ast.Ident)
  76. return ident != nil && ident.Name == "_"
  77. }
  78. func IsIntLiteral(expr ast.Expr, literal string) bool {
  79. lit, ok := expr.(*ast.BasicLit)
  80. return ok && lit.Kind == token.INT && lit.Value == literal
  81. }
  82. // Deprecated: use IsIntLiteral instead
  83. func IsZero(expr ast.Expr) bool {
  84. return IsIntLiteral(expr, "0")
  85. }
  86. func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
  87. return IsType(pass.TypesInfo.TypeOf(expr), name)
  88. }
  89. func IsInTest(pass *analysis.Pass, node lint.Positioner) bool {
  90. // FIXME(dh): this doesn't work for global variables with
  91. // initializers
  92. f := pass.Fset.File(node.Pos())
  93. return f != nil && strings.HasSuffix(f.Name(), "_test.go")
  94. }
  95. func IsInMain(pass *analysis.Pass, node lint.Positioner) bool {
  96. if node, ok := node.(packager); ok {
  97. return node.Package().Pkg.Name() == "main"
  98. }
  99. return pass.Pkg.Name() == "main"
  100. }
  101. func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
  102. info := pass.TypesInfo
  103. sel := info.Selections[expr]
  104. if sel == nil {
  105. if x, ok := expr.X.(*ast.Ident); ok {
  106. pkg, ok := info.ObjectOf(x).(*types.PkgName)
  107. if !ok {
  108. // This shouldn't happen
  109. return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
  110. }
  111. return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
  112. }
  113. panic(fmt.Sprintf("unsupported selector: %v", expr))
  114. }
  115. return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
  116. }
  117. func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
  118. return pass.TypesInfo.Types[expr].IsNil()
  119. }
  120. func BoolConst(pass *analysis.Pass, expr ast.Expr) bool {
  121. val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
  122. return constant.BoolVal(val)
  123. }
  124. func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {
  125. // We explicitly don't support typed bools because more often than
  126. // not, custom bool types are used as binary enums and the
  127. // explicit comparison is desired.
  128. ident, ok := expr.(*ast.Ident)
  129. if !ok {
  130. return false
  131. }
  132. obj := pass.TypesInfo.ObjectOf(ident)
  133. c, ok := obj.(*types.Const)
  134. if !ok {
  135. return false
  136. }
  137. basic, ok := c.Type().(*types.Basic)
  138. if !ok {
  139. return false
  140. }
  141. if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
  142. return false
  143. }
  144. return true
  145. }
  146. func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
  147. tv := pass.TypesInfo.Types[expr]
  148. if tv.Value == nil {
  149. return 0, false
  150. }
  151. if tv.Value.Kind() != constant.Int {
  152. return 0, false
  153. }
  154. return constant.Int64Val(tv.Value)
  155. }
  156. func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
  157. val := pass.TypesInfo.Types[expr].Value
  158. if val == nil {
  159. return "", false
  160. }
  161. if val.Kind() != constant.String {
  162. return "", false
  163. }
  164. return constant.StringVal(val), true
  165. }
  166. // Dereference returns a pointer's element type; otherwise it returns
  167. // T.
  168. func Dereference(T types.Type) types.Type {
  169. if p, ok := T.Underlying().(*types.Pointer); ok {
  170. return p.Elem()
  171. }
  172. return T
  173. }
  174. // DereferenceR returns a pointer's element type; otherwise it returns
  175. // T. If the element type is itself a pointer, DereferenceR will be
  176. // applied recursively.
  177. func DereferenceR(T types.Type) types.Type {
  178. if p, ok := T.Underlying().(*types.Pointer); ok {
  179. return DereferenceR(p.Elem())
  180. }
  181. return T
  182. }
  183. func IsGoVersion(pass *analysis.Pass, minor int) bool {
  184. version := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter).Get().(int)
  185. return version >= minor
  186. }
  187. func CallNameAST(pass *analysis.Pass, call *ast.CallExpr) string {
  188. switch fun := call.Fun.(type) {
  189. case *ast.SelectorExpr:
  190. fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
  191. if !ok {
  192. return ""
  193. }
  194. return lint.FuncName(fn)
  195. case *ast.Ident:
  196. obj := pass.TypesInfo.ObjectOf(fun)
  197. switch obj := obj.(type) {
  198. case *types.Func:
  199. return lint.FuncName(obj)
  200. case *types.Builtin:
  201. return obj.Name()
  202. default:
  203. return ""
  204. }
  205. default:
  206. return ""
  207. }
  208. }
  209. func IsCallToAST(pass *analysis.Pass, node ast.Node, name string) bool {
  210. call, ok := node.(*ast.CallExpr)
  211. if !ok {
  212. return false
  213. }
  214. return CallNameAST(pass, call) == name
  215. }
  216. func IsCallToAnyAST(pass *analysis.Pass, node ast.Node, names ...string) bool {
  217. for _, name := range names {
  218. if IsCallToAST(pass, node, name) {
  219. return true
  220. }
  221. }
  222. return false
  223. }
  224. func Render(pass *analysis.Pass, x interface{}) string {
  225. var buf bytes.Buffer
  226. if err := printer.Fprint(&buf, pass.Fset, x); err != nil {
  227. panic(err)
  228. }
  229. return buf.String()
  230. }
  231. func RenderArgs(pass *analysis.Pass, args []ast.Expr) string {
  232. var ss []string
  233. for _, arg := range args {
  234. ss = append(ss, Render(pass, arg))
  235. }
  236. return strings.Join(ss, ", ")
  237. }
  238. func Preamble(f *ast.File) string {
  239. cutoff := f.Package
  240. if f.Doc != nil {
  241. cutoff = f.Doc.Pos()
  242. }
  243. var out []string
  244. for _, cmt := range f.Comments {
  245. if cmt.Pos() >= cutoff {
  246. break
  247. }
  248. out = append(out, cmt.Text())
  249. }
  250. return strings.Join(out, "\n")
  251. }
  252. func Inspect(node ast.Node, fn func(node ast.Node) bool) {
  253. if node == nil {
  254. return
  255. }
  256. ast.Inspect(node, fn)
  257. }
  258. func GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {
  259. if len(specs) == 0 {
  260. return nil
  261. }
  262. groups := make([][]ast.Spec, 1)
  263. groups[0] = append(groups[0], specs[0])
  264. for _, spec := range specs[1:] {
  265. g := groups[len(groups)-1]
  266. if fset.PositionFor(spec.Pos(), false).Line-1 !=
  267. fset.PositionFor(g[len(g)-1].End(), false).Line {
  268. groups = append(groups, nil)
  269. }
  270. groups[len(groups)-1] = append(groups[len(groups)-1], spec)
  271. }
  272. return groups
  273. }
  274. func IsObject(obj types.Object, name string) bool {
  275. var path string
  276. if pkg := obj.Pkg(); pkg != nil {
  277. path = pkg.Path() + "."
  278. }
  279. return path+obj.Name() == name
  280. }
  281. type Field struct {
  282. Var *types.Var
  283. Tag string
  284. Path []int
  285. }
  286. // FlattenFields recursively flattens T and embedded structs,
  287. // returning a list of fields. If multiple fields with the same name
  288. // exist, all will be returned.
  289. func FlattenFields(T *types.Struct) []Field {
  290. return flattenFields(T, nil, nil)
  291. }
  292. func flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Field {
  293. if seen == nil {
  294. seen = map[types.Type]bool{}
  295. }
  296. if seen[T] {
  297. return nil
  298. }
  299. seen[T] = true
  300. var out []Field
  301. for i := 0; i < T.NumFields(); i++ {
  302. field := T.Field(i)
  303. tag := T.Tag(i)
  304. np := append(path[:len(path):len(path)], i)
  305. if field.Anonymous() {
  306. if s, ok := Dereference(field.Type()).Underlying().(*types.Struct); ok {
  307. out = append(out, flattenFields(s, np, seen)...)
  308. }
  309. } else {
  310. out = append(out, Field{field, tag, np})
  311. }
  312. }
  313. return out
  314. }
  315. func File(pass *analysis.Pass, node lint.Positioner) *ast.File {
  316. pass.Fset.PositionFor(node.Pos(), true)
  317. m := pass.ResultOf[facts.TokenFile].(map[*token.File]*ast.File)
  318. return m[pass.Fset.File(node.Pos())]
  319. }
  320. // IsGenerated reports whether pos is in a generated file, It ignores
  321. // //line directives.
  322. func IsGenerated(pass *analysis.Pass, pos token.Pos) bool {
  323. _, ok := Generator(pass, pos)
  324. return ok
  325. }
  326. // Generator returns the generator that generated the file containing
  327. // pos. It ignores //line directives.
  328. func Generator(pass *analysis.Pass, pos token.Pos) (facts.Generator, bool) {
  329. file := pass.Fset.PositionFor(pos, false).Filename
  330. m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
  331. g, ok := m[file]
  332. return g, ok
  333. }
  334. func ReportfFG(pass *analysis.Pass, pos token.Pos, f string, args ...interface{}) {
  335. file := lint.DisplayPosition(pass.Fset, pos).Filename
  336. m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
  337. if _, ok := m[file]; ok {
  338. return
  339. }
  340. pass.Reportf(pos, f, args...)
  341. }
  342. func ReportNodef(pass *analysis.Pass, node ast.Node, format string, args ...interface{}) {
  343. msg := fmt.Sprintf(format, args...)
  344. pass.Report(analysis.Diagnostic{Pos: node.Pos(), End: node.End(), Message: msg})
  345. }
  346. func ReportNodefFG(pass *analysis.Pass, node ast.Node, format string, args ...interface{}) {
  347. file := lint.DisplayPosition(pass.Fset, node.Pos()).Filename
  348. m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
  349. if _, ok := m[file]; ok {
  350. return
  351. }
  352. ReportNodef(pass, node, format, args...)
  353. }