github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/var-declarations.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "go/types" 8 9 "github.com/mgechev/revive/lint" 10 ) 11 12 // VarDeclarationsRule lints given else constructs. 13 type VarDeclarationsRule struct{} 14 15 // Apply applies the rule to given file. 16 func (r *VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 17 var failures []lint.Failure 18 19 fileAst := file.AST 20 walker := &lintVarDeclarations{ 21 file: file, 22 fileAst: fileAst, 23 onFailure: func(failure lint.Failure) { 24 failures = append(failures, failure) 25 }, 26 } 27 28 file.Pkg.TypeCheck() 29 ast.Walk(walker, fileAst) 30 31 return failures 32 } 33 34 // Name returns the rule name. 35 func (r *VarDeclarationsRule) Name() string { 36 return "var-declaration" 37 } 38 39 type lintVarDeclarations struct { 40 fileAst *ast.File 41 file *lint.File 42 lastGen *ast.GenDecl 43 onFailure func(lint.Failure) 44 } 45 46 func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor { 47 switch v := node.(type) { 48 case *ast.GenDecl: 49 if v.Tok != token.CONST && v.Tok != token.VAR { 50 return nil 51 } 52 w.lastGen = v 53 return w 54 case *ast.ValueSpec: 55 if w.lastGen.Tok == token.CONST { 56 return nil 57 } 58 if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { 59 return nil 60 } 61 rhs := v.Values[0] 62 // An underscore var appears in a common idiom for compile-time interface satisfaction, 63 // as in "var _ Interface = (*Concrete)(nil)". 64 if isIdent(v.Names[0], "_") { 65 return nil 66 } 67 // If the RHS is a zero value, suggest dropping it. 68 zero := false 69 if lit, ok := rhs.(*ast.BasicLit); ok { 70 zero = zeroLiteral[lit.Value] 71 } else if isIdent(rhs, "nil") { 72 zero = true 73 } 74 if zero { 75 w.onFailure(lint.Failure{ 76 Confidence: 0.9, 77 Node: rhs, 78 Category: "zero-value", 79 Failure: fmt.Sprintf("should drop = %s from declaration of var %s; it is the zero value", w.file.Render(rhs), v.Names[0]), 80 }) 81 return nil 82 } 83 lhsTyp := w.file.Pkg.TypeOf(v.Type) 84 rhsTyp := w.file.Pkg.TypeOf(rhs) 85 86 if !validType(lhsTyp) || !validType(rhsTyp) { 87 // Type checking failed (often due to missing imports). 88 return nil 89 } 90 91 if !types.Identical(lhsTyp, rhsTyp) { 92 // Assignment to a different type is not redundant. 93 return nil 94 } 95 96 // The next three conditions are for suppressing the warning in situations 97 // where we were unable to typecheck. 98 99 // If the LHS type is an interface, don't warn, since it is probably a 100 // concrete type on the RHS. Note that our feeble lexical check here 101 // will only pick up interface{} and other literal interface types; 102 // that covers most of the cases we care to exclude right now. 103 if _, ok := v.Type.(*ast.InterfaceType); ok { 104 return nil 105 } 106 // If the RHS is an untyped const, only warn if the LHS type is its default type. 107 if defType, ok := w.file.IsUntypedConst(rhs); ok && !isIdent(v.Type, defType) { 108 return nil 109 } 110 111 w.onFailure(lint.Failure{ 112 Category: "type-inference", 113 Confidence: 0.8, 114 Node: v.Type, 115 Failure: fmt.Sprintf("should omit type %s from declaration of var %s; it will be inferred from the right-hand side", w.file.Render(v.Type), v.Names[0]), 116 }) 117 return nil 118 } 119 return w 120 }