|
| 1 | +/** |
| 2 | + * @name Wrapped error is always nil |
| 3 | + * @description Finds calls to `Wrap` from `pkg/errors` where the error argument is always nil. |
| 4 | + * @kind problem |
| 5 | + * @problem.severity warning |
| 6 | + * @id go/unexpected-nil-value |
| 7 | + * @tags reliability |
| 8 | + * correctness |
| 9 | + * logic |
| 10 | + * @precision high |
| 11 | + */ |
| 12 | + |
| 13 | +import go |
| 14 | + |
| 15 | +/** Gets package for `github.com/pkg/errors`. */ |
| 16 | +string packagePath() { result = package("github.com/pkg/errors", "") } |
| 17 | + |
| 18 | +/** |
| 19 | + * An equality test which guarantees that an expression is always `nil`. |
| 20 | + */ |
| 21 | +class NilTestGuard extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode { |
| 22 | + DataFlow::Node otherNode; |
| 23 | + |
| 24 | + NilTestGuard() { |
| 25 | + this.getAnOperand() = Builtin::nil().getARead() and |
| 26 | + otherNode = this.getAnOperand() and |
| 27 | + not otherNode = Builtin::nil().getARead() |
| 28 | + } |
| 29 | + |
| 30 | + override predicate checks(Expr e, boolean outcome) { |
| 31 | + e = otherNode.asExpr() and |
| 32 | + outcome = this.getPolarity() |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +/** Gets a use of a local variable that has the value `nil`. */ |
| 37 | +DataFlow::ExprNode getNilFromLocalVariable() { |
| 38 | + exists(SsaVariable ssa, Write w | |
| 39 | + w.definesSsaVariable(ssa, Builtin::nil().getARead()) and |
| 40 | + result.asInstruction() = ssa.getAUse() |
| 41 | + ) |
| 42 | +} |
| 43 | + |
| 44 | +from DataFlow::Node n |
| 45 | +where |
| 46 | + n = any(Function f | f.hasQualifiedName(packagePath(), "Wrap")).getACall().getArgument(0) and |
| 47 | + ( |
| 48 | + // ```go |
| 49 | + // errors.Wrap(nil, "") |
| 50 | + // ``` |
| 51 | + n = Builtin::nil().getARead() |
| 52 | + or |
| 53 | + // ```go |
| 54 | + // var localVar error = nil |
| 55 | + // errors.Wrap(localVar, "") |
| 56 | + // ``` |
| 57 | + n = getNilFromLocalVariable() |
| 58 | + or |
| 59 | + // ```go |
| 60 | + // if err != nil { |
| 61 | + // return ... |
| 62 | + // } |
| 63 | + // if ok2, _ := f2(input); !ok2 { |
| 64 | + // return errors.Wrap(err, "") |
| 65 | + // } |
| 66 | + n = any(NilTestGuard ntg).getAGuardedNode() |
| 67 | + ) |
| 68 | +select n, "The first argument to 'errors.Wrap' is always nil" |
0 commit comments