From 3dc86d87c85160083a934842aa220ebbacbabd22 Mon Sep 17 00:00:00 2001 From: chavacava Date: Thu, 28 Jun 2018 11:06:43 +0200 Subject: [PATCH] new ADS rule: newerr --- config.go | 1 + fixtures/ads-newerror.go | 7 +++ rule/ads-newerr.go | 97 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 fixtures/ads-newerror.go create mode 100644 rule/ads-newerr.go diff --git a/config.go b/config.go index ba45edb..ad6c7ae 100644 --- a/config.go +++ b/config.go @@ -52,6 +52,7 @@ var allRules = append([]lint.Rule{ &rule.GetReturnRule{}, &rule.ModifiesParamRule{}, &rule.DeepExitRule{}, + &rule.ADSNewErrRule{}, }, defaultRules...) var allFormatters = []lint.Formatter{ diff --git a/fixtures/ads-newerror.go b/fixtures/ads-newerror.go new file mode 100644 index 0000000..b92e6d8 --- /dev/null +++ b/fixtures/ads-newerror.go @@ -0,0 +1,7 @@ +package fixtures + +import "errors" + +func foo(a, b, c, d int) { + errors.New("aaa") +} diff --git a/rule/ads-newerr.go b/rule/ads-newerr.go new file mode 100644 index 0000000..c130aa9 --- /dev/null +++ b/rule/ads-newerr.go @@ -0,0 +1,97 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// ADSNewErrRule lints program exit at functions other than main or init. +type ADSNewErrRule struct{} + +// Apply applies the rule to given file. +func (r *ADSNewErrRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintNewErr{onFailure, "errors", "New"} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (r *ADSNewErrRule) Name() string { + return "ads-newerr" +} + +type lintNewErr struct { + onFailure func(lint.Failure) + targetPkg string + targetFunc string +} + +func (w lintNewErr) Visit(node ast.Node) ast.Visitor { + ce, ok := node.(*ast.CallExpr) + if !ok { + return w + } + + pkg, fn := getPkgFunc(ce) + + if pkg == w.targetPkg && fn == w.targetFunc { + if pkg == "errors" && fn == "New" { + w.targetFunc = "MessageOption" + return w + } + + s := searchErr{&w, ce} + for _, exp := range ce.Args { + ast.Walk(s, exp) + } + + w.targetFunc = "New" + } + + return w +} + +type searchErr struct { + w *lintNewErr + fc *ast.CallExpr +} + +func (s searchErr) Visit(node ast.Node) ast.Visitor { + + id, ok := node.(*ast.Ident) + if !ok { + return s + } + if id.Name == "err" { + s.w.onFailure(lint.Failure{ + Confidence: 0.8, + Node: s.fc, + Category: "bad practice", + URL: "#ads-newerr", + Failure: "consider errors.Wrap instead of errors.New", + }) + + } + + return s +} + +func getPkgFunc(ce *ast.CallExpr) (string, string) { + fc, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return "", "" + } + id, ok := fc.X.(*ast.Ident) + if !ok { + return "", "" + } + + return id.Name, fc.Sel.Name + +}