From 4165372ec8fd142475a4e35fd0cf4f8042132208 Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Wed, 22 Feb 2023 22:16:50 +0100 Subject: dependencies: update set go min requirements to 1.19 update dependencies update vendor --- vendor/github.com/mgechev/revive/config/config.go | 1 + .../github.com/mgechev/revive/formatter/sarif.go | 4 +- .../github.com/mgechev/revive/rule/add-constant.go | 101 +++++++++++-- .../mgechev/revive/rule/comment-spacings.go | 82 +++++++++++ .../github.com/mgechev/revive/rule/early-return.go | 156 +++++++++++++++++---- .../github.com/mgechev/revive/rule/empty-lines.go | 4 +- .../mgechev/revive/rule/function-length.go | 8 +- .../mgechev/revive/rule/identical-branches.go | 4 +- .../mgechev/revive/rule/nested-structs.go | 10 ++ .../mgechev/revive/rule/string-format.go | 36 ++++- .../mgechev/revive/rule/unconditional-recursion.go | 6 +- .../mgechev/revive/rule/unhandled-error.go | 66 +++++++-- 12 files changed, 410 insertions(+), 68 deletions(-) create mode 100644 vendor/github.com/mgechev/revive/rule/comment-spacings.go (limited to 'vendor/github.com/mgechev') diff --git a/vendor/github.com/mgechev/revive/config/config.go b/vendor/github.com/mgechev/revive/config/config.go index 5c63f35b3..d6b4f4100 100644 --- a/vendor/github.com/mgechev/revive/config/config.go +++ b/vendor/github.com/mgechev/revive/config/config.go @@ -86,6 +86,7 @@ var allRules = append([]lint.Rule{ &rule.OptimizeOperandsOrderRule{}, &rule.UseAnyRule{}, &rule.DataRaceRule{}, + &rule.CommentSpacingsRule{}, }, defaultRules...) var allFormatters = []lint.Formatter{ diff --git a/vendor/github.com/mgechev/revive/formatter/sarif.go b/vendor/github.com/mgechev/revive/formatter/sarif.go index ee62adcc0..c6288db76 100644 --- a/vendor/github.com/mgechev/revive/formatter/sarif.go +++ b/vendor/github.com/mgechev/revive/formatter/sarif.go @@ -81,8 +81,8 @@ func (l *reviveRunLog) AddResult(failure lint.Failure) { } position := failure.Position filename := position.Start.Filename - line := positiveOrZero(position.Start.Line - 1) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_line - column := positiveOrZero(position.Start.Column - 1) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_column + line := positiveOrZero(position.Start.Line) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_line + column := positiveOrZero(position.Start.Column) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_column result := garif.NewResult(garif.NewMessageFromText(failure.Failure)) location := garif.NewLocation().WithURI(filename).WithLineColumn(line, column) diff --git a/vendor/github.com/mgechev/revive/rule/add-constant.go b/vendor/github.com/mgechev/revive/rule/add-constant.go index 414be38c3..36a7003da 100644 --- a/vendor/github.com/mgechev/revive/rule/add-constant.go +++ b/vendor/github.com/mgechev/revive/rule/add-constant.go @@ -3,6 +3,7 @@ package rule import ( "fmt" "go/ast" + "regexp" "strconv" "strings" "sync" @@ -32,8 +33,9 @@ func (wl whiteList) add(kind, list string) { // AddConstantRule lints unused params in functions. type AddConstantRule struct { - whiteList whiteList - strLitLimit int + whiteList whiteList + ignoreFunctions []*regexp.Regexp + strLitLimit int sync.Mutex } @@ -47,7 +49,13 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin failures = append(failures, failure) } - w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int), strLitLimit: r.strLitLimit, whiteLst: r.whiteList} + w := lintAddConstantRule{ + onFailure: onFailure, + strLits: make(map[string]int), + strLitLimit: r.strLitLimit, + whiteLst: r.whiteList, + ignoreFunctions: r.ignoreFunctions, + } ast.Walk(w, file.AST) @@ -60,28 +68,76 @@ func (*AddConstantRule) Name() string { } type lintAddConstantRule struct { - onFailure func(lint.Failure) - strLits map[string]int - strLitLimit int - whiteLst whiteList + onFailure func(lint.Failure) + strLits map[string]int + strLitLimit int + whiteLst whiteList + ignoreFunctions []*regexp.Regexp } func (w lintAddConstantRule) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { + case *ast.CallExpr: + w.checkFunc(n) + return nil case *ast.GenDecl: return nil // skip declarations case *ast.BasicLit: - switch kind := n.Kind.String(); kind { - case kindFLOAT, kindINT: - w.checkNumLit(kind, n) - case kindSTRING: - w.checkStrLit(n) - } + w.checkLit(n) } return w } +func (w lintAddConstantRule) checkFunc(expr *ast.CallExpr) { + fName := w.getFuncName(expr) + + for _, arg := range expr.Args { + switch t := arg.(type) { + case *ast.CallExpr: + w.checkFunc(t) + case *ast.BasicLit: + if w.isIgnoredFunc(fName) { + continue + } + w.checkLit(t) + } + } +} + +func (w lintAddConstantRule) getFuncName(expr *ast.CallExpr) string { + switch f := expr.Fun.(type) { + case *ast.SelectorExpr: + switch prefix := f.X.(type) { + case *ast.Ident: + return prefix.Name + "." + f.Sel.Name + } + case *ast.Ident: + return f.Name + } + + return "" +} + +func (w lintAddConstantRule) checkLit(n *ast.BasicLit) { + switch kind := n.Kind.String(); kind { + case kindFLOAT, kindINT: + w.checkNumLit(kind, n) + case kindSTRING: + w.checkStrLit(n) + } +} + +func (w lintAddConstantRule) isIgnoredFunc(fName string) bool { + for _, pattern := range w.ignoreFunctions { + if pattern.MatchString(fName) { + return true + } + } + + return false +} + func (w lintAddConstantRule) checkStrLit(n *ast.BasicLit) { if w.whiteLst[kindSTRING][n.Value] { return @@ -158,6 +214,25 @@ func (r *AddConstantRule) configure(arguments lint.Arguments) { panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) } r.strLitLimit = limit + case "ignoreFuncs": + excludes, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)) + } + + for _, exclude := range strings.Split(excludes, ",") { + exclude = strings.Trim(exclude, " ") + if exclude == "" { + panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") + } + + exp, err := regexp.Compile(exclude) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err)) + } + + r.ignoreFunctions = append(r.ignoreFunctions, exp) + } } } } diff --git a/vendor/github.com/mgechev/revive/rule/comment-spacings.go b/vendor/github.com/mgechev/revive/rule/comment-spacings.go new file mode 100644 index 000000000..abe2ad76d --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/comment-spacings.go @@ -0,0 +1,82 @@ +package rule + +import ( + "fmt" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// CommentSpacings Rule check the whether there is a space between +// the comment symbol( // ) and the start of the comment text +type CommentSpacingsRule struct { + allowList []string + sync.Mutex +} + +func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { + r.Lock() + defer r.Unlock() + + if r.allowList == nil { + r.allowList = []string{ + "//go:", + "//revive:", + } + + for _, arg := range arguments { + allow, ok := arg.(string) // Alt. non panicking version + if !ok { + panic(fmt.Sprintf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)) + } + r.allowList = append(r.allowList, `//`+allow+`:`) + } + } +} + +func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configure(args) + + var failures []lint.Failure + + for _, cg := range file.AST.Comments { + for _, comment := range cg.List { + commentLine := comment.Text + if len(commentLine) < 3 { + continue // nothing to do + } + + isOK := commentLine[2] == ' ' + if isOK { + continue + } + + if r.isAllowed(commentLine) { + continue + } + + failures = append(failures, lint.Failure{ + Node: comment, + Confidence: 1, + Category: "style", + Failure: "no space between comment delimiter and comment text", + }) + } + } + return failures +} + +func (*CommentSpacingsRule) Name() string { + return "comment-spacings" +} + +func (r *CommentSpacingsRule) isAllowed(line string) bool { + for _, allow := range r.allowList { + if strings.HasPrefix(line, allow) { + return true + } + } + + return false +} diff --git a/vendor/github.com/mgechev/revive/rule/early-return.go b/vendor/github.com/mgechev/revive/rule/early-return.go index bfbf6717c..ed0fcfae4 100644 --- a/vendor/github.com/mgechev/revive/rule/early-return.go +++ b/vendor/github.com/mgechev/revive/rule/early-return.go @@ -1,12 +1,15 @@ package rule import ( + "fmt" "go/ast" + "go/token" "github.com/mgechev/revive/lint" ) -// EarlyReturnRule lints given else constructs. +// EarlyReturnRule finds opportunities to reduce nesting by inverting +// the condition of an "if" block. type EarlyReturnRule struct{} // Apply applies the rule to given file. @@ -32,47 +35,142 @@ type lintEarlyReturnRule struct { } func (w lintEarlyReturnRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { + ifStmt, ok := node.(*ast.IfStmt) + if !ok { + return w + } + + w.visitIf(ifStmt, false, false) + return nil +} + +func (w lintEarlyReturnRule) visitIf(ifStmt *ast.IfStmt, hasNonReturnBranch, hasIfInitializer bool) { + // look for other if-else chains nested inside this if { } block + ast.Walk(w, ifStmt.Body) + + if ifStmt.Else == nil { + // no else branch + return + } + + if as, ok := ifStmt.Init.(*ast.AssignStmt); ok && as.Tok == token.DEFINE { + hasIfInitializer = true + } + bodyFlow := w.branchFlow(ifStmt.Body) + + switch elseBlock := ifStmt.Else.(type) { case *ast.IfStmt: - if n.Else == nil { - // no else branch - return w + if bodyFlow.canFlowIntoNext() { + hasNonReturnBranch = true } + w.visitIf(elseBlock, hasNonReturnBranch, hasIfInitializer) + + case *ast.BlockStmt: + // look for other if-else chains nested inside this else { } block + ast.Walk(w, elseBlock) - elseBlock, ok := n.Else.(*ast.BlockStmt) - if !ok { - // is if-else-if - return w + if hasNonReturnBranch && bodyFlow != branchFlowEmpty { + // if we de-indent this block then a previous branch + // might flow into it, affecting program behaviour + return } - lenElseBlock := len(elseBlock.List) - if lenElseBlock < 1 { - // empty else block, continue (there is another rule that warns on empty blocks) - return w + if !bodyFlow.canFlowIntoNext() { + // avoid overlapping with superfluous-else + return } - lenThenBlock := len(n.Body.List) - if lenThenBlock < 1 { - // then block is empty thus the stmt can be simplified - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Failure: "if c { } else {... return} can be simplified to if !c { ... return }", - }) + elseFlow := w.branchFlow(elseBlock) + if !elseFlow.canFlowIntoNext() { + failMsg := fmt.Sprintf("if c {%[1]s } else {%[2]s } can be simplified to if !c {%[2]s }%[1]s", + bodyFlow, elseFlow) - return w - } + if hasIfInitializer { + // if statement has a := initializer, so we might need to move the assignment + // onto its own line in case the body references it + failMsg += " (move short variable declaration to its own line if necessary)" + } - _, lastThenStmtIsReturn := n.Body.List[lenThenBlock-1].(*ast.ReturnStmt) - _, lastElseStmtIsReturn := elseBlock.List[lenElseBlock-1].(*ast.ReturnStmt) - if lastElseStmtIsReturn && !lastThenStmtIsReturn { w.onFailure(lint.Failure{ Confidence: 1, - Node: n, - Failure: "if c {...} else {... return } can be simplified to if !c { ... return } ...", + Node: ifStmt, + Failure: failMsg, }) } + + default: + panic("invalid node type for else") } +} - return w +type branchFlowKind int + +const ( + branchFlowEmpty branchFlowKind = iota + branchFlowReturn + branchFlowPanic + branchFlowContinue + branchFlowBreak + branchFlowGoto + branchFlowRegular +) + +func (w lintEarlyReturnRule) branchFlow(block *ast.BlockStmt) branchFlowKind { + blockLen := len(block.List) + if blockLen == 0 { + return branchFlowEmpty + } + + switch stmt := block.List[blockLen-1].(type) { + case *ast.ReturnStmt: + return branchFlowReturn + case *ast.BlockStmt: + return w.branchFlow(stmt) + case *ast.BranchStmt: + switch stmt.Tok { + case token.BREAK: + return branchFlowBreak + case token.CONTINUE: + return branchFlowContinue + case token.GOTO: + return branchFlowGoto + } + case *ast.ExprStmt: + if call, ok := stmt.X.(*ast.CallExpr); ok && isIdent(call.Fun, "panic") { + return branchFlowPanic + } + } + + return branchFlowRegular +} + +// Whether this branch's control can flow into the next statement following the if-else chain +func (k branchFlowKind) canFlowIntoNext() bool { + switch k { + case branchFlowReturn, branchFlowPanic, branchFlowContinue, branchFlowBreak, branchFlowGoto: + return false + default: + return true + } +} + +func (k branchFlowKind) String() string { + switch k { + case branchFlowEmpty: + return "" + case branchFlowReturn: + return " ... return" + case branchFlowPanic: + return " ... panic()" + case branchFlowContinue: + return " ... continue" + case branchFlowBreak: + return " ... break" + case branchFlowGoto: + return " ... goto" + case branchFlowRegular: + return " ..." + default: + panic("invalid kind") + } } diff --git a/vendor/github.com/mgechev/revive/rule/empty-lines.go b/vendor/github.com/mgechev/revive/rule/empty-lines.go index 12866072e..2710a8979 100644 --- a/vendor/github.com/mgechev/revive/rule/empty-lines.go +++ b/vendor/github.com/mgechev/revive/rule/empty-lines.go @@ -51,7 +51,7 @@ func (w lintEmptyLines) checkStart(block *ast.BlockStmt) { firstNode := block.List[0] firstStmt := w.position(firstNode.Pos()) - firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) == 0 + firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) <= 0 _, firstBlockLineIsComment := w.cmap[blockStart.Line+1] if firstBlockLineIsStmt || firstBlockLineIsComment { return @@ -70,7 +70,7 @@ func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) { lastNode := block.List[len(block.List)-1] lastStmt := w.position(lastNode.End()) - lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line == 0 + lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line <= 0 _, lastBlockLineIsComment := w.cmap[blockEnd.Line-1] if lastBlockLineIsStmt || lastBlockLineIsComment { return diff --git a/vendor/github.com/mgechev/revive/rule/function-length.go b/vendor/github.com/mgechev/revive/rule/function-length.go index 717ddbf7b..d600d7a2a 100644 --- a/vendor/github.com/mgechev/revive/rule/function-length.go +++ b/vendor/github.com/mgechev/revive/rule/function-length.go @@ -11,17 +11,19 @@ import ( // FunctionLength lint. type FunctionLength struct { - maxStmt int - maxLines int + maxStmt int + maxLines int + configured bool sync.Mutex } func (r *FunctionLength) configure(arguments lint.Arguments) { r.Lock() - if r.maxLines == 0 { + if !r.configured { maxStmt, maxLines := r.parseArguments(arguments) r.maxStmt = int(maxStmt) r.maxLines = int(maxLines) + r.configured = true } r.Unlock() } diff --git a/vendor/github.com/mgechev/revive/rule/identical-branches.go b/vendor/github.com/mgechev/revive/rule/identical-branches.go index b1a69097f..9222c8a9c 100644 --- a/vendor/github.com/mgechev/revive/rule/identical-branches.go +++ b/vendor/github.com/mgechev/revive/rule/identical-branches.go @@ -63,8 +63,10 @@ func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { } ref := gofmt(branches[0]) + refSize := len(branches[0].List) for i := 1; i < len(branches); i++ { - if gofmt(branches[i]) != ref { + currentSize := len(branches[i].List) + if currentSize != refSize || gofmt(branches[i]) != ref { return false } } diff --git a/vendor/github.com/mgechev/revive/rule/nested-structs.go b/vendor/github.com/mgechev/revive/rule/nested-structs.go index b4f7352db..fd1226991 100644 --- a/vendor/github.com/mgechev/revive/rule/nested-structs.go +++ b/vendor/github.com/mgechev/revive/rule/nested-structs.go @@ -37,12 +37,22 @@ type lintNestedStructs struct { func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor { switch v := n.(type) { + case *ast.TypeSpec: + _, isInterface := v.Type.(*ast.InterfaceType) + if isInterface { + return nil // do not analyze interface declarations + } case *ast.FuncDecl: if v.Body != nil { ast.Walk(l, v.Body) } return nil case *ast.Field: + _, isChannelField := v.Type.(*ast.ChanType) + if isChannelField { + return nil + } + filter := func(n ast.Node) bool { switch n.(type) { case *ast.StructType: diff --git a/vendor/github.com/mgechev/revive/rule/string-format.go b/vendor/github.com/mgechev/revive/rule/string-format.go index e7841e8c3..0e30ebf8b 100644 --- a/vendor/github.com/mgechev/revive/rule/string-format.go +++ b/vendor/github.com/mgechev/revive/rule/string-format.go @@ -68,6 +68,7 @@ type stringFormatSubrule struct { parent *lintStringFormatRule scope stringFormatSubruleScope regexp *regexp.Regexp + negated bool errorMessage string } @@ -89,17 +90,18 @@ var parseStringFormatScope = regexp.MustCompile( func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) { for i, argument := range arguments { - scope, regex, errorMessage := w.parseArgument(argument, i) + scope, regex, negated, errorMessage := w.parseArgument(argument, i) w.rules = append(w.rules, stringFormatSubrule{ parent: w, scope: scope, regexp: regex, + negated: negated, errorMessage: errorMessage, }) } } -func (w lintStringFormatRule) parseArgument(argument interface{}, ruleNum int) (scope stringFormatSubruleScope, regex *regexp.Regexp, errorMessage string) { +func (w lintStringFormatRule) parseArgument(argument interface{}, ruleNum int) (scope stringFormatSubruleScope, regex *regexp.Regexp, negated bool, errorMessage string) { g, ok := argument.([]interface{}) // Cast to generic slice first if !ok { w.configError("argument is not a slice", ruleNum, 0) @@ -146,7 +148,12 @@ func (w lintStringFormatRule) parseArgument(argument interface{}, ruleNum int) ( } // Strip / characters from the beginning and end of rule[1] before compiling - regex, err := regexp.Compile(rule[1][1 : len(rule[1])-1]) + negated = rule[1][0] == '!' + offset := 1 + if negated { + offset++ + } + regex, err := regexp.Compile(rule[1][offset : len(rule[1])-1]) if err != nil { w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1) } @@ -155,7 +162,7 @@ func (w lintStringFormatRule) parseArgument(argument interface{}, ruleNum int) ( if len(rule) == 3 { errorMessage = rule[2] } - return scope, regex, errorMessage + return scope, regex, negated, errorMessage } // Report an invalid config, this is specifically the user's fault @@ -261,7 +268,26 @@ func (r *stringFormatSubrule) Apply(call *ast.CallExpr) { } func (r *stringFormatSubrule) lintMessage(s string, node ast.Node) { - // Fail if the string doesn't match the user's regex + if r.negated { + if !r.regexp.MatchString(s) { + return + } + // Fail if the string does match the user's regex + var failure string + if len(r.errorMessage) > 0 { + failure = r.errorMessage + } else { + failure = fmt.Sprintf("string literal matches user defined regex /%s/", r.regexp.String()) + } + r.parent.onFailure(lint.Failure{ + Confidence: 1, + Failure: failure, + Node: node, + }) + return + } + + // Fail if the string does NOT match the user's regex if r.regexp.MatchString(s) { return } diff --git a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go b/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go index f0e83b0ce..bad907533 100644 --- a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go +++ b/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go @@ -28,12 +28,12 @@ func (*UnconditionalRecursionRule) Name() string { } type funcDesc struct { - reciverID *ast.Ident - id *ast.Ident + receiverID *ast.Ident + id *ast.Ident } func (fd *funcDesc) equal(other *funcDesc) bool { - receiversAreEqual := (fd.reciverID == nil && other.reciverID == nil) || fd.reciverID != nil && other.reciverID != nil && fd.reciverID.Name == other.reciverID.Name + receiversAreEqual := (fd.receiverID == nil && other.receiverID == nil) || fd.receiverID != nil && other.receiverID != nil && fd.receiverID.Name == other.receiverID.Name idsAreEqual := (fd.id == nil && other.id == nil) || fd.id.Name == other.id.Name return receiversAreEqual && idsAreEqual diff --git a/vendor/github.com/mgechev/revive/rule/unhandled-error.go b/vendor/github.com/mgechev/revive/rule/unhandled-error.go index 6cde24b7f..32a5fe48b 100644 --- a/vendor/github.com/mgechev/revive/rule/unhandled-error.go +++ b/vendor/github.com/mgechev/revive/rule/unhandled-error.go @@ -4,6 +4,8 @@ import ( "fmt" "go/ast" "go/types" + "regexp" + "strings" "sync" "github.com/mgechev/revive/lint" @@ -11,24 +13,30 @@ import ( // UnhandledErrorRule lints given else constructs. type UnhandledErrorRule struct { - ignoreList ignoreListType + ignoreList []*regexp.Regexp sync.Mutex } -type ignoreListType map[string]struct{} - func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { r.Lock() if r.ignoreList == nil { - r.ignoreList = make(ignoreListType, len(arguments)) - for _, arg := range arguments { argStr, ok := arg.(string) if !ok { panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) } - r.ignoreList[argStr] = struct{}{} + argStr = strings.Trim(argStr, " ") + if argStr == "" { + panic("Invalid argument to the unhandled-error rule, expected regular expression must not be empty.") + } + + exp, err := regexp.Compile(argStr) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the unhandled-error rule: regexp %q does not compile: %v", argStr, err)) + } + + r.ignoreList = append(r.ignoreList, exp) } } r.Unlock() @@ -60,7 +68,7 @@ func (*UnhandledErrorRule) Name() string { } type lintUnhandledErrors struct { - ignoreList ignoreListType + ignoreList []*regexp.Regexp pkg *lint.Package onFailure func(lint.Failure) } @@ -102,8 +110,8 @@ func (w *lintUnhandledErrors) Visit(node ast.Node) ast.Visitor { } func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) { - funcName := gofmt(n.Fun) - if _, mustIgnore := w.ignoreList[funcName]; mustIgnore { + name := w.funcName(n) + if w.isIgnoredFunc(name) { return } @@ -111,10 +119,34 @@ func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) { Category: "bad practice", Confidence: 1, Node: n, - Failure: fmt.Sprintf("Unhandled error in call to function %v", funcName), + Failure: fmt.Sprintf("Unhandled error in call to function %v", gofmt(n.Fun)), }) } +func (w *lintUnhandledErrors) funcName(call *ast.CallExpr) string { + fn, ok := w.getFunc(call) + if !ok { + return gofmt(call.Fun) + } + + name := fn.FullName() + name = strings.Replace(name, "(", "", -1) + name = strings.Replace(name, ")", "", -1) + name = strings.Replace(name, "*", "", -1) + + return name +} + +func (w *lintUnhandledErrors) isIgnoredFunc(funcName string) bool { + for _, pattern := range w.ignoreList { + if len(pattern.FindString(funcName)) == len(funcName) { + return true + } + } + + return false +} + func (*lintUnhandledErrors) isTypeError(t *types.Named) bool { const errorTypeName = "_.error" @@ -130,3 +162,17 @@ func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool { } return false } + +func (w *lintUnhandledErrors) getFunc(call *ast.CallExpr) (*types.Func, bool) { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return nil, false + } + + fn, ok := w.pkg.TypesInfo().ObjectOf(sel.Sel).(*types.Func) + if !ok { + return nil, false + } + + return fn, true +} -- cgit mrf-deployment