/* * Copyright 2017 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.errorprone.bugpatterns; import com.google.errorprone.BugCheckerRefactoringTestHelper; import com.google.errorprone.CompilationTestHelper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Unit tests for {@link TypeParameterShadowing} */ @RunWith(JUnit4.class) public class TypeParameterShadowingTest { private CompilationTestHelper compilationHelper; private BugCheckerRefactoringTestHelper refactoring; @Before public void setUp() { compilationHelper = CompilationTestHelper.newInstance(TypeParameterShadowing.class, getClass()); refactoring = BugCheckerRefactoringTestHelper.newInstance(new TypeParameterShadowing(), getClass()); } @Test public void singleLevel() throws Exception { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "class Test<T> {", " // BUG: Diagnostic contains: T declared in Test", " <T> void something() {}", "}") .doTest(); } @Test public void staticNotFlagged() throws Exception { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "class Test<T> {", " static <T> void something() {}", "}") .doTest(); } @Test public void staticMethodInnerDoesntConflictWithOuter() throws Exception { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "class Test<D> {", " static <T> void something() {", " class MethodInner<D> {}", // Doesn't clash with Test<D> " class MethodInner2 {", " // BUG: Diagnostic contains: T declared in something", " <T> void clashingMethod() {}", " <D> void nonClashingMethod() {}", // <D> not in scope " }", " }", "}") .doTest(); } @Test public void nestedClassDeclarations() throws Exception { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "class Test<D> {", " // BUG: Diagnostic contains: D declared in Test", " class Test2<D> {}", "}") .doTest(); } @Test public void twoLevels() throws Exception { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "class Test<T> {", " class MyTest<J> {", " // BUG: Diagnostic matches: combo", " public <T,J> void something() {}", " }", "}") .expectErrorMessage( "combo", s -> s.contains("J declared in MyTest") && s.contains("T declared in Test")) .doTest(); } @Test public void renameTypeVar() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " <T> void something(T t) { T other = t;}", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " <T2> void something(T2 t) { T2 other = t;}", "}") .doTest(); } @Test public void renameRecursiveBound() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " <T extends Comparable<T>> void something(T t) { T other = t;}", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " <T2 extends Comparable<T2>> void something(T2 t) { T2 other = t;}", "}") .doTest(); } @Test public void refactorUnderneathStuff() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " <T> void something(T t) { T other = t;}", " <T> T identity(T t) { return t; }", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " <T2> void something(T2 t) { T2 other = t;}", " <T2> T2 identity(T2 t) { return t; }", "}") .doTest(); } @Test public void refactorMultipleVars() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T,D> {", " <T,D> void something(T t) { ", " T other = t;", " java.util.List<T> ts = new java.util.ArrayList<T>();", " D d = null; ", " }", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T,D> {", " <T2,D2> void something(T2 t) { ", " T2 other = t;", " java.util.List<T2> ts = new java.util.ArrayList<T2>();", " D2 d = null; ", " }", "}") .doTest(); } @Test public void refactorWithNestedTypeParameterDeclaration() throws Exception { // The nested @SuppressWarnings are because there will be multiple findings // (T is shadowed multiple times). We're trying to test all of the fixes suggested by the // finding generated from the outermost instance method, namely that it doesn't attempt to // rewrite symbols T that don't match the T we're rewriting. refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " <T,T2> void something(T t) { ", " T var = t;", " @SuppressWarnings(\"TypeParameterShadowing\")", " class MethodInnerWithGeneric<T> {}", " MethodInnerWithGeneric<T> innerVar = null;", " class MethodInner {", " @SuppressWarnings(\"TypeParameterShadowing\")", " <T> void doSomething() {}", " void doSomethingElse(T t) { this.<T>doSomething(); }", " }", " MethodInner myInner = null;", " }", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " <T3,T2> void something(T3 t) { ", " T3 var = t;", " @SuppressWarnings(\"TypeParameterShadowing\")", " class MethodInnerWithGeneric<T> {}", " MethodInnerWithGeneric<T3> innerVar = null;", " class MethodInner {", " @SuppressWarnings(\"TypeParameterShadowing\")", " <T> void doSomething() {}", " void doSomethingElse(T3 t) { this.<T3>doSomething(); }", " }", " MethodInner myInner = null;", " }", "}") .doTest(); } @Test public void refactorCheckForExisting() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " class A<T2,T3,T4> {", " <T> void something(T t) { ", " T var = t;", " }", " }", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " class A<T2,T3,T4> {", " <T5> void something(T5 t) { ", " T5 var = t;", " }", " }", "}") .doTest(); } @Test public void refactorMethodInnerInner() throws Exception { refactoring .addInputLines( "in/Test.java", "package foo.bar;", "class Test<T> {", " static <D> void something(D t) {", " class B {", " class C<T,D> {}", " }", " }", "}") .addOutputLines( "out/Test.java", "package foo.bar;", "class Test<T> {", " static <D> void something(D t) {", " class B {", // T isn't accessible to the inner since the method is static " class C<T,D2> {}", " }", " }", "}") .doTest(); } // don't try to read type parameters off the enclosing local variable declaration @Test public void symbolWithoutTypeParameters() { compilationHelper .addSourceLines( "Test.java", "package foo.bar;", "import java.util.Map;", "import java.util.Comparator;", "class Test {", " static Comparator<Map.Entry<Integer, String>> ENTRY_COMPARATOR =", " new Comparator<Map.Entry<Integer, String>>() {", " public int compare(", " Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {", " return 0;", " }", " private <T extends Comparable> int c(T o1, T o2) {", " return 0;", " }", " };", "}") .doTest(); } @Test public void lambdaParameterDesugaring() throws Exception { refactoring .addInputLines( "in/A.java", "import java.util.function.Consumer;", "class A<T> {", " abstract class B<T> {", " void f() {", " g(t -> {});", " }", " abstract void g(Consumer<T> c);", " }", "}") .addOutputLines( "out/A.java", "import java.util.function.Consumer;", "class A<T> {", " abstract class B<T2> {", " void f() {", " g(t -> {});", " }", " abstract void g(Consumer<T2> c);", " }", "}") .doTest(); } @Test public void typesWithBounds() throws Exception { refactoring .addInputLines( "in/Test.java", "import java.util.function.Predicate;", "class Test<T> {", " <B extends Object & Comparable> void something(B b) {", " class Foo<B extends Object & Comparable> implements Predicate<B> {", " public boolean test(B b) { return false; }", " }", " new Foo<>();", " }", "}") .addOutputLines( "out/Test.java", "import java.util.function.Predicate;", "class Test<T> {", " <B extends Object & Comparable> void something(B b) {", " class Foo<B2 extends Object & Comparable> implements Predicate<B2> {", " public boolean test(B2 b) { return false; }", " }", " new Foo<>();", " }", "}") .doTest(); } }