forked from Marcono1234/codeql-java-queries
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathavoidable-String-operation-before-StringBuilder-append.ql
More file actions
120 lines (106 loc) · 3.67 KB
/
Copy pathavoidable-String-operation-before-StringBuilder-append.ql
File metadata and controls
120 lines (106 loc) · 3.67 KB
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* Finds avoidable method calls on `CharSequence` and `String` values before they are appended
* to a `StringBuilder`. `StringBuilder` offers mutliple convenience methods which avoid creating
* temporary subsequences or substrings, or which are less verbose to use. If possible, these
* methods should be preferred.
*
* @kind problem
*/
import java
import semmle.code.java.dataflow.DataFlow
abstract class ExprWithAppendAlternative extends Expr {
abstract Expr getConvertedExpr();
}
class CharSequenceCall extends ExprWithAppendAlternative, MethodAccess {
CharSequenceCall() {
exists(Method m |
m = getMethod()
and m.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.lang", "CharSequence")
|
m.hasName([
// StringBuilder.append alternative with start and end parameters exists
"subSequence",
"toString"
])
)
}
override
Expr getConvertedExpr() {
result = getQualifier()
}
}
class StringCall extends ExprWithAppendAlternative, MethodAccess {
boolean isConvertingQualifier;
StringCall() {
exists(Method m |
m = getMethod()
and m.getDeclaringType() instanceof TypeString
|
isConvertingQualifier = true and m.hasName([
"concat",
"subSequence",
"substring",
"toCharArray"
// Don't consider String.toString(), that is already detected by a built-in CodeQL query
])
or isConvertingQualifier = false and m.hasName([
"copyValueOf",
"valueOf"
])
)
}
override
Expr getConvertedExpr() {
isConvertingQualifier = true and result = getQualifier()
or isConvertingQualifier = false and result = getArgument(0)
}
}
class BoxedToStringCall extends ExprWithAppendAlternative, MethodAccess {
BoxedToStringCall() {
exists(Method m |
m = getMethod()
and m.getDeclaringType() instanceof BoxedType
|
// Only consider static methods, for instance methods there is no difference
// in calling it manually or having StringBuilder call it internally
m.isStatic()
// Also covers Character.toString(int) (in addition to `toString(char)`)
and m.hasName([
"toString",
// Character.toChars(int)
"toChars"
])
and m.getNumberOfParameters() = 1
)
}
override
Expr getConvertedExpr() {
result = getArgument(0)
}
}
class StringBuildingCall extends MethodAccess {
StringBuildingType stringBuildingType;
int paramIndex;
StringBuildingCall() {
exists(Method m |
m = getMethod()
and stringBuildingType = m.getDeclaringType()
|
m.hasName("append") and paramIndex = 0
or
m.hasName("insert") and paramIndex = 1
)
}
Expr getAppendedArg() {
result = getArgument(paramIndex)
}
string getStringBuildingTypeName() {
result = stringBuildingType.getName()
}
}
from ExprWithAppendAlternative expr, Expr convertedExpr, StringBuildingCall stringBuildingCall
where
DataFlow::localExprFlow(expr, stringBuildingCall.getAppendedArg())
and convertedExpr = expr.getConvertedExpr()
select expr, "Instead of converting $@ expression and then appending it $@, should instead use specialized " + stringBuildingCall.getStringBuildingTypeName() + " method directly appending its value",
convertedExpr, "this", stringBuildingCall, "here"