Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit d8ff2d1

Browse files
authored
Merge pull request #246 from smowton/smowton/feature/nuisance-dead-code-warnings
UnreachableStatement: tolerate more harmless unreachable return statements
2 parents 61bc51c + 5b34c05 commit d8ff2d1

4 files changed

Lines changed: 100 additions & 12 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The query "Unreachable statement" (`go/unreachable-statement`) now tolerates more unreachable return statements, which can often be required in Go following a function call that cannot return. Newly tolerated statements include `return true`, `return MyStruct{0, true}`, and any return when the return value has type `error`. This eliminates some nuisance results.

ql/src/RedundantCode/UnreachableStatement.ql

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,36 @@ ControlFlow::Node nonGuardPredecessor(ControlFlow::Node nd) {
2121
)
2222
}
2323

24+
/**
25+
* Matches if `retval` is a constant or a struct composed wholly of constants.
26+
*/
27+
predicate isAllowedReturnValue(Expr retval) {
28+
retval = Builtin::nil().getAReference()
29+
or
30+
retval = Builtin::true_().getAReference()
31+
or
32+
retval = Builtin::false_().getAReference()
33+
or
34+
retval instanceof BasicLit
35+
or
36+
// Allow -1 (which parses as unary-minus-of-literal) or !true, but not &somestruct,
37+
// for which we would usually prefer `return nil`
38+
isAllowedReturnValue(retval.(UnaryExpr).getOperand()) and
39+
not retval.getType().getUnderlyingType() instanceof PointerType
40+
or
41+
// Allow structs composed of allowed values
42+
retval instanceof StructLit and
43+
forall(Expr element | element = retval.(StructLit).getAnElement() | isAllowedReturnValue(element))
44+
or
45+
// Allow anything of type `error`, as `abort(); return constructError(...);`
46+
// is preferable to insisting on a misleading `return nil` that suggests
47+
// successful return:
48+
retval.getType().getEntity() = Builtin::error()
49+
}
50+
51+
/**
52+
* Matches if `s` is an allowed unreachable statement.
53+
*/
2454
predicate allowlist(Stmt s) {
2555
// `panic("unreachable")` and similar
2656
exists(CallExpr ce | ce = s.(ExprStmt).getExpr() or ce = s.(ReturnStmt).getExpr() |
@@ -29,11 +59,7 @@ predicate allowlist(Stmt s) {
2959
or
3060
// `return nil` and similar
3161
exists(ReturnStmt ret | ret = s |
32-
forall(Expr retval | retval = ret.getAnExpr() |
33-
retval = Builtin::nil().getAReference() or
34-
retval instanceof BasicLit or
35-
retval.(UnaryExpr).getOperand() instanceof BasicLit
36-
)
62+
forall(Expr retval | retval = ret.getAnExpr() | isAllowedReturnValue(retval))
3763
)
3864
or
3965
// statements in an `if false { ... }` and similar
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
| UnreachableStatement.go:5:27:5:29 | increment statement | This statement is unreachable. |
2-
| main.go:9:2:9:14 | expression statement | This statement is unreachable. |
3-
| main.go:14:2:14:14 | expression statement | This statement is unreachable. |
4-
| main.go:18:22:18:34 | expression statement | This statement is unreachable. |
5-
| main.go:26:2:26:14 | expression statement | This statement is unreachable. |
6-
| main.go:45:2:45:14 | expression statement | This statement is unreachable. |
7-
| main.go:51:3:51:15 | expression statement | This statement is unreachable. |
8-
| main.go:53:2:53:14 | expression statement | This statement is unreachable. |
2+
| main.go:11:2:11:14 | expression statement | This statement is unreachable. |
3+
| main.go:16:2:16:14 | expression statement | This statement is unreachable. |
4+
| main.go:20:22:20:34 | expression statement | This statement is unreachable. |
5+
| main.go:28:2:28:14 | expression statement | This statement is unreachable. |
6+
| main.go:47:2:47:14 | expression statement | This statement is unreachable. |
7+
| main.go:53:3:53:15 | expression statement | This statement is unreachable. |
8+
| main.go:55:2:55:14 | expression statement | This statement is unreachable. |
9+
| main.go:139:2:139:26 | return statement | This statement is unreachable. |
10+
| main.go:145:2:145:17 | return statement | This statement is unreachable. |
11+
| main.go:151:2:151:22 | return statement | This statement is unreachable. |
12+
| main.go:157:2:157:43 | return statement | This statement is unreachable. |

ql/test/query-tests/RedundantCode/UnreachableStatement/main.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package main
22

3+
import ("errors")
4+
35
func unreachable() {}
46

57
func reachable() {}
@@ -101,4 +103,58 @@ func test11() {
101103
}
102104
}
103105

106+
func test12() bool {
107+
select {}
108+
// Not flagged as this is a primitive return
109+
return true
110+
}
111+
112+
func test13() bool {
113+
select {}
114+
// Not flagged as this is a primitive return
115+
return false
116+
}
117+
118+
type mystruct struct {
119+
x int
120+
y bool
121+
}
122+
123+
func test14() mystruct {
124+
select {}
125+
// Not flagged as this is a struct literal composed of primitives
126+
return mystruct{0, true}
127+
}
128+
129+
func test15() error {
130+
select {}
131+
// Not flagged as any expression is acceptable for type `error`
132+
return errors.New("unreachable")
133+
}
134+
135+
func test16() *mystruct {
136+
select {}
137+
// Flagged, as `return nil` is possible and preferable when the
138+
// return site is unreachable.
139+
return &mystruct{0, true}
140+
}
141+
142+
func test17() int {
143+
select {}
144+
// Flagged, as a nontrivial unreachable return
145+
return test10(1)
146+
}
147+
148+
func test18() bool {
149+
select {}
150+
// Flagged, as a nontrivial unreachable return
151+
return test10(1) == 1
152+
}
153+
154+
func test19() mystruct {
155+
select {}
156+
// Flagged, as a nontrivial unreachable return
157+
return mystruct{test10(1), test10(2) == 2}
158+
}
159+
104160
func main() {}

0 commit comments

Comments
 (0)