/*
* Copyright 2015 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.matchers;
import static com.google.errorprone.BugPattern.Category.ONE_OFF;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.Matchers.inLoop;
import static com.google.errorprone.matchers.Matchers.isPrimitiveOrVoidType;
import static com.google.errorprone.matchers.Matchers.isPrimitiveType;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.isVoidType;
import static com.google.errorprone.matchers.Matchers.methodReturns;
import static com.google.errorprone.suppliers.Suppliers.typeFromString;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.MatcherChecker;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.scanner.ErrorProneScanner;
import com.google.errorprone.scanner.ScannerSupplier;
import com.sun.source.tree.MethodTree;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Unit tests for {@link Matchers}.
*/
@RunWith(JUnit4.class)
public class MatchersTest {
private CompilationTestHelper compilationHelper;
@Before
public void setUp() {
compilationHelper = CompilationTestHelper.newInstance(InLoopChecker.class, getClass());
}
@Test
public void inLoopShouldMatchInWhileLoop() {
compilationHelper
.addSourceLines("Test.java",
"public class Test {",
" public void doIt() {",
" while (true) {",
" // BUG: Diagnostic contains:",
" System.out.println();",
" }",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldMatchInDoLoop() {
compilationHelper
.addSourceLines("Test.java",
"public class Test {",
" public void doIt() {",
" do {",
" // BUG: Diagnostic contains:",
" System.out.println();",
" } while (true);",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldMatchInForLoop() {
compilationHelper
.addSourceLines("Test.java",
"public class Test {",
" public void doIt() {",
" for (; true; ) {",
" // BUG: Diagnostic contains:",
" System.out.println();",
" }",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldMatchInEnhancedForLoop() {
compilationHelper
.addSourceLines("Test.java",
"import java.util.List;",
"public class Test {",
" public void doIt(List<String> strings) {",
" for (String s : strings) {",
" // BUG: Diagnostic contains:",
" System.out.println();",
" }",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldNotMatchInInitializerWithoutLoop() {
compilationHelper
.addSourceLines("Test.java",
"import java.util.List;",
"public class Test {",
" {",
" System.out.println();",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldMatchInInitializerInLoop() {
compilationHelper
.addSourceLines("Test.java",
"import java.util.List;",
"public class Test {",
" {",
" int count = 0;",
" while (count < 10) {",
" // BUG: Diagnostic contains:",
" System.out.println();",
" }",
" }",
"}")
.doTest();
}
@Test
public void inLoopShouldNotMatchInAnonymousInnerClassDefinedInLoop() {
compilationHelper
.addSourceLines("Test.java",
"import java.util.*;",
"public class Test {",
" public void sort(List<List<String>> stringLists) {",
" for (List<String> stringList : stringLists) {",
" Collections.sort(stringList, new Comparator<String>() {",
" {",
" System.out.println();",
" }",
" public int compare(String s1, String s2) {",
" return 0;",
" }",
" public boolean equals(Object obj) {",
" return false;",
" }",
" });",
" }",
" }",
"}")
.doTest();
}
@Test
public void methodWithClassAndName() {
Matcher<MethodTree> matcher =
Matchers.methodWithClassAndName("com.google.errorprone.foo.bar.Test", "myMethod");
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"com/google/errorprone/foo/bar/Test.java",
"package com.google.errorprone.foo.bar;",
"public class Test {",
" // BUG: Diagnostic contains:",
" public void myMethod() {}",
"}")
.doTest();
}
@Test
public void methodReturnsSubtype() {
Matcher<MethodTree> matcher = methodReturns(isSubtypeOf("java.util.List"));
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsSubtypeTest.java",
"package test;",
"public class MethodReturnsSubtypeTest {",
" // BUG: Diagnostic contains:",
" public java.util.ArrayList<String> matches() {",
" return null;",
" }",
" public java.util.HashSet<String> doesntMatch() {",
" return null;",
" }",
"}")
.doTest();
}
@Test
public void methodReturnsType() {
Matcher<MethodTree> matcher = methodReturns(typeFromString("java.lang.Number"));
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsSubtypeTest.java",
"package test;",
"public class MethodReturnsSubtypeTest {",
" public java.lang.Integer doesntMatch() {",
" return 0;",
" }",
" // BUG: Diagnostic contains:",
" public java.lang.Number matches() {",
" return 0;",
" }",
"}")
.doTest();
}
@Test
public void methodReturnsPrimitiveType() {
Matcher<MethodTree> matcher = methodReturns(isPrimitiveType());
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsPrimitiveTypeTest.java",
"package test;",
"public class MethodReturnsPrimitiveTypeTest {",
" // BUG: Diagnostic contains:",
" public boolean matches1() {",
" return false;",
" }",
" public void doesntMatch() {",
" }",
" // BUG: Diagnostic contains:",
" public int matches2() {",
" return 42;",
" }",
"}")
.doTest();
}
@Test
public void methodReturnsVoidType() {
Matcher<MethodTree> matcher =
Matchers.allOf(methodReturns(typeFromString("void")), methodReturns(isVoidType()));
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsVoidTypeTest.java",
"package test;",
"public class MethodReturnsVoidTypeTest {",
" public boolean doesntMatch1() {",
" return true;",
" }",
" public Object doesntMatch2() {",
" return new Integer(42);",
" }",
" // BUG: Diagnostic contains:",
" public void matches() {",
" System.out.println(42);",
" }",
"}")
.doTest();
}
@Test
public void methodReturnsPrimitiveOrVoidType() {
Matcher<MethodTree> matcher = methodReturns(isPrimitiveOrVoidType());
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsPrimitiveOrVoidTypeTest.java",
"package test;",
"public class MethodReturnsPrimitiveOrVoidTypeTest {",
" public Object doesntMatch() {",
" return null;",
" }",
" // BUG: Diagnostic contains:",
" public boolean doesMatch1() {",
" return true;",
" }",
" // BUG: Diagnostic contains:",
" public void doesMatch2() {",
" }",
" // BUG: Diagnostic contains:",
" public double doesMatch3() {",
" return 42.0;",
" }",
"}")
.doTest();
}
@Test
public void methodReturnsNonPrimitiveType() {
Matcher<MethodTree> matcher = Matchers.methodReturnsNonPrimitiveType();
CompilationTestHelper.newInstance(methodTreeCheckerSupplier(matcher), getClass())
.addSourceLines(
"test/MethodReturnsSubtypeTest.java",
"package test;",
"public class MethodReturnsSubtypeTest {",
" public int doesntMatch() {",
" return 0;",
" }",
" public void doesntMatch2() {",
" }",
" // BUG: Diagnostic contains:",
" public String matches() {",
" return \"\";",
" }",
"}")
.doTest();
}
@BugPattern(
name = "InLoopChecker",
summary = "Checker that flags the given expression statement if the given matcher matches",
category = ONE_OFF,
severity = ERROR
)
public static class InLoopChecker extends MatcherChecker {
public InLoopChecker() {
super("System.out.println();", inLoop());
}
}
private static ScannerSupplier methodTreeCheckerSupplier(Matcher<MethodTree> matcher) {
return ScannerSupplier.fromScanner(new ErrorProneScanner(new MethodTreeChecker(matcher)));
}
@BugPattern(
name = "MethodTreeChecker",
summary = "Checker that flags the given method declaration if the given matcher matches",
category = ONE_OFF,
severity = ERROR
)
static class MethodTreeChecker extends BugChecker
implements MethodTreeMatcher {
private final Matcher<MethodTree> matcher;
public MethodTreeChecker(Matcher<MethodTree> matcher) {
this.matcher = matcher;
}
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
return matcher.matches(tree, state) ? describeMatch(tree) : Description.NO_MATCH;
}
}
}