1
0
mirror of https://github.com/mgechev/revive.git synced 2025-04-27 12:12:10 +02:00

code cleanup (#1054)

This commit is contained in:
chavacava 2024-10-01 12:14:02 +02:00 committed by GitHub
parent 798ce21849
commit ca2a32b087
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 326 additions and 263 deletions

View File

@ -160,12 +160,15 @@ func (w *lintAddConstantRule) isIgnoredFunc(fName string) bool {
} }
func (w *lintAddConstantRule) checkStrLit(n *ast.BasicLit) { func (w *lintAddConstantRule) checkStrLit(n *ast.BasicLit) {
const IgnoreMarker = -1
if w.allowList[kindSTRING][n.Value] { if w.allowList[kindSTRING][n.Value] {
return return
} }
count := w.strLits[n.Value] count := w.strLits[n.Value]
if count >= 0 { mustCheck := count > IgnoreMarker
if mustCheck {
w.strLits[n.Value] = count + 1 w.strLits[n.Value] = count + 1
if w.strLits[n.Value] > w.strLitLimit { if w.strLits[n.Value] > w.strLitLimit {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{

View File

@ -10,7 +10,7 @@ import (
// ArgumentsLimitRule lints given else constructs. // ArgumentsLimitRule lints given else constructs.
type ArgumentsLimitRule struct { type ArgumentsLimitRule struct {
total int max int
sync.Mutex sync.Mutex
} }
@ -19,18 +19,20 @@ const defaultArgumentsLimit = 8
func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) { func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.total == 0 { if r.max != 0 {
if len(arguments) < 1 {
r.total = defaultArgumentsLimit
return return
} }
total, ok := arguments[0].(int64) // Alt. non panicking version if len(arguments) < 1 {
r.max = defaultArgumentsLimit
return
}
maxArguments, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(`invalid value passed as argument number to the "argument-limit" rule`) panic(`invalid value passed as argument number to the "argument-limit" rule`)
} }
r.total = int(total) r.max = int(maxArguments)
}
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
@ -43,7 +45,7 @@ func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []
} }
walker := lintArgsNum{ walker := lintArgsNum{
total: r.total, max: r.max,
onFailure: onFailure, onFailure: onFailure,
} }
@ -58,27 +60,30 @@ func (*ArgumentsLimitRule) Name() string {
} }
type lintArgsNum struct { type lintArgsNum struct {
total int max int
onFailure func(lint.Failure) onFailure func(lint.Failure)
} }
func (w lintArgsNum) Visit(n ast.Node) ast.Visitor { func (w lintArgsNum) Visit(n ast.Node) ast.Visitor {
node, ok := n.(*ast.FuncDecl) node, ok := n.(*ast.FuncDecl)
if ok { if !ok {
return w
}
num := 0 num := 0
for _, l := range node.Type.Params.List { for _, l := range node.Type.Params.List {
for range l.Names { for range l.Names {
num++ num++
} }
} }
if num > w.total {
if num > w.max {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Confidence: 1, Confidence: 1,
Failure: fmt.Sprintf("maximum number of arguments per function exceeded; max %d but got %d", w.total, num), Failure: fmt.Sprintf("maximum number of arguments per function exceeded; max %d but got %d", w.max, num),
Node: node.Type, Node: node.Type,
}) })
return w
} }
}
return w return nil // skip visiting the body of the function
} }

View File

@ -24,7 +24,6 @@ func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failu
const ( const (
message = "a blank import should be only in a main or test package, or have a comment justifying it" message = "a blank import should be only in a main or test package, or have a comment justifying it"
category = "imports" category = "imports"
embedImportPath = `"embed"` embedImportPath = `"embed"`
) )
@ -39,7 +38,8 @@ func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failu
continue // Ignore non-blank imports. continue // Ignore non-blank imports.
} }
if i > 0 { isNotFirstElement := i > 0
if isNotFirstElement {
prev := file.AST.Imports[i-1] prev := file.AST.Imports[i-1]
prevPos := file.ToPosition(prev.Pos()) prevPos := file.ToPosition(prev.Pos())

View File

@ -45,7 +45,6 @@ func (w *lintBoolLiteral) Visit(node ast.Node) ast.Visitor {
lexeme, ok := isExprABooleanLit(n.X) lexeme, ok := isExprABooleanLit(n.X)
if !ok { if !ok {
lexeme, ok = isExprABooleanLit(n.Y) lexeme, ok = isExprABooleanLit(n.Y)
if !ok { if !ok {
return w return w
} }

View File

@ -21,7 +21,9 @@ const defaultMaxCognitiveComplexity = 7
func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) { func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.maxComplexity == 0 { if r.maxComplexity != 0 {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
r.maxComplexity = defaultMaxCognitiveComplexity r.maxComplexity = defaultMaxCognitiveComplexity
@ -32,9 +34,9 @@ func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) {
if !ok { if !ok {
panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0]))
} }
r.maxComplexity = int(complexity) r.maxComplexity = int(complexity)
} }
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {

View File

@ -18,8 +18,10 @@ type CommentSpacingsRule struct {
func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { func (r *CommentSpacingsRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.allowList != nil {
return // already configured
}
if r.allowList == nil {
r.allowList = []string{} r.allowList = []string{}
for _, arg := range arguments { for _, arg := range arguments {
allow, ok := arg.(string) // Alt. non panicking version allow, ok := arg.(string) // Alt. non panicking version
@ -29,7 +31,6 @@ func (r *CommentSpacingsRule) configure(arguments lint.Arguments) {
r.allowList = append(r.allowList, `//`+allow) r.allowList = append(r.allowList, `//`+allow)
} }
} }
}
// Apply the rule. // Apply the rule.
func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {

View File

@ -41,8 +41,9 @@ func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor {
return w return w
} }
if gofmt(n.X) != gofmt(n.Y) { // check if subexpressions are the same subExpressionsAreNotEqual := gofmt(n.X) != gofmt(n.Y)
return w if subExpressionsAreNotEqual {
return w // nothing to say
} }
// Handles cases like: a <= a, a == a, a >= a // Handles cases like: a <= a, a == a, a >= a

View File

@ -22,7 +22,10 @@ const defaultMaxCyclomaticComplexity = 10
func (r *CyclomaticRule) configure(arguments lint.Arguments) { func (r *CyclomaticRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.maxComplexity == 0 { if r.maxComplexity != 0 {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
r.maxComplexity = defaultMaxCyclomaticComplexity r.maxComplexity = defaultMaxCyclomaticComplexity
return return
@ -34,7 +37,6 @@ func (r *CyclomaticRule) configure(arguments lint.Arguments) {
} }
r.maxComplexity = int(complexity) r.maxComplexity = int(complexity)
} }
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
@ -70,7 +72,11 @@ type lintCyclomatic struct {
func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor { func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor {
f := w.file f := w.file
for _, decl := range f.AST.Decls { for _, decl := range f.AST.Decls {
if fn, ok := decl.(*ast.FuncDecl); ok { fn, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
c := complexity(fn) c := complexity(fn)
if c > w.complexity { if c > w.complexity {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
@ -82,19 +88,19 @@ func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor {
}) })
} }
} }
}
return nil return nil
} }
// funcName returns the name representation of a function or method: // funcName returns the name representation of a function or method:
// "(Type).Name" for methods or simply "Name" for functions. // "(Type).Name" for methods or simply "Name" for functions.
func funcName(fn *ast.FuncDecl) string { func funcName(fn *ast.FuncDecl) string {
if fn.Recv != nil { declarationHasReceiver := fn.Recv != nil && fn.Recv.NumFields() > 0
if fn.Recv.NumFields() > 0 { if declarationHasReceiver {
typ := fn.Recv.List[0].Type typ := fn.Recv.List[0].Type
return fmt.Sprintf("(%s).%s", recvString(typ), fn.Name) return fmt.Sprintf("(%s).%s", recvString(typ), fn.Name)
} }
}
return fn.Name.Name return fn.Name.Name
} }

View File

@ -73,9 +73,10 @@ func (w lintDeepExit) Visit(node ast.Node) ast.Visitor {
return w return w
} }
fn := fc.Sel.Name
pkg := id.Name pkg := id.Name
if w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn] { // it's a call to an exit function fn := fc.Sel.Name
isACallToExitFunction := w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn]
if isACallToExitFunction {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Confidence: 1, Confidence: 1,
Node: ce, Node: ce,

View File

@ -16,10 +16,12 @@ type DeferRule struct {
func (r *DeferRule) configure(arguments lint.Arguments) { func (r *DeferRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
if r.allow == nil { defer r.Unlock()
r.allow = r.allowFromArgs(arguments) if r.allow != nil {
return // already configured
} }
r.Unlock()
r.allow = r.allowFromArgs(arguments)
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.

View File

@ -81,12 +81,13 @@ type lintImports struct {
} }
func (w lintImports) Visit(_ ast.Node) ast.Visitor { func (w lintImports) Visit(_ ast.Node) ast.Visitor {
for _, is := range w.fileAst.Imports { for _, importSpec := range w.fileAst.Imports {
if is.Name != nil && is.Name.Name == "." && !w.allowPackages.isAllowedPackage(is.Path.Value) { isDotImport := importSpec.Name != nil && importSpec.Name.Name == "."
if isDotImport && !w.allowPackages.isAllowedPackage(importSpec.Path.Value) {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Confidence: 1, Confidence: 1,
Failure: "should not use dot imports", Failure: "should not use dot imports",
Node: is, Node: importSpec,
Category: "imports", Category: "imports",
}) })
} }

View File

@ -94,8 +94,8 @@ func (r *EnforceMapStyleRule) Apply(file *lint.File, arguments lint.Arguments) [
return true return true
} }
if len(v.Elts) > 0 { isEmptyMap := len(v.Elts) > 0
// not an empty map if isEmptyMap {
return true return true
} }

View File

@ -101,8 +101,8 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments)
return true return true
} }
if len(v.Elts) > 0 { isNotEmptySlice := len(v.Elts) > 0
// not an empty slice if isNotEmptySlice {
return true return true
} }
@ -132,8 +132,8 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments)
return true return true
} }
if len(v.Args) < 2 { isInvalidMakeDeclaration := len(v.Args) < 2
// skip invalid make declarations if isInvalidMakeDeclaration {
return true return true
} }
@ -148,8 +148,8 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments)
return true return true
} }
if arg.Value != "0" { isSliceSizeNotZero := arg.Value != "0"
// skip slice with non-zero size if isSliceSizeNotZero {
return true return true
} }
@ -160,8 +160,8 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments)
return true return true
} }
if arg.Value != "0" { isNonZeroCapacitySlice := arg.Value != "0"
// skip non-zero capacity slice if isNonZeroCapacitySlice {
return true return true
} }
} }

View File

@ -22,7 +22,10 @@ var (
func (r *FileHeaderRule) configure(arguments lint.Arguments) { func (r *FileHeaderRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.header == "" { if r.header != "" {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
return return
} }
@ -33,7 +36,6 @@ func (r *FileHeaderRule) configure(arguments lint.Arguments) {
panic(fmt.Sprintf("invalid argument for \"file-header\" rule: argument should be a string, got %T", arguments[0])) panic(fmt.Sprintf("invalid argument for \"file-header\" rule: argument should be a string, got %T", arguments[0]))
} }
} }
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {

View File

@ -20,12 +20,14 @@ type FunctionLength struct {
func (r *FunctionLength) configure(arguments lint.Arguments) { func (r *FunctionLength) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if !r.configured { if r.configured {
return
}
r.configured = true
maxStmt, maxLines := r.parseArguments(arguments) maxStmt, maxLines := r.parseArguments(arguments)
r.maxStmt = int(maxStmt) r.maxStmt = int(maxStmt)
r.maxLines = int(maxLines) r.maxLines = int(maxLines)
r.configured = true
}
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.
@ -61,8 +63,9 @@ func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLin
return defaultFuncStmtsLimit, defaultFuncLinesLimit return defaultFuncStmtsLimit, defaultFuncLinesLimit
} }
if len(arguments) != 2 { const minArguments = 2
panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected 2 arguments but got %d`, len(arguments))) if len(arguments) != minArguments {
panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected %d arguments but got %d`, minArguments, len(arguments)))
} }
maxStmt, maxStmtOk := arguments[0].(int64) maxStmt, maxStmtOk := arguments[0].(int64)
@ -98,7 +101,8 @@ func (w lintFuncLength) Visit(n ast.Node) ast.Visitor {
} }
body := node.Body body := node.Body
if body == nil || len(node.Body.List) == 0 { emptyBody := body == nil || len(node.Body.List) == 0
if emptyBody {
return nil return nil
} }

View File

@ -19,11 +19,15 @@ const defaultResultsLimit = 3
func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) { func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.max == 0 { if r.max != 0 {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultResultsLimit r.max = defaultResultsLimit
return return
} }
maxResults, ok := arguments[0].(int64) // Alt. non panicking version maxResults, ok := arguments[0].(int64) // Alt. non panicking version
if !ok { if !ok {
panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0]))
@ -31,9 +35,9 @@ func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) {
if maxResults < 0 { if maxResults < 0 {
panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`)
} }
r.max = int(maxResults) r.max = int(maxResults)
} }
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
@ -67,7 +71,8 @@ func (w lintFunctionResultsNum) Visit(n ast.Node) ast.Visitor {
node, ok := n.(*ast.FuncDecl) node, ok := n.(*ast.FuncDecl)
if ok { if ok {
num := 0 num := 0
if node.Type.Results != nil { hasResults := node.Type.Results != nil
if hasResults {
num = node.Type.Results.NumFields() num = node.Type.Results.NumFields()
} }
if num > w.max { if num > w.max {
@ -76,8 +81,10 @@ func (w lintFunctionResultsNum) Visit(n ast.Node) ast.Visitor {
Failure: fmt.Sprintf("maximum number of return results per function exceeded; max %d but got %d", w.max, num), Failure: fmt.Sprintf("maximum number of return results per function exceeded; max %d but got %d", w.max, num),
Node: node.Type, Node: node.Type,
}) })
return w }
}
} return nil // skip visiting function's body
}
return w return w
} }

View File

@ -33,17 +33,27 @@ type lintReturnRule struct {
onFailure func(lint.Failure) onFailure func(lint.Failure)
} }
const getterPrefix = "GET"
var lenGetterPrefix = len(getterPrefix)
func isGetter(name string) bool { func isGetter(name string) bool {
if strings.HasPrefix(strings.ToUpper(name), "GET") { nameHasGetterPrefix := strings.HasPrefix(strings.ToUpper(name), getterPrefix)
if len(name) > 3 { if !nameHasGetterPrefix {
c := name[3] return false
return !(c >= 'a' && c <= 'z')
}
} }
isJustGet := len(name) == lenGetterPrefix
if isJustGet {
return false return false
} }
c := name[lenGetterPrefix]
lowerCaseAfterGetterPrefix := c >= 'a' && c <= 'z'
return !lowerCaseAfterGetterPrefix
}
func hasResults(rs *ast.FieldList) bool { func hasResults(rs *ast.FieldList) bool {
return rs != nil && len(rs.List) > 0 return rs != nil && len(rs.List) > 0
} }

View File

@ -39,9 +39,11 @@ func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor {
return w return w
} }
if n.Else == nil { noElseBranch := n.Else == nil
if noElseBranch {
return w return w
} }
branches := []*ast.BlockStmt{n.Body} branches := []*ast.BlockStmt{n.Body}
elseBranch, ok := n.Else.(*ast.BlockStmt) elseBranch, ok := n.Else.(*ast.BlockStmt)
@ -59,14 +61,15 @@ func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor {
func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool {
if len(branches) < 2 { if len(branches) < 2 {
return false return false // only one branch to compare thus we return
} }
ref := gofmt(branches[0]) referenceBranch := gofmt(branches[0])
refSize := len(branches[0].List) referenceBranchSize := len(branches[0].List)
for i := 1; i < len(branches); i++ { for i := 1; i < len(branches); i++ {
currentSize := len(branches[i].List) currentBranch := branches[i]
if currentSize != refSize || gofmt(branches[i]) != ref { currentBranchSize := len(currentBranch.List)
if currentBranchSize != referenceBranchSize || gofmt(currentBranch) != referenceBranch {
return false return false
} }
} }

View File

@ -23,7 +23,10 @@ const defaultLineLengthLimit = 80
func (r *LineLengthLimitRule) configure(arguments lint.Arguments) { func (r *LineLengthLimitRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.max == 0 { if r.max != 0 {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultLineLengthLimit r.max = defaultLineLengthLimit
return return
@ -35,7 +38,7 @@ func (r *LineLengthLimitRule) configure(arguments lint.Arguments) {
} }
r.max = int(maxLength) r.max = int(maxLength)
}
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.

View File

@ -110,7 +110,7 @@ func (r *MaxControlNestingRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if !(r.max < 1) { if !(r.max < 1) {
return // max already set return // max already configured
} }
if len(arguments) < 1 { if len(arguments) < 1 {

View File

@ -19,7 +19,10 @@ const defaultMaxPublicStructs = 5
func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) { func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.max < 1 { if r.max == 0 {
return // already configured
}
if len(arguments) < 1 { if len(arguments) < 1 {
r.max = defaultMaxPublicStructs r.max = defaultMaxPublicStructs
return return
@ -32,7 +35,7 @@ func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) {
panic(`invalid value passed as argument number to the "max-public-structs" rule`) panic(`invalid value passed as argument number to the "max-public-structs" rule`)
} }
r.max = maxStructs r.max = maxStructs
}
} }
// Apply applies the rule to given file. // Apply applies the rule to given file.

View File

@ -20,7 +20,12 @@ type StructTagRule struct {
func (r *StructTagRule) configure(arguments lint.Arguments) { func (r *StructTagRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if r.userDefined == nil && len(arguments) > 0 {
mustConfigure := r.userDefined == nil && len(arguments) > 0
if !mustConfigure {
return
}
checkNumberOfArguments(1, arguments, r.Name()) checkNumberOfArguments(1, arguments, r.Name())
r.userDefined = make(map[string][]string, len(arguments)) r.userDefined = make(map[string][]string, len(arguments))
for _, arg := range arguments { for _, arg := range arguments {
@ -39,7 +44,6 @@ func (r *StructTagRule) configure(arguments lint.Arguments) {
} }
} }
} }
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
@ -75,11 +79,13 @@ type lintStructTagRule struct {
func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) { switch n := node.(type) {
case *ast.StructType: case *ast.StructType:
if n.Fields == nil || n.Fields.NumFields() < 1 { isEmptyStruct := n.Fields == nil || n.Fields.NumFields() < 1
if isEmptyStruct {
return nil // skip empty structs return nil // skip empty structs
} }
w.usedTagNbr = map[int]bool{} // init
w.usedTagName = map[string]bool{} // init w.usedTagNbr = map[int]bool{}
w.usedTagName = map[string]bool{}
for _, f := range n.Fields.List { for _, f := range n.Fields.List {
if f.Tag != nil { if f.Tag != nil {
w.checkTaggedField(f) w.checkTaggedField(f)

View File

@ -50,26 +50,23 @@ func (l *lintTimeEqual) Visit(node ast.Node) ast.Visitor {
return l return l
} }
xtyp := l.file.Pkg.TypeOf(expr.X) typeOfX := l.file.Pkg.TypeOf(expr.X)
ytyp := l.file.Pkg.TypeOf(expr.Y) typeOfY := l.file.Pkg.TypeOf(expr.Y)
bothAreOfTimeType := isNamedType(typeOfX, "time", "Time") && isNamedType(typeOfY, "time", "Time")
if !isNamedType(xtyp, "time", "Time") || !isNamedType(ytyp, "time", "Time") { if !bothAreOfTimeType {
return l return l
} }
var failure string negateStr := ""
switch expr.Op { if token.NEQ == expr.Op {
case token.EQL: negateStr = "!"
failure = fmt.Sprintf("use %s.Equal(%s) instead of %q operator", gofmt(expr.X), gofmt(expr.Y), expr.Op)
case token.NEQ:
failure = fmt.Sprintf("use !%s.Equal(%s) instead of %q operator", gofmt(expr.X), gofmt(expr.Y), expr.Op)
} }
l.onFailure(lint.Failure{ l.onFailure(lint.Failure{
Category: "time", Category: "time",
Confidence: 1, Confidence: 1,
Node: node, Node: node,
Failure: failure, Failure: fmt.Sprintf("use %s%s.Equal(%s) instead of %q operator", negateStr, gofmt(expr.X), gofmt(expr.Y), expr.Op),
}) })
return l return l

View File

@ -90,6 +90,7 @@ func isNamedType(typ types.Type, importPath, name string) bool {
if !ok { if !ok {
return false return false
} }
tn := n.Obj()
return tn != nil && tn.Pkg() != nil && tn.Pkg().Path() == importPath && tn.Name() == name typeName := n.Obj()
return typeName != nil && typeName.Pkg() != nil && typeName.Pkg().Path() == importPath && typeName.Name() == name
} }

View File

@ -54,7 +54,7 @@ func (u *UncheckedTypeAssertionRule) Apply(file *lint.File, args lint.Arguments)
var failures []lint.Failure var failures []lint.Failure
walker := &lintUnchekedTypeAssertion{ walker := &lintUncheckedTypeAssertion{
onFailure: func(failure lint.Failure) { onFailure: func(failure lint.Failure) {
failures = append(failures, failure) failures = append(failures, failure)
}, },
@ -71,7 +71,7 @@ func (*UncheckedTypeAssertionRule) Name() string {
return "unchecked-type-assertion" return "unchecked-type-assertion"
} }
type lintUnchekedTypeAssertion struct { type lintUncheckedTypeAssertion struct {
onFailure func(lint.Failure) onFailure func(lint.Failure)
acceptIgnoredTypeAssertionResult bool acceptIgnoredTypeAssertionResult bool
} }
@ -89,14 +89,14 @@ func isTypeSwitch(e *ast.TypeAssertExpr) bool {
return e.Type == nil return e.Type == nil
} }
func (w *lintUnchekedTypeAssertion) requireNoTypeAssert(expr ast.Expr) { func (w *lintUncheckedTypeAssertion) requireNoTypeAssert(expr ast.Expr) {
e, ok := expr.(*ast.TypeAssertExpr) e, ok := expr.(*ast.TypeAssertExpr)
if ok && !isTypeSwitch(e) { if ok && !isTypeSwitch(e) {
w.addFailure(e, ruleUTAMessagePanic) w.addFailure(e, ruleUTAMessagePanic)
} }
} }
func (w *lintUnchekedTypeAssertion) handleIfStmt(n *ast.IfStmt) { func (w *lintUncheckedTypeAssertion) handleIfStmt(n *ast.IfStmt) {
ifCondition, ok := n.Cond.(*ast.BinaryExpr) ifCondition, ok := n.Cond.(*ast.BinaryExpr)
if ok { if ok {
w.requireNoTypeAssert(ifCondition.X) w.requireNoTypeAssert(ifCondition.X)
@ -104,7 +104,7 @@ func (w *lintUnchekedTypeAssertion) handleIfStmt(n *ast.IfStmt) {
} }
} }
func (w *lintUnchekedTypeAssertion) requireBinaryExpressionWithoutTypeAssertion(expr ast.Expr) { func (w *lintUncheckedTypeAssertion) requireBinaryExpressionWithoutTypeAssertion(expr ast.Expr) {
binaryExpr, ok := expr.(*ast.BinaryExpr) binaryExpr, ok := expr.(*ast.BinaryExpr)
if ok { if ok {
w.requireNoTypeAssert(binaryExpr.X) w.requireNoTypeAssert(binaryExpr.X)
@ -112,19 +112,19 @@ func (w *lintUnchekedTypeAssertion) requireBinaryExpressionWithoutTypeAssertion(
} }
} }
func (w *lintUnchekedTypeAssertion) handleCaseClause(n *ast.CaseClause) { func (w *lintUncheckedTypeAssertion) handleCaseClause(n *ast.CaseClause) {
for _, expr := range n.List { for _, expr := range n.List {
w.requireNoTypeAssert(expr) w.requireNoTypeAssert(expr)
w.requireBinaryExpressionWithoutTypeAssertion(expr) w.requireBinaryExpressionWithoutTypeAssertion(expr)
} }
} }
func (w *lintUnchekedTypeAssertion) handleSwitch(n *ast.SwitchStmt) { func (w *lintUncheckedTypeAssertion) handleSwitch(n *ast.SwitchStmt) {
w.requireNoTypeAssert(n.Tag) w.requireNoTypeAssert(n.Tag)
w.requireBinaryExpressionWithoutTypeAssertion(n.Tag) w.requireBinaryExpressionWithoutTypeAssertion(n.Tag)
} }
func (w *lintUnchekedTypeAssertion) handleAssignment(n *ast.AssignStmt) { func (w *lintUncheckedTypeAssertion) handleAssignment(n *ast.AssignStmt) {
if len(n.Rhs) == 0 { if len(n.Rhs) == 0 {
return return
} }
@ -148,21 +148,21 @@ func (w *lintUnchekedTypeAssertion) handleAssignment(n *ast.AssignStmt) {
} }
// handles "return foo(.*bar)" - one of them is enough to fail as golang does not forward the type cast tuples in return statements // handles "return foo(.*bar)" - one of them is enough to fail as golang does not forward the type cast tuples in return statements
func (w *lintUnchekedTypeAssertion) handleReturn(n *ast.ReturnStmt) { func (w *lintUncheckedTypeAssertion) handleReturn(n *ast.ReturnStmt) {
for _, r := range n.Results { for _, r := range n.Results {
w.requireNoTypeAssert(r) w.requireNoTypeAssert(r)
} }
} }
func (w *lintUnchekedTypeAssertion) handleRange(n *ast.RangeStmt) { func (w *lintUncheckedTypeAssertion) handleRange(n *ast.RangeStmt) {
w.requireNoTypeAssert(n.X) w.requireNoTypeAssert(n.X)
} }
func (w *lintUnchekedTypeAssertion) handleChannelSend(n *ast.SendStmt) { func (w *lintUncheckedTypeAssertion) handleChannelSend(n *ast.SendStmt) {
w.requireNoTypeAssert(n.Value) w.requireNoTypeAssert(n.Value)
} }
func (w *lintUnchekedTypeAssertion) Visit(node ast.Node) ast.Visitor { func (w *lintUncheckedTypeAssertion) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) { switch n := node.(type) {
case *ast.RangeStmt: case *ast.RangeStmt:
w.handleRange(n) w.handleRange(n)
@ -183,7 +183,7 @@ func (w *lintUnchekedTypeAssertion) Visit(node ast.Node) ast.Visitor {
return w return w
} }
func (w *lintUnchekedTypeAssertion) addFailure(n *ast.TypeAssertExpr, why string) { func (w *lintUncheckedTypeAssertion) addFailure(n *ast.TypeAssertExpr, why string) {
s := fmt.Sprintf("type cast result is unchecked in %v - %s", gofmt(n), why) s := fmt.Sprintf("type cast result is unchecked in %v - %s", gofmt(n), why)
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Category: "bad practice", Category: "bad practice",

View File

@ -185,9 +185,10 @@ func (lintUnconditionalRecursionRule) hasControlExit(node ast.Node) bool {
return false return false
} }
fn := se.Sel.Name functionName := se.Sel.Name
pkg := id.Name pkgName := id.Name
if exitFunctions[pkg] != nil && exitFunctions[pkg][fn] { // it's a call to an exit function isCallToExitFunction := exitFunctions[pkgName] != nil && exitFunctions[pkgName][functionName]
if isCallToExitFunction {
return true return true
} }
} }

View File

@ -19,7 +19,12 @@ type UnhandledErrorRule struct {
func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { func (r *UnhandledErrorRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
if r.ignoreList == nil { defer r.Unlock()
if r.ignoreList != nil {
return // already configured
}
for _, arg := range arguments { for _, arg := range arguments {
argStr, ok := arg.(string) argStr, ok := arg.(string)
if !ok { if !ok {
@ -39,8 +44,6 @@ func (r *UnhandledErrorRule) configure(arguments lint.Arguments) {
r.ignoreList = append(r.ignoreList, exp) r.ignoreList = append(r.ignoreList, exp)
} }
} }
r.Unlock()
}
// Apply applies the rule to given file. // Apply applies the rule to given file.
func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
@ -130,9 +133,9 @@ func (w *lintUnhandledErrors) funcName(call *ast.CallExpr) string {
} }
name := fn.FullName() name := fn.FullName()
name = strings.Replace(name, "(", "", -1) name = strings.ReplaceAll(name, "(", "")
name = strings.Replace(name, ")", "", -1) name = strings.ReplaceAll(name, ")", "")
name = strings.Replace(name, "*", "", -1) name = strings.ReplaceAll(name, "*", "")
return name return name
} }

View File

@ -46,13 +46,15 @@ type lintVarDeclarations struct {
func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor { func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor {
switch v := node.(type) { switch v := node.(type) {
case *ast.GenDecl: case *ast.GenDecl:
if v.Tok != token.CONST && v.Tok != token.VAR { isVarOrConstDeclaration := v.Tok == token.CONST || v.Tok == token.VAR
if !isVarOrConstDeclaration {
return nil return nil
} }
w.lastGen = v w.lastGen = v
return w return w
case *ast.ValueSpec: case *ast.ValueSpec:
if w.lastGen.Tok == token.CONST { isConstDeclaration := w.lastGen.Tok == token.CONST
if isConstDeclaration {
return nil return nil
} }
if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 {
@ -64,14 +66,14 @@ func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor {
if isIdent(v.Names[0], "_") { if isIdent(v.Names[0], "_") {
return nil return nil
} }
// If the RHS is a zero value, suggest dropping it. // If the RHS is a isZero value, suggest dropping it.
zero := false isZero := false
if lit, ok := rhs.(*ast.BasicLit); ok { if lit, ok := rhs.(*ast.BasicLit); ok {
zero = zeroLiteral[lit.Value] isZero = zeroLiteral[lit.Value]
} else if isIdent(rhs, "nil") { } else if isIdent(rhs, "nil") {
zero = true isZero = true
} }
if zero { if isZero {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Confidence: 0.9, Confidence: 0.9,
Node: rhs, Node: rhs,

View File

@ -19,9 +19,9 @@ var upperCaseConstRE = regexp.MustCompile(`^_?[A-Z][A-Z\d]*(_[A-Z\d]+)*$`)
// VarNamingRule lints given else constructs. // VarNamingRule lints given else constructs.
type VarNamingRule struct { type VarNamingRule struct {
configured bool configured bool
allowlist []string allowList []string
blocklist []string blockList []string
upperCaseConst bool // if true - allows to use UPPER_SOME_NAMES for constants allowUpperCaseConst bool // if true - allows to use UPPER_SOME_NAMES for constants
skipPackageNameChecks bool skipPackageNameChecks bool
sync.Mutex sync.Mutex
} }
@ -35,11 +35,11 @@ func (r *VarNamingRule) configure(arguments lint.Arguments) {
r.configured = true r.configured = true
if len(arguments) >= 1 { if len(arguments) >= 1 {
r.allowlist = getList(arguments[0], "allowlist") r.allowList = getList(arguments[0], "allowlist")
} }
if len(arguments) >= 2 { if len(arguments) >= 2 {
r.blocklist = getList(arguments[1], "blocklist") r.blockList = getList(arguments[1], "blocklist")
} }
if len(arguments) >= 3 { if len(arguments) >= 3 {
@ -56,7 +56,7 @@ func (r *VarNamingRule) configure(arguments lint.Arguments) {
if !ok { if !ok {
panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])) panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0]))
} }
r.upperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true" r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true"
r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true" r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true"
} }
} }
@ -93,12 +93,12 @@ func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.
walker := lintNames{ walker := lintNames{
file: file, file: file,
fileAst: fileAst, fileAst: fileAst,
allowlist: r.allowlist, allowList: r.allowList,
blocklist: r.blocklist, blockList: r.blockList,
onFailure: func(failure lint.Failure) { onFailure: func(failure lint.Failure) {
failures = append(failures, failure) failures = append(failures, failure)
}, },
upperCaseConst: r.upperCaseConst, upperCaseConst: r.allowUpperCaseConst,
} }
if !r.skipPackageNameChecks { if !r.skipPackageNameChecks {
@ -151,7 +151,7 @@ func (w *lintNames) check(id *ast.Ident, thing string) {
return return
} }
should := lint.Name(id.Name, w.allowlist, w.blocklist) should := lint.Name(id.Name, w.allowList, w.blockList)
if id.Name == should { if id.Name == should {
return return
} }
@ -177,8 +177,8 @@ type lintNames struct {
file *lint.File file *lint.File
fileAst *ast.File fileAst *ast.File
onFailure func(lint.Failure) onFailure func(lint.Failure)
allowlist []string allowList []string
blocklist []string blockList []string
upperCaseConst bool upperCaseConst bool
} }

View File

@ -51,7 +51,7 @@ func (w lintWaitGroupByValueRule) Visit(node ast.Node) ast.Visitor {
}) })
} }
return nil return nil // skip visiting function body
} }
func (lintWaitGroupByValueRule) isWaitGroup(ft ast.Expr) bool { func (lintWaitGroupByValueRule) isWaitGroup(ft ast.Expr) bool {