-
Notifications
You must be signed in to change notification settings - Fork 25.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
StringFormattingCheck
checkstyle rule (#81603)
The new rule Checks for calls to `String#formatted(Object...)` that include format specifiers that are not locale-safe. This method always uses the default `Locale`, and so for our purposes it is safer to use `String#format(Locale, String, Object...)`. Note that this rule can currently only detect violations when calling `formatted()` on a string literal or text block. In theory, it could be extended to detect violations in local variables or statics. Note that this change also forbids `.formatted()` in server code, so we are only permitted to use `.formatted()` in test code.
- Loading branch information
1 parent
5b1b0fb
commit 3f01fad
Showing
23 changed files
with
210 additions
and
86 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
...ons/src/main/java/org/elasticsearch/gradle/internal/checkstyle/StringFormattingCheck.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 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
package org.elasticsearch.gradle.internal.checkstyle; | ||
|
||
import com.puppycrawl.tools.checkstyle.StatelessCheck; | ||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | ||
import com.puppycrawl.tools.checkstyle.api.DetailAST; | ||
import com.puppycrawl.tools.checkstyle.api.TokenTypes; | ||
|
||
import java.util.Locale; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Checks for calls to {@link String#formatted(Object...)} that include format specifiers that | ||
* are not locale-safe. This method always uses the default {@link Locale}, and so for our | ||
* purposes it is safer to use {@link String#format(Locale, String, Object...)}. | ||
* <p> | ||
* Note that this rule can currently only detect violations when calling <code>formatted()</code> | ||
* on a string literal or text block. In theory, it could be extended to detect violations in | ||
* local variables or statics. | ||
*/ | ||
@StatelessCheck | ||
public class StringFormattingCheck extends AbstractCheck { | ||
|
||
public static final String FORMATTED_MSG_KEY = "forbidden.formatted"; | ||
|
||
@Override | ||
public int[] getDefaultTokens() { | ||
return getRequiredTokens(); | ||
} | ||
|
||
@Override | ||
public int[] getAcceptableTokens() { | ||
return getRequiredTokens(); | ||
} | ||
|
||
@Override | ||
public int[] getRequiredTokens() { | ||
return new int[] { TokenTypes.METHOD_CALL }; | ||
} | ||
|
||
@Override | ||
public void visitToken(DetailAST ast) { | ||
checkFormattedMethod(ast); | ||
} | ||
|
||
// Originally pinched from java/util/Formatter.java but then modified. | ||
// %[argument_index$][flags][width][.precision][t]conversion | ||
private static final Pattern formatSpecifier = Pattern.compile("%(?:\\d+\\$)?(?:[-#+ 0,\\(<]*)?(?:\\d+)?(?:\\.\\d+)?([tT]?[a-zA-Z%])"); | ||
|
||
private void checkFormattedMethod(DetailAST ast) { | ||
final DetailAST dotAst = ast.findFirstToken(TokenTypes.DOT); | ||
if (dotAst == null) { | ||
return; | ||
} | ||
|
||
final String methodName = dotAst.findFirstToken(TokenTypes.IDENT).getText(); | ||
if (methodName.equals("formatted") == false) { | ||
return; | ||
} | ||
|
||
final DetailAST subjectAst = dotAst.getFirstChild(); | ||
|
||
String stringContent = null; | ||
if (subjectAst.getType() == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) { | ||
stringContent = subjectAst.findFirstToken(TokenTypes.TEXT_BLOCK_CONTENT).getText(); | ||
} else if (subjectAst.getType() == TokenTypes.STRING_LITERAL) { | ||
stringContent = subjectAst.getText(); | ||
} | ||
|
||
if (stringContent != null) { | ||
final Matcher m = formatSpecifier.matcher(stringContent); | ||
while (m.find()) { | ||
char specifier = m.group(1).toLowerCase(Locale.ROOT).charAt(0); | ||
|
||
if (specifier == 'd' || specifier == 'e' || specifier == 'f' || specifier == 'g' || specifier == 't') { | ||
log(ast, FORMATTED_MSG_KEY, m.group()); | ||
} | ||
} | ||
} | ||
} | ||
} |
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
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
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.