-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
RedundantStringEscape
check
This check aims to simplify string constants by dropping redundant single quote escape sequences. The check is optimized for performance. While there, update existing checks such that they do not introduce violations of the type flagged by this new check.
- Loading branch information
1 parent
9e23030
commit 1b69726
Showing
11 changed files
with
276 additions
and
18 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
...prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringEscape.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package tech.picnic.errorprone.bugpatterns; | ||
|
||
import static com.google.errorprone.BugPattern.LinkType.CUSTOM; | ||
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; | ||
import static com.google.errorprone.BugPattern.StandardTags.SIMPLIFICATION; | ||
import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL; | ||
|
||
import com.google.auto.service.AutoService; | ||
import com.google.errorprone.BugPattern; | ||
import com.google.errorprone.VisitorState; | ||
import com.google.errorprone.bugpatterns.BugChecker; | ||
import com.google.errorprone.bugpatterns.BugChecker.LiteralTreeMatcher; | ||
import com.google.errorprone.fixes.SuggestedFix; | ||
import com.google.errorprone.matchers.Description; | ||
import com.google.errorprone.util.ASTHelpers; | ||
import com.sun.source.tree.LiteralTree; | ||
import tech.picnic.errorprone.utils.SourceCode; | ||
|
||
/** A {@link BugChecker} that flags string constants with extraneous escaping. */ | ||
@AutoService(BugChecker.class) | ||
@BugPattern( | ||
summary = "Inside string expressions single quotes do not need to be escaped", | ||
link = BUG_PATTERNS_BASE_URL + "RedundantStringEscape", | ||
linkType = CUSTOM, | ||
severity = SUGGESTION, | ||
tags = SIMPLIFICATION) | ||
public final class RedundantStringEscape extends BugChecker implements LiteralTreeMatcher { | ||
private static final long serialVersionUID = 1L; | ||
|
||
/** Instantiates a new {@link RedundantStringEscape} instance. */ | ||
public RedundantStringEscape() {} | ||
|
||
@Override | ||
public Description matchLiteral(LiteralTree tree, VisitorState state) { | ||
String constant = ASTHelpers.constValue(tree, String.class); | ||
if (constant == null || constant.indexOf('\'') == -1) { | ||
Check warning on line 36 in error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringEscape.java GitHub Actions / pitestA change can be made to line 36 without causing a test to fail
|
||
/* Fast path: this isn't a string constant with a single quote. */ | ||
return Description.NO_MATCH; | ||
} | ||
|
||
String source = SourceCode.treeToString(tree, state); | ||
if (!containsBannedEscapeSequence(source)) { | ||
/* Semi-fast path: this expression doesn't contain an escaped single quote. */ | ||
return Description.NO_MATCH; | ||
} | ||
|
||
/* Slow path: suggest dropping the escape characters. */ | ||
return describeMatch(tree, SuggestedFix.replace(tree, dropRedundantEscapeSequences(source))); | ||
} | ||
|
||
/** | ||
* Tells whether the given string constant source expression contains an escaped single quote. | ||
* | ||
* @implNote As the input is a literal Java string expression, it will start and end with a double | ||
* quote; as such any found backslash will not be the string's final character. | ||
*/ | ||
private static boolean containsBannedEscapeSequence(String source) { | ||
for (int p = source.indexOf('\\'); p != -1; p = source.indexOf('\\', p + 2)) { | ||
if (source.charAt(p + 1) == '\'') { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Simplifies the given string constant source expression by dropping the backslash preceding an | ||
* escaped single quote. | ||
* | ||
* @implNote Note that this method does not delegate to {@link | ||
* SourceCode#toStringConstantExpression}, as that operation may replace other Unicode | ||
* characters with their associated escape sequence. | ||
* @implNote As the input is a literal Java string expression, it will start and end with a double | ||
* quote; as such any found backslash will not be the string's final character. | ||
*/ | ||
private static String dropRedundantEscapeSequences(String source) { | ||
StringBuilder result = new StringBuilder(); | ||
|
||
for (int p = 0; p < source.length(); p++) { | ||
char c = source.charAt(p); | ||
if (c != '\\' || source.charAt(p + 1) != '\'') { | ||
result.append(c); | ||
} | ||
} | ||
|
||
return result.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
...e-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/RedundantStringEscapeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package tech.picnic.errorprone.bugpatterns; | ||
|
||
import com.google.errorprone.BugCheckerRefactoringTestHelper; | ||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; | ||
import com.google.errorprone.CompilationTestHelper; | ||
import org.junit.jupiter.api.Test; | ||
|
||
final class RedundantStringEscapeTest { | ||
@Test | ||
void identification() { | ||
CompilationTestHelper.newInstance(RedundantStringEscape.class, getClass()) | ||
.addSourceLines( | ||
"A.java", | ||
"import java.util.Arrays;", | ||
"import java.util.List;", | ||
"", | ||
"class A {", | ||
" List<String> m() {", | ||
" return Arrays.asList(", | ||
" \"foo\",", | ||
" \"ß\",", | ||
" \"'\",", | ||
" \"\\\"\",", | ||
" \"\\\\\",", | ||
" \"\\\\'\",", | ||
" \"'\\\\\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\\\\\'\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\'\\\\\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\'\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"'\\'\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\''\",", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\'\\'\",", | ||
" (", | ||
" // BUG: Diagnostic contains:", | ||
" /* Leading comment. */ \"\\'\" /* Trailing comment. */),", | ||
" // BUG: Diagnostic contains:", | ||
" \"\\'foo\\\"bar\\'baz\\\"qux\\'\");", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
void replacement() { | ||
BugCheckerRefactoringTestHelper.newInstance(RedundantStringEscape.class, getClass()) | ||
.addInputLines( | ||
"A.java", | ||
"import java.util.Arrays;", | ||
"import java.util.List;", | ||
"", | ||
"class A {", | ||
" List<String> m() {", | ||
" return Arrays.asList(", | ||
" \"\\'\",", | ||
" \"'\\'\",", | ||
" \"\\''\",", | ||
" \"\\'\\'\",", | ||
" \"\\'ß\\'\",", | ||
" (", | ||
" /* Leading comment. */ \"\\'\" /* Trailing comment. */),", | ||
" \"\\'foo\\\"bar\\'baz\\\"qux\\'\");", | ||
" }", | ||
"}") | ||
.addOutputLines( | ||
"A.java", | ||
"import java.util.Arrays;", | ||
"import java.util.List;", | ||
"", | ||
"class A {", | ||
" List<String> m() {", | ||
" return Arrays.asList(", | ||
" \"'\",", | ||
" \"''\",", | ||
" \"''\",", | ||
" \"''\",", | ||
" \"'ß'\",", | ||
" (", | ||
" /* Leading comment. */ \"'\" /* Trailing comment. */),", | ||
" \"'foo\\\"bar'baz\\\"qux'\");", | ||
" }", | ||
"}") | ||
.doTest(TestMode.TEXT_MATCH); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.