diff --git a/check_api/src/main/java/com/google/errorprone/fixes/SuggestedFixes.java b/check_api/src/main/java/com/google/errorprone/fixes/SuggestedFixes.java index 7c384c113a3..5fa79321507 100644 --- a/check_api/src/main/java/com/google/errorprone/fixes/SuggestedFixes.java +++ b/check_api/src/main/java/com/google/errorprone/fixes/SuggestedFixes.java @@ -1863,10 +1863,10 @@ public static String castTree(ExpressionTree expressionTree, String toType, Visi public static Optional replaceVariableType( VariableTree tree, String replacementType, VisitorState state) { Tree type = tree.getType(); - if (hasExplicitSource(type, state)) { + if (type != null && hasExplicitSource(type, state)) { return Optional.of(SuggestedFix.replace(type, replacementType)); } - int pos = getStartPosition(type); + int pos = type != null ? getStartPosition(type) : Position.NOPOS; if (pos == Position.NOPOS) { pos = getStartPosition(tree); } diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/VarWithPrimitiveTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/VarWithPrimitiveTest.java index 1eab280f126..280e82f26b8 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/VarWithPrimitiveTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/VarWithPrimitiveTest.java @@ -16,7 +16,10 @@ package com.google.errorprone.bugpatterns; +import static com.google.common.truth.TruthJUnit.assume; + import com.google.errorprone.BugCheckerRefactoringTestHelper; +import com.google.errorprone.CompilationTestHelper; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -27,6 +30,9 @@ public final class VarWithPrimitiveTest { private final BugCheckerRefactoringTestHelper refactoringHelper = BugCheckerRefactoringTestHelper.newInstance(VarWithPrimitive.class, getClass()); + private final CompilationTestHelper compilationHelper = + CompilationTestHelper.newInstance(VarWithPrimitive.class, getClass()); + // from https://openjdk.org/projects/amber/guides/lvti-style-guide#G7 @Test public void lvtiExamples() { @@ -194,6 +200,229 @@ long getAgeAsLong() { .doTest(); } + @Test + public void enhancedForLoop() { + refactoringHelper + .addInputLines( + "Test.java", + """ + class Test { + void t() { + int[] arr = {1, 2, 3}; + for (var x : arr) { + System.out.println(x); + } + } + } + """) + .addOutputLines( + "Test.java", + """ + class Test { + void t() { + int[] arr = {1, 2, 3}; + for (int x : arr) { + System.out.println(x); + } + } + } + """) + .doTest(); + } + + @Test + public void forLoopInitializer() { + refactoringHelper + .addInputLines( + "Test.java", + """ + class Test { + void t() { + for (var i = 0; i < 10; i++) { + System.out.println(i); + } + } + } + """) + .addOutputLines( + "Test.java", + """ + class Test { + void t() { + for (int i = 0; i < 10; i++) { + System.out.println(i); + } + } + } + """) + .doTest(); + } + + @Test + public void implicitLambdaParameter_noMatch() { + refactoringHelper + .addInputLines( + "Test.java", + """ + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + import java.util.stream.IntStream; + class Test { + void foo() { + byte[] bar = new byte[6]; + Map> indicesMap = + IntStream.range(0, 6) + .mapToObj(n -> n) + .collect( + Collectors.groupingBy( + n -> bar[n], + Collectors.mapping( + n -> (byte) n.intValue(), Collectors.toList()))); + } + } + """) + .expectUnchanged() + .doTest(); + } + + @Test + public void varLambdaParam() { + refactoringHelper + .addInputLines( + "Test.java", + """ + import java.util.function.IntUnaryOperator; + class Test { + void t() { + IntUnaryOperator op = (var n) -> n * 2; + } + } + """) + .addOutputLines( + "Test.java", + """ + import java.util.function.IntUnaryOperator; + class Test { + void t() { + IntUnaryOperator op = (int n) -> n * 2; + } + } + """) + .doTest(); + } + + @Test + public void annotatedVarLambdaParam() { + refactoringHelper + .addInputLines("A.java", "@interface A { int var() default 0; }") + .expectUnchanged() + .addInputLines( + "Test.java", + """ + import java.util.function.IntUnaryOperator; + class Test { + void t() { + IntUnaryOperator op = (@A(var = 0) var n) -> n * 2; + } + } + """) + .addOutputLines( + "Test.java", + """ + import java.util.function.IntUnaryOperator; + class Test { + void t() { + IntUnaryOperator op = (@A(var = 0) int n) -> n * 2; + } + } + """) + .doTest(); + } + + @Test + public void annotatedVarLocalVariable_beforeJdk27() { + // Before JDK 27 there is no AST node for the var type, so the token scan finds two 'var' + // tokens (@A(var=0) attribute + type keyword) and safely returns no fix. + assume().that(Runtime.version().feature()).isLessThan(27); + refactoringHelper + .addInputLines("A.java", "@interface A { int var() default 0; }") + .expectUnchanged() + .addInputLines( + "Test.java", + """ + class Test { + void t() { + @A(var = 0) var n = 5; + } + } + """) + .expectUnchanged() + .doTest(); + } + + @Test + public void annotatedVarLocalVariable_jdk27AndAbove() { + // On JDK 27+ there is an AST node for the var type with source positions, so hasExplicitSource + // returns true and the replacement targets the type keyword directly, preserving the + // annotation. + assume().that(Runtime.version().feature()).isAtLeast(27); + refactoringHelper + .addInputLines("A.java", "@interface A { int var() default 0; }") + .expectUnchanged() + .addInputLines( + "Test.java", + """ + class Test { + void t() { + @A(var = 0) var n = 5; + } + } + """) + .addOutputLines( + "Test.java", + """ + class Test { + void t() { + @A(var = 0) int n = 5; + } + } + """) + .doTest(); + } + + @Test + public void multiParamImplicitLambda_noMatch() { + compilationHelper + .addSourceLines( + "Test.java", + """ + import java.util.function.IntBinaryOperator; + class Test { + void t() { + IntBinaryOperator op = (a, b) -> a + b; + } + } + """) + .doTest(); + } + + @Test + public void explicitLambdaParam_noMatch() { + compilationHelper + .addSourceLines( + "Test.java", + """ + import java.util.function.IntUnaryOperator; + class Test { + void t() { + IntUnaryOperator op = (int n) -> n * 2; + } + } + """) + .doTest(); + } + @Test public void explicitType() { refactoringHelper