1package checkers 2 3import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/go-critic/go-critic/checkers/internal/astwalk" 8 "github.com/go-critic/go-critic/framework/linter" 9 "github.com/go-toolsmith/astequal" 10 "github.com/go-toolsmith/typep" 11) 12 13func init() { 14 var info linter.CheckerInfo 15 info.Name = "nilValReturn" 16 info.Tags = []string{"diagnostic", "experimental"} 17 info.Summary = "Detects return statements those results evaluate to nil" 18 info.Before = ` 19if err == nil { 20 return err 21}` 22 info.After = ` 23// (A) - return nil explicitly 24if err == nil { 25 return nil 26} 27// (B) - typo in "==", change to "!=" 28if err != nil { 29 return err 30}` 31 32 collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) { 33 return astwalk.WalkerForStmt(&nilValReturnChecker{ctx: ctx}), nil 34 }) 35} 36 37type nilValReturnChecker struct { 38 astwalk.WalkHandler 39 ctx *linter.CheckerContext 40} 41 42func (c *nilValReturnChecker) VisitStmt(stmt ast.Stmt) { 43 ifStmt, ok := stmt.(*ast.IfStmt) 44 if !ok || len(ifStmt.Body.List) != 1 { 45 return 46 } 47 ret, ok := ifStmt.Body.List[0].(*ast.ReturnStmt) 48 if !ok { 49 return 50 } 51 expr, ok := ifStmt.Cond.(*ast.BinaryExpr) 52 if !ok { 53 return 54 } 55 xIsNil := expr.Op == token.EQL && 56 typep.SideEffectFree(c.ctx.TypesInfo, expr.X) && 57 qualifiedName(expr.Y) == "nil" 58 if !xIsNil { 59 return 60 } 61 for _, res := range ret.Results { 62 if astequal.Expr(expr.X, res) { 63 c.warn(ret, expr.X) 64 break 65 } 66 } 67} 68 69func (c *nilValReturnChecker) warn(cause, val ast.Node) { 70 c.ctx.Warn(cause, "returned expr is always nil; replace %s with nil", val) 71} 72