lint.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package sharedcheck
  2. import (
  3. "fmt"
  4. "go/ast"
  5. "go/token"
  6. "go/types"
  7. "honnef.co/go/tools/analysis/code"
  8. "honnef.co/go/tools/analysis/edit"
  9. "honnef.co/go/tools/analysis/facts"
  10. "honnef.co/go/tools/analysis/report"
  11. "honnef.co/go/tools/go/ast/astutil"
  12. "honnef.co/go/tools/go/ir"
  13. "honnef.co/go/tools/go/ir/irutil"
  14. "honnef.co/go/tools/go/types/typeutil"
  15. "honnef.co/go/tools/internal/passes/buildir"
  16. "golang.org/x/tools/go/analysis"
  17. "golang.org/x/tools/go/analysis/passes/inspect"
  18. )
  19. func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
  20. for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
  21. cb := func(node ast.Node) bool {
  22. rng, ok := node.(*ast.RangeStmt)
  23. if !ok || !astutil.IsBlank(rng.Key) {
  24. return true
  25. }
  26. v, _ := fn.ValueForExpr(rng.X)
  27. // Check that we're converting from string to []rune
  28. val, _ := v.(*ir.Convert)
  29. if val == nil {
  30. return true
  31. }
  32. Tsrc, ok := typeutil.CoreType(val.X.Type()).(*types.Basic)
  33. if !ok || Tsrc.Kind() != types.String {
  34. return true
  35. }
  36. Tdst, ok := typeutil.CoreType(val.Type()).(*types.Slice)
  37. if !ok {
  38. return true
  39. }
  40. TdstElem, ok := Tdst.Elem().(*types.Basic)
  41. if !ok || TdstElem.Kind() != types.Int32 {
  42. return true
  43. }
  44. // Check that the result of the conversion is only used to
  45. // range over
  46. refs := val.Referrers()
  47. if refs == nil {
  48. return true
  49. }
  50. // Expect two refs: one for obtaining the length of the slice,
  51. // one for accessing the elements
  52. if len(irutil.FilterDebug(*refs)) != 2 {
  53. // TODO(dh): right now, we check that only one place
  54. // refers to our slice. This will miss cases such as
  55. // ranging over the slice twice. Ideally, we'd ensure that
  56. // the slice is only used for ranging over (without
  57. // accessing the key), but that is harder to do because in
  58. // IR form, ranging over a slice looks like an ordinary
  59. // loop with index increments and slice accesses. We'd
  60. // have to look at the associated AST node to check that
  61. // it's a range statement.
  62. return true
  63. }
  64. pass.Reportf(rng.Pos(), "should range over string, not []rune(string)")
  65. return true
  66. }
  67. if source := fn.Source(); source != nil {
  68. ast.Inspect(source, cb)
  69. }
  70. }
  71. return nil, nil
  72. }
  73. // RedundantTypeInDeclarationChecker returns a checker that flags variable declarations with redundantly specified types.
  74. // That is, it flags 'var v T = e' where e's type is identical to T and 'var v = e' (or 'v := e') would have the same effect.
  75. //
  76. // It does not flag variables under the following conditions, to reduce the number of false positives:
  77. // - global variables – these often specify types to aid godoc
  78. // - files that use cgo – cgo code generation and pointer checking emits redundant types
  79. //
  80. // It does not flag variables under the following conditions, unless flagHelpfulTypes is true, to reduce the number of noisy positives:
  81. // - packages that import syscall or unsafe – these sometimes use this form of assignment to make sure types are as expected
  82. // - variables named the blank identifier – a pattern used to confirm the types of variables
  83. // - untyped expressions on the rhs – the explicitness might aid readability
  84. func RedundantTypeInDeclarationChecker(verb string, flagHelpfulTypes bool) *analysis.Analyzer {
  85. fn := func(pass *analysis.Pass) (interface{}, error) {
  86. eval := func(expr ast.Expr) (types.TypeAndValue, error) {
  87. info := &types.Info{
  88. Types: map[ast.Expr]types.TypeAndValue{},
  89. }
  90. err := types.CheckExpr(pass.Fset, pass.Pkg, expr.Pos(), expr, info)
  91. return info.Types[expr], err
  92. }
  93. if !flagHelpfulTypes {
  94. // Don't look at code in low-level packages
  95. for _, imp := range pass.Pkg.Imports() {
  96. if imp.Path() == "syscall" || imp.Path() == "unsafe" {
  97. return nil, nil
  98. }
  99. }
  100. }
  101. fn := func(node ast.Node) {
  102. decl := node.(*ast.GenDecl)
  103. if decl.Tok != token.VAR {
  104. return
  105. }
  106. gen, _ := code.Generator(pass, decl.Pos())
  107. if gen == facts.Cgo {
  108. // TODO(dh): remove this exception once we can use UsesCgo
  109. return
  110. }
  111. // Delay looking up parent AST nodes until we have to
  112. checkedDecl := false
  113. specLoop:
  114. for _, spec := range decl.Specs {
  115. spec := spec.(*ast.ValueSpec)
  116. if spec.Type == nil {
  117. continue
  118. }
  119. if len(spec.Names) != len(spec.Values) {
  120. continue
  121. }
  122. Tlhs := pass.TypesInfo.TypeOf(spec.Type)
  123. for i, v := range spec.Values {
  124. if !flagHelpfulTypes && spec.Names[i].Name == "_" {
  125. continue specLoop
  126. }
  127. Trhs := pass.TypesInfo.TypeOf(v)
  128. if !types.Identical(Tlhs, Trhs) {
  129. continue specLoop
  130. }
  131. // Some expressions are untyped and get converted to the lhs type implicitly.
  132. // This applies to untyped constants, shift operations with an untyped lhs, and possibly others.
  133. //
  134. // Check if the type is truly redundant, i.e. if the type on the lhs doesn't match the default type of the untyped constant.
  135. tv, err := eval(v)
  136. if err != nil {
  137. panic(err)
  138. }
  139. if b, ok := tv.Type.(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
  140. if Tlhs != types.Default(b) {
  141. // The rhs is untyped and its default type differs from the explicit type on the lhs
  142. continue specLoop
  143. }
  144. switch v := v.(type) {
  145. case *ast.Ident:
  146. // Only flag named constant rhs if it's a predeclared identifier.
  147. // Don't flag other named constants, as the explicit type may aid readability.
  148. if pass.TypesInfo.ObjectOf(v).Pkg() != nil && !flagHelpfulTypes {
  149. continue specLoop
  150. }
  151. case *ast.BasicLit:
  152. // Do flag basic literals
  153. default:
  154. // Don't flag untyped rhs expressions unless flagHelpfulTypes is set
  155. if !flagHelpfulTypes {
  156. continue specLoop
  157. }
  158. }
  159. }
  160. }
  161. if !checkedDecl {
  162. // Don't flag global variables. These often have explicit types for godoc's sake.
  163. path, _ := astutil.PathEnclosingInterval(code.File(pass, decl), decl.Pos(), decl.Pos())
  164. pathLoop:
  165. for _, el := range path {
  166. switch el.(type) {
  167. case *ast.FuncDecl, *ast.FuncLit:
  168. checkedDecl = true
  169. break pathLoop
  170. }
  171. }
  172. if !checkedDecl {
  173. // decl is not inside a function
  174. break specLoop
  175. }
  176. }
  177. report.Report(pass, spec.Type, fmt.Sprintf("%s omit type %s from declaration; it will be inferred from the right-hand side", verb, report.Render(pass, spec.Type)), report.FilterGenerated(),
  178. report.Fixes(edit.Fix("Remove redundant type", edit.Delete(spec.Type))))
  179. }
  180. }
  181. code.Preorder(pass, fn, (*ast.GenDecl)(nil))
  182. return nil, nil
  183. }
  184. return &analysis.Analyzer{
  185. Run: fn,
  186. Requires: []*analysis.Analyzer{facts.Generated, inspect.Analyzer, facts.TokenFile, facts.Generated},
  187. }
  188. }