1
0
mirror of https://github.com/mgechev/revive.git synced 2024-11-24 08:32:22 +02:00

Var declarations

This commit is contained in:
mgechev 2017-11-26 20:03:12 -08:00
parent e20e8e338a
commit ce81530c03
2 changed files with 120 additions and 0 deletions

View File

@ -3,6 +3,7 @@ package defaultrule
import (
"fmt"
"go/ast"
"go/types"
"regexp"
"strings"
@ -101,3 +102,29 @@ var commonInitialisms = map[string]bool{
}
var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
func isIdent(expr ast.Expr, ident string) bool {
id, ok := expr.(*ast.Ident)
return ok && id.Name == ident
}
var zeroLiteral = map[string]bool{
"false": true, // bool
// runes
`'\x00'`: true,
`'\000'`: true,
// strings
`""`: true,
"``": true,
// numerics
"0": true,
"0.": true,
"0.0": true,
"0i": true,
}
func validType(T types.Type) bool {
return T != nil &&
T != types.Typ[types.Invalid] &&
!strings.Contains(T.String(), "invalid type") // good but not foolproof
}

View File

@ -0,0 +1,93 @@
package defaultrule
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"go/token"
"github.com/mgechev/revive/file"
"github.com/mgechev/revive/rule"
)
// VarDeclarationsRule lints given else constructs.
type VarDeclarationsRule struct{}
// Apply applies the rule to given file.
func (r *VarDeclarationsRule) Apply(file *file.File, arguments rule.Arguments) []rule.Failure {
var failures []rule.Failure
fileAst := file.GetAST()
walker := &lintVarDeclarations{
file: file,
fileAst: fileAst,
onFailure: func(failure rule.Failure) {
failures = append(failures, failure)
},
}
ast.Walk(walker, fileAst)
return failures
}
// Name returns the rule name.
func (r *VarDeclarationsRule) Name() string {
return "blank-imports"
}
type lintVarDeclarations struct {
fileAst *ast.File
file *file.File
lastGen *ast.GenDecl
onFailure func(rule.Failure)
}
func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor {
switch v := node.(type) {
case *ast.GenDecl:
if v.Tok != token.CONST && v.Tok != token.VAR {
return nil
}
w.lastGen = v
return w
case *ast.ValueSpec:
if w.lastGen.Tok == token.CONST {
return nil
}
if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 {
return nil
}
rhs := v.Values[0]
// An underscore var appears in a common idiom for compile-time interface satisfaction,
// as in "var _ Interface = (*Concrete)(nil)".
if isIdent(v.Names[0], "_") {
return nil
}
// If the RHS is a zero value, suggest dropping it.
zero := false
if lit, ok := rhs.(*ast.BasicLit); ok {
zero = zeroLiteral[lit.Value]
} else if isIdent(rhs, "nil") {
zero = true
}
if zero {
w.onFailure(rule.Failure{
Confidence: 0.9,
Node: rhs,
Failure: fmt.Sprintf("should drop = %s from declaration of var %s; it is the zero value", render(rhs), v.Names[0]),
})
return nil
}
}
return w
}
func render(x interface{}) string {
var buf bytes.Buffer
if err := printer.Fprint(&buf, token.NewFileSet(), x); err != nil {
panic(err)
}
return buf.String()
}