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