Skip to content

Commit 594df79

Browse files
committed
Add statement-on-same-line-as-multi-line-statement-end.ql
1 parent 986a406 commit 594df79

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Finds statements which start on the same line as the end of a multi-line statement.
3+
* This might indicate broken formatting which can make the code difficult to read,
4+
* or it might indicate a logic error, e.g. a separate `if` statement instead of
5+
* `else if`:
6+
* ```java
7+
* if (conditionA()) {
8+
* ...
9+
* } if (conditionB()) {
10+
* //^ was supposed to be `else if`
11+
* ...
12+
* }
13+
* ```
14+
*
15+
* @kind problem
16+
*/
17+
18+
import java
19+
20+
from Stmt first, Location firstLocation, Stmt second, Location secondLocation
21+
where
22+
// This also excludes implicit statements in lambdas which are compared without statement outside of it
23+
first.getEnclosingCallable() = second.getEnclosingCallable()
24+
and firstLocation = first.getLocation()
25+
and secondLocation = second.getLocation()
26+
// Make sure both are in the same file to prevent false positives when CodeQL has for some reason extracted
27+
// different files with the same classes
28+
and firstLocation.getFile() = secondLocation.getFile()
29+
and firstLocation.getEndLine() = secondLocation.getStartLine()
30+
and firstLocation.getEndColumn() < secondLocation.getStartColumn()
31+
// To reduce false positives only consider statements spanning over more than one line
32+
and firstLocation.getStartLine() < firstLocation.getEndLine()
33+
// Ignore all kinds of statements which intentionally start on the same line
34+
and not (
35+
first.getParent+() = second
36+
or second.getParent+() = first
37+
or first instanceof SwitchCase
38+
// Empty statement is already covered by other queries
39+
or second instanceof EmptyStmt
40+
// Ignore false positives caused implicit parent constructor invocations
41+
or second = any(Callable c).getBody()
42+
// Ignore implicit statements when declaring enum constants
43+
or any(EnumConstant c).getAnAccess().(FieldWrite).getAnEnclosingStmt() = second
44+
// Ignore if statements where both statements can be a block without either being the parent of the other
45+
or exists(IfStmt ifStmt |
46+
first = ifStmt.getThen()
47+
and (
48+
second = ifStmt.getElse()
49+
or second.getParent() = ifStmt.getElse()
50+
)
51+
)
52+
// Ignore try statements where both statements can be a block without either being the parent of the other
53+
or exists(TryStmt tryStmt |
54+
first = tryStmt.getBlock()
55+
or first = tryStmt.getACatchClause().getBlock()
56+
|
57+
second = tryStmt.getACatchClause()
58+
// Use getParent() to also consider catch block statements at the same line as catch clause
59+
or second.getParent*() = tryStmt.getACatchClause().getBlock()
60+
or second = tryStmt.getFinally()
61+
)
62+
// Ignore try resource variable declaration
63+
or exists(TryStmt tryStmt |
64+
first = tryStmt.getAResourceDecl()
65+
|
66+
second = tryStmt.getBlock()
67+
or second = tryStmt.getAResourceDecl()
68+
)
69+
// Ignore jump statments, they are probably not as irritating if one the same line
70+
or second instanceof JumpStmt
71+
// Ignore single statement in block
72+
or any(SingletonBlock s).getStmt() = second.(ExprStmt)
73+
// Avoid duplicate results for statements with block
74+
or any(IfStmt s).getThen() = second
75+
or any(LoopStmt s).getBody() = second
76+
or any(TryStmt s).getBlock() = second
77+
)
78+
// Ignore Kotlin source code for now, the cases to ignore defined above don't seem to work there properly
79+
and not first.getCompilationUnit().isKotlinSourceFile()
80+
select second, "Starts on the same line as end of $@", first, "this statement"

0 commit comments

Comments
 (0)