Skip to content

Commit 472bfa2

Browse files
authored
Merge pull request #19115 from owen-mc/java/port/java/string-replace-all-with-non-regex
Java: Add new quality query to detect `String#replaceAll` with non-regex first argument
2 parents 3d7c020 + 4f5bdbb commit 472bfa2

File tree

7 files changed

+72
-7
lines changed

7 files changed

+72
-7
lines changed

java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ ql/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql
99
ql/java/ql/src/Likely Bugs/Likely Typos/SuspiciousDateFormat.ql
1010
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
1111
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
12+
ql/java/ql/src/Performance/StringReplaceAllWithNonRegex.ql
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use of `String#replaceAll` with a first argument which is not a regular expression
2+
3+
Using `String#replaceAll` is less performant than `String#replace` when the first argument is not a regular expression.
4+
5+
## Overview
6+
7+
The `String#replaceAll` method is designed to work with regular expressions as its first parameter. When you use a simple string without any regex patterns (like special characters or syntax), it's more efficient to use `String#replace` instead. This is because `replaceAll` has to compile the input as a regular expression first, which adds unnecessary overhead when you are just replacing literal text.
8+
9+
## Recommendation
10+
11+
Use `String#replace` instead where a `replaceAll` call uses a trivial string as its first argument.
12+
13+
## Example
14+
15+
```java
16+
public class Test {
17+
void f() {
18+
String s1 = "test";
19+
s1 = s1.replaceAll("t", "x"); // NON_COMPLIANT
20+
s1 = s1.replaceAll(".*", "x"); // COMPLIANT
21+
}
22+
}
23+
24+
```
25+
26+
## References
27+
28+
- Java SE Documentation: [String.replaceAll](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/String.html#replaceAll(java.lang.String,java.lang.String)).
29+
- Common Weakness Enumeration: [CWE-1176](https://cwe.mitre.org/data/definitions/1176.html).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @id java/string-replace-all-with-non-regex
3+
* @name Use of `String#replaceAll` with a first argument which is not a regular expression
4+
* @description Using `String#replaceAll` with a first argument which is not a regular expression
5+
* is less efficient than using `String#replace`.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity recommendation
9+
* @tags quality
10+
* reliability
11+
* performance
12+
* external/cwe/cwe-1176
13+
*/
14+
15+
import java
16+
17+
from StringReplaceAllCall replaceAllCall, StringLiteral firstArg
18+
where
19+
firstArg = replaceAllCall.getArgument(0) and
20+
//only contains characters that could be a simple string
21+
firstArg.getValue().regexpMatch("^[a-zA-Z0-9]+$")
22+
select replaceAllCall,
23+
"This call to 'replaceAll' should be a call to 'replace' as its $@ is not a regular expression.",
24+
firstArg, "first argument"
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
- queries: .
22
- include:
33
id:
4-
- java/suspicious-date-format
5-
- java/integer-multiplication-cast-to-long
6-
- java/equals-on-unrelated-types
74
- java/contradictory-type-checks
8-
- java/reference-equality-of-boxed-types
5+
- java/equals-on-unrelated-types
96
- java/inconsistent-equals-and-hashcode
10-
- java/unchecked-cast-in-equals
11-
- java/unused-container
127
- java/input-resource-leak
8+
- java/integer-multiplication-cast-to-long
139
- java/output-resource-leak
14-
- java/type-variable-hides-type
10+
- java/reference-equality-of-boxed-types
11+
- java/string-replace-all-with-non-regex
12+
- java/suspicious-date-format
13+
- java/type-variable-hides-type
14+
- java/unchecked-cast-in-equals
15+
- java/unused-container
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| Test.java:4:14:4:36 | replaceAll(...) | This call to 'replaceAll' should be a call to 'replace' as its $@ is not a regular expression. | Test.java:4:28:4:30 | "t" | first argument |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: Performance/StringReplaceAllWithNonRegex.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class Test {
2+
void f() {
3+
String s1 = "test";
4+
s1 = s1.replaceAll("t", "x"); // $ Alert // NON_COMPLIANT
5+
s1 = s1.replaceAll(".*", "x"); // COMPLIANT
6+
}
7+
}

0 commit comments

Comments
 (0)