Skip to content

Commit

Permalink
Introduce SpecifyLocale check
Browse files Browse the repository at this point in the history
  • Loading branch information
mlrprananta committed Nov 26, 2022
1 parent 16c8bb0 commit d1a6032
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package tech.picnic.errorprone.bugpatterns;

import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static tech.picnic.errorprone.bugpatterns.util.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.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import tech.picnic.errorprone.bugpatterns.util.SourceCode;

/**
* A {@link BugChecker} that flags {@link String#toLowerCase()} or {@link String#toUpperCase()}
* which do not specify a Locale.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Specify `Locale.ROOT` or `Locale.getDefault()` when calling `String#to{Lower,Upper}Case` without a specific Locale",
link = BUG_PATTERNS_BASE_URL + "SpecifyLocale",
linkType = CUSTOM,
severity = WARNING,
tags = LIKELY_ERROR)
public final class SpecifyLocale extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Matcher<ExpressionTree> STRING_TO_UPPER_OR_LOWER_CASE =
instanceMethod()
.onExactClass(String.class.getName())
.namedAnyOf("toLowerCase", "toUpperCase");

/** Instantiates a new {@link SpecifyLocale} instance. */
public SpecifyLocale() {}

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (STRING_TO_UPPER_OR_LOWER_CASE.matches(tree, state) && tree.getArguments().isEmpty()) {
return buildDescription(tree)
.addFix(buildFix("Locale.ROOT", tree, state))
.addFix(buildFix("Locale.getDefault()", tree, state))
.build();
}
return Description.NO_MATCH;
}

private static Fix buildFix(
String localeToSpecify, MethodInvocationTree tree, VisitorState state) {
return SuggestedFix.builder()
.replace(
tree, SourceCode.treeToString(tree, state).replace("()", "(" + localeToSpecify + ")"))
.addImport("java.util.Locale")
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package tech.picnic.errorprone.bugpatterns;

import static com.google.errorprone.BugCheckerRefactoringTestHelper.newInstance;

import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;

final class SpecifyLocaleTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(SpecifyLocale.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
newInstance(SpecifyLocale.class, getClass());

@Test
void identification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" // BUG: Diagnostic contains:",
" \"a\".toUpperCase();",
"",
" // BUG: Diagnostic contains:",
" \"b\".toLowerCase();",
"",
" String c = \"c\";",
" // BUG: Diagnostic contains:",
" c.toUpperCase();",
"",
" String d = \"d\";",
" // BUG: Diagnostic contains:",
" d.toLowerCase();",
" }",
"}")
.doTest();
}

@Test
void replacementFirstSuggestedFix() {
refactoringTestHelper
.setFixChooser(FixChoosers.FIRST)
.addInputLines(
"A.java",
"",
"class A {",
" void m() {",
" \"a\".toUpperCase();",
" \"b\".toLowerCase();",
" }",
"}")
.addOutputLines(
"A.java",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" \"a\".toUpperCase(Locale.ROOT);",
" \"b\".toLowerCase(Locale.ROOT);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}

@Test
void replacementSecondSuggestedFix() {
refactoringTestHelper
.setFixChooser(FixChoosers.SECOND)
.addInputLines(
"A.java",
"",
"class A {",
" void m() {",
" \"a\".toUpperCase();",
" \"b\".toLowerCase();",
" }",
"}")
.addOutputLines(
"A.java",
"import java.util.Locale;",
"",
"class A {",
" void m() {",
" \"a\".toUpperCase(Locale.getDefault());",
" \"b\".toLowerCase(Locale.getDefault());",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

0 comments on commit d1a6032

Please sign in to comment.