diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/DoNotUseRuleChain.java b/core/src/main/java/com/google/errorprone/bugpatterns/DoNotUseRuleChain.java deleted file mode 100644 index aac2bb4dd0f..00000000000 --- a/core/src/main/java/com/google/errorprone/bugpatterns/DoNotUseRuleChain.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2023 The Error Prone Authors. - * - * 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 static com.google.common.collect.Streams.stream; -import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; -import static com.google.errorprone.matchers.Matchers.anyMethod; -import static java.util.stream.Collectors.joining; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.errorprone.BugPattern; -import com.google.errorprone.BugPattern.StandardTags; -import com.google.errorprone.VisitorState; -import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher; -import com.google.errorprone.fixes.SuggestedFix; -import com.google.errorprone.matchers.Description; -import com.google.errorprone.matchers.Matcher; -import com.google.errorprone.matchers.Matchers; -import com.google.errorprone.util.ASTHelpers; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.MethodInvocationTree; -import com.sun.source.tree.Tree.Kind; -import com.sun.source.tree.VariableTree; -import com.sun.tools.javac.code.Symbol.VarSymbol; -import com.sun.tools.javac.code.Type; -import java.util.List; -import java.util.Optional; -import javax.lang.model.element.ElementKind; - -/** - * Identifies {@code RuleChain} class fields and proposes refactoring to ordered {@code @Rule(order - * = n)}. - */ -@BugPattern( - tags = {StandardTags.REFACTORING}, - summary = - "Prefer using `@Rule` with an explicit order over declaring a `RuleChain`. RuleChain was" - + " the only way to declare ordered rules before JUnit 4.13. Newer versions should use" - + " the cleaner individual `@Rule(order = n)` option. The rules with a higher value are" - + " inner.", - severity = WARNING) -public class DoNotUseRuleChain extends BugChecker implements VariableTreeMatcher { - - private static final String TEST_RULE_VAR_PREFIX = "testRule"; - - private static final String JUNIT_RULE_CHAIN_IMPORT_PATH = "org.junit.rules.RuleChain"; - private static final String JUNIT_CLASS_RULE_IMPORT_PATH = "org.junit.ClassRule"; - private static final String JUNIT_RULE_IMPORT_PATH = "org.junit.Rule"; - - private static final Matcher RULE_CHAIN_METHOD_MATCHER = - anyMethod().onDescendantOf(JUNIT_RULE_CHAIN_IMPORT_PATH).namedAnyOf("around", "outerRule"); - - @Override - public Description matchVariable(VariableTree tree, VisitorState state) { - VarSymbol symbol = ASTHelpers.getSymbol(tree); - if (symbol.getKind() != ElementKind.FIELD - || !isRuleChainExpression(tree, state) - || !isClassWithSingleRule(state) - || isChainedRuleChain(tree, state)) { - return Description.NO_MATCH; - } - return describeMatch(tree, refactor(tree, state)); - } - - private static boolean isRuleChainExpression(VariableTree tree, VisitorState state) { - ExpressionTree expression = tree.getInitializer(); - if (expression == null) { - return false; - } - return RULE_CHAIN_METHOD_MATCHER.matches(expression, state); - } - - /** - * This refactoring only matches classes that feature a single occurrence of a {@code @Rule}. This - * is done to avoid breaking some edge cases where more than one RuleChain might be declared or a - * mixture of {@code @Rule} and {@code @RuleChain}. - */ - private static boolean isClassWithSingleRule(VisitorState state) { - Optional classTree = getClassTree(state); - if (classTree.isEmpty()) { - return false; - } - return classTree.get().getMembers().stream() - .filter(tree -> ASTHelpers.hasAnnotation(tree, JUNIT_RULE_IMPORT_PATH, state)) - .count() - == 1; - } - - private static Optional getClassTree(VisitorState state) { - return stream(state.getPath().iterator()) - .filter(parent -> parent.getKind() == Kind.CLASS) - .map(ClassTree.class::cast) - .findFirst(); - } - - /** - * Don't evaluate if there is a {@code RuleChain} expression inside a {@code - * RuleChain.outerRule()} or {@code RuleChain.around()} method. - */ - private static boolean isChainedRuleChain(VariableTree tree, VisitorState state) { - return getOrderedExpressions(tree, state).stream() - .map(DoNotUseRuleChain::getArgumentExpression) - .anyMatch(ex -> Matchers.isSameType(JUNIT_RULE_CHAIN_IMPORT_PATH).matches(ex, state)); - } - - private static SuggestedFix refactor(VariableTree tree, VisitorState state) { - SuggestedFix.Builder fix = SuggestedFix.builder(); - ImmutableList expressions = getOrderedExpressions(tree, state); - - String replacement = ""; - for (int i = 0; i < expressions.size(); i++) { - ExpressionTree expression = getArgumentExpression(expressions.get(i)); - addImportIfNecessary(expression, fix); - replacement += extractRuleFromExpression(expression, i, tree, state); - } - - fix.removeImport(JUNIT_RULE_CHAIN_IMPORT_PATH); - fix.replace(tree, replacement); - return fix.build(); - } - - /** - * Since {@code ASTHelpers.getReceiver()} goes into reverse order, the expression list must be - * reversed in order for them to follow the required ordering from {@code @Rule(order = n)}. - */ - private static ImmutableList getOrderedExpressions( - VariableTree tree, VisitorState state) { - ImmutableList.Builder expressions = ImmutableList.builder(); - for (ExpressionTree ex = tree.getInitializer(); - RULE_CHAIN_METHOD_MATCHER.matches(ex, state); - ex = ASTHelpers.getReceiver(ex)) { - expressions.add(ex); - } - return expressions.build().reverse(); - } - - // Gets the only argument from {@code RuleChain.outerRule()} or {@code RuleChain.around()} to use - // as the variable value from the new ordered {@code @Rule(order = n)} - private static ExpressionTree getArgumentExpression(ExpressionTree ex) { - MethodInvocationTree methodInvocation = (MethodInvocationTree) ex; - return methodInvocation.getArguments().get(0); - } - - private static void addImportIfNecessary(ExpressionTree expression, SuggestedFix.Builder fix) { - Type originalType = ASTHelpers.getResultType(expression); - if (ImmutableSet.of(Kind.METHOD_INVOCATION, Kind.LAMBDA_EXPRESSION) - .contains(expression.getKind())) { - fix.addImport(originalType.tsym.getQualifiedName().toString()); - } - } - - private static String extractRuleFromExpression( - ExpressionTree expression, int order, VariableTree tree, VisitorState state) { - String className = className(expression); - - return String.format( - "%s(order = %d)\npublic %sfinal %s %s = %s;\n", - annotationName(tree, state), - order, - ASTHelpers.getSymbol(tree).isStatic() ? "static " : "", - className, - classToVariableName(className), - state.getSourceForNode(expression)); - } - - private static String className(ExpressionTree expression) { - Type originalType = ASTHelpers.getResultType(expression); - List arguments = originalType.getTypeArguments(); - String className = originalType.tsym.getSimpleName().toString(); - if (!arguments.isEmpty()) { - String argumentString = - arguments.stream().map(t -> t.tsym.getSimpleName().toString()).collect(joining(", ")); - return String.format("%s<%s>", className, argumentString); - } - return className; - } - - private static String annotationName(VariableTree tree, VisitorState state) { - if (ASTHelpers.hasAnnotation(tree, JUNIT_CLASS_RULE_IMPORT_PATH, state)) { - return "@ClassRule"; - } - return "@Rule"; - } - - private static String classToVariableName(String className) { - return String.format("%s%s", TEST_RULE_VAR_PREFIX, className) - .replace("<", "") - .replace(">", "") - .replace(",", "") - .replace(" ", ""); - } -} diff --git a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java index a01309f8afc..f9b7bed5964 100644 --- a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java +++ b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java @@ -120,7 +120,6 @@ import com.google.errorprone.bugpatterns.DoNotClaimAnnotations; import com.google.errorprone.bugpatterns.DoNotMockAutoValue; import com.google.errorprone.bugpatterns.DoNotMockChecker; -import com.google.errorprone.bugpatterns.DoNotUseRuleChain; import com.google.errorprone.bugpatterns.DoubleBraceInitialization; import com.google.errorprone.bugpatterns.DuplicateDateFormatField; import com.google.errorprone.bugpatterns.DuplicateMapKeys; @@ -1163,7 +1162,6 @@ public static ScannerSupplier warningChecks() { DefaultLocale.class, // TODO: enable this by default. DepAnn.class, DifferentNameButSame.class, - DoNotUseRuleChain.class, EmptyIfStatement.class, EqualsBrokenForNull.class, EqualsMissingNullable.class, diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/DoNotUseRuleChainTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/DoNotUseRuleChainTest.java deleted file mode 100644 index 02aab682930..00000000000 --- a/core/src/test/java/com/google/errorprone/bugpatterns/DoNotUseRuleChainTest.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2023 The Error Prone Authors. - * - * 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.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link DoNotUseRuleChain} */ -@RunWith(JUnit4.class) -public class DoNotUseRuleChainTest { - - private final BugCheckerRefactoringTestHelper refactoringHelper = - BugCheckerRefactoringTestHelper.newInstance(DoNotUseRuleChain.class, getClass()); - - private final CompilationTestHelper compilationHelper = - CompilationTestHelper.newInstance(DoNotUseRuleChain.class, getClass()); - - @Test - public void negativeEmptyFile() { - compilationHelper.addSourceLines("EmptyFile.java", "// Empty file").doTest(); - } - - @Test - public void negativeRuleChainNoInitializer() { - compilationHelper - .addSourceLines( - "RuleChainNoInitializer.java", - """ - package rulechainnoinitializer; - - import org.junit.Rule; - import org.junit.rules.RuleChain; - import org.junit.rules.TestRule; - import org.junit.runner.Description; - import org.junit.runners.model.Statement; - - public class RuleChainNoInitializer { - @Rule public RuleChain notInitializedRule; - } - """) - .doTest(); - } - - @Test - public void negativeEmptyRuleChain() { - compilationHelper - .addSourceLines( - "EmptyRuleChain.java", - "package emptyrulechain;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class EmptyRuleChain {", - "@Rule", - "public final RuleChain emptyRuleChain = RuleChain.emptyRuleChain();", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeNullRuleChain() { - compilationHelper - .addSourceLines( - "NullRuleChain.java", - "package nullrulechain;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class NullRuleChain {", - "@Rule", - "public final RuleChain nullRuleChain = null;", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeLocalVariable() { - compilationHelper - .addSourceLines( - "LocalVariable.java", - "package localvariable;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class LocalVariable {", - "public void doNothing() {", - "RuleChain ruleChain = RuleChain.outerRule(new Rule2()).around(new Rule3());", - "}", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeSingleRules() { - compilationHelper - .addSourceLines( - "SingleRule.java", - "package singlerule;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class SingleRule {", - "@Rule", - "public final Rule1 ruleOne = new Rule1(null, null);", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeOrderedRules() { - compilationHelper - .addSourceLines( - "OrderedRules.java", - "package orderedrules;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class OrderedRules {", - "@Rule(order = 0)", - "public final Rule1 ruleOne = new Rule1(\"unused\", new Rule2());", - "@Rule(order = 1)", - "public final Rule2 ruleTwo = new Rule2();", - "@Rule(order = 2)", - "public final Rule3 ruleThree = new Rule3();", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeOrderedRulesClassRules() { - compilationHelper - .addSourceLines( - "OrderedRulesClassRules.java", - "package orderedrulesclassRules;", - "import org.junit.Rule;", - "import org.junit.ClassRule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class OrderedRulesClassRules {", - "@Rule(order = 0)", - "public final Rule1 ruleOne = new Rule1(\"unused\", new Rule2());", - "@Rule(order = 1)", - "public final Rule2 ruleTwo = new Rule2();", - "@ClassRule(order = 0)", - "public static final Rule4 ruleFour = new Rule4();", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeChainedRulesChain() { - compilationHelper - .addSourceLines( - "OrderedRuleChain.java", - "package orderedrulechain;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class OrderedRuleChain {", - "@Rule(order = 0)", - "public final RuleChain orderedRuleChain = RuleChain.outerRule(new Rule3())", - ".around(RuleChain.outerRule(new Rule2()).around(new Rule3()));", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void negativeMultipleRuleChains() { - compilationHelper - .addSourceLines( - "MultipleRuleChains.java", - "package multiplerulechains;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class MultipleRuleChains {", - "public final Rule5 varRule52 = new Rule5();", - "@Rule", - "public final RuleChain ruleChain = RuleChain.outerRule(", - "new Rule1(\"unused\", new Rule2()))", - ".around(new Rule2()).around(new Rule5());", - "@Rule", - "public final RuleChain ruleChain2 = RuleChain.outerRule(new Rule3()).around(", - "varRule52);", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringOrderedRulesChain() { - refactoringHelper - .addInputLines( - "OrderedRuleChain.java", - "package orderedrulechain;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class OrderedRuleChain {", - "@Rule(order = 0)", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public final RuleChain orderedRuleChain = RuleChain.outerRule(new Rule3())", - ".around(new Rule2());", - customRuleClasses(), - "}") - .addOutputLines( - "OrderedRuleChain.java", - "package orderedrulechain;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class OrderedRuleChain {", - "@Rule(order = 0)", - "public final Rule3 testRuleRule3 = new Rule3();", - "@Rule(order = 1)", - "public final Rule2 testRuleRule2 = new Rule2();", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringUnorderedRuleChainWithNewObjects() { - refactoringHelper - .addInputLines( - "UnorderedRuleChainWithNewObjects.java", - "package orderedrulechainwithnewobjects;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithNewObjects {", - "@Rule", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public final RuleChain ruleChain = RuleChain.outerRule(", - "new Rule1(\"really big string so that the new falls into another line\"", - ", new Rule2()))", - ".around(new Rule2()).around(new Rule5()).around((base, description) ->", - "new Statement() {", - "@Override", - "public void evaluate() throws Throwable {", - "// Do nothing", - "}", - "});", - customRuleClasses(), - "}") - .addOutputLines( - "UnorderedRuleChainWithNewObjects.java", - "package orderedrulechainwithnewobjects;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithNewObjects {", - "@Rule(order = 0)", - "public final Rule1 testRuleRule1 =", - "new Rule1(\"really big string so that the new falls into another line\"", - ", new Rule2());", - "@Rule(order = 1)", - "public final Rule2 testRuleRule2 = new Rule2();", - "@Rule(order = 2)", - "public final Rule5 testRuleRule5 = new Rule5();", - "@Rule(order = 3)", - "public final TestRule testRuleTestRule = (base, description) ->", - "new Statement() {", - "@Override", - "public void evaluate() throws Throwable {", - "// Do nothing", - "}", - "};", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringUnorderedRuleChainWithExistingObjects() { - refactoringHelper - .addInputLines( - "UnorderedRuleChainWithExistingObjects.java", - "package orderedrulechainwithexistingobjects;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithExistingObjects {", - "public final Rule1 varRule1 = new Rule1(\"unused\", new Rule2());", - "public final Rule2 varRule2 = new Rule2();", - "@Rule", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public final RuleChain ruleChain = RuleChain.outerRule(varRule1).around(varRule2);", - customRuleClasses(), - "}") - .addOutputLines( - "UnorderedRuleChainWithExistingObjects.java", - "package orderedrulechainwithexistingobjects;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithExistingObjects {", - "public final Rule1 varRule1 = new Rule1(\"unused\", new Rule2());", - "public final Rule2 varRule2 = new Rule2();", - "@Rule(order = 0)", - "public final Rule1 testRuleRule1 = varRule1;", - "@Rule(order = 1)", - "public final Rule2 testRuleRule2 = varRule2;", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringUnorderedRuleChainWithGenericClass() { - refactoringHelper - .addInputLines( - "UnorderedRuleChainWithGenericClass.java", - "package orderedrulechainwithgenericclass;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithGenericClass {", - "public final Rule5 varRule52 = new Rule5();", - "public final Rule6 varRule532 = new Rule6();", - "@Rule", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public final RuleChain ruleChain = RuleChain.outerRule(varRule52).around(varRule532);", - customRuleClasses(), - "}") - .addOutputLines( - "UnorderedRuleChainWithGenericClass.java", - "package orderedrulechainwithgenericclass;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithGenericClass {", - "public final Rule5 varRule52 = new Rule5();", - "public final Rule6 varRule532 = new Rule6();", - "@Rule(order = 0)", - "public final Rule5 testRuleRule5Rule2 = varRule52;", - "@Rule(order = 1)", - "public final Rule6 testRuleRule6Rule3Rule2 = varRule532;", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringUnorderedTwoRuleChainWithClassRule() { - refactoringHelper - .addInputLines( - "UnorderedRuleChainWithClassRule.java", - "package orderedrulechainwithclassrule;", - "import org.junit.ClassRule;", - "import org.junit.Rule;", - "import org.junit.rules.RuleChain;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithClassRule {", - "@Rule", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public final RuleChain ruleChain = RuleChain.outerRule(", - "new Rule2()).around(new Rule3());", - "@ClassRule", - "// BUG: Diagnostic contains: Do not use RuleChain", - "public static final RuleChain classRuleChain = RuleChain.outerRule(new Rule4());", - customRuleClasses(), - "}") - .addOutputLines( - "UnorderedRuleChainWithClassRule.java", - "package orderedrulechainwithclassrule;", - "import org.junit.ClassRule;", - "import org.junit.Rule;", - "import org.junit.rules.TestRule;", - "import org.junit.runner.Description;", - "import org.junit.runners.model.Statement;", - "public class UnorderedRuleChainWithClassRule {", - "@Rule(order = 0)", - "public final Rule2 testRuleRule2 = new Rule2();", - "@Rule(order = 1)", - "public final Rule3 testRuleRule3 = new Rule3();", - "@ClassRule(order = 0)", - "public static final Rule4 testRuleRule4 = new Rule4();", - customRuleClasses(), - "}") - .doTest(); - } - - @Test - public void refactoringUnorderedRuleChainWithoutTestRuleImport() { - refactoringHelper - .addInputLines( - "UnorderedRuleChainWithoutTestRuleImport.java", - """ - package orderedrulechainwithouttestruleimport; - - import org.junit.Rule; - import org.junit.rules.RuleChain; - import org.junit.runners.model.Statement; - - public class UnorderedRuleChainWithoutTestRuleImport { - @Rule - // BUG: Diagnostic contains: Do not use RuleChain - public final RuleChain ruleChain = - RuleChain.outerRule( - (base, description) -> - new Statement() { - @Override - public void evaluate() throws Throwable { - // Do nothing - } - }); - } - """) - .addOutputLines( - "UnorderedRuleChainWithoutTestRuleImport.java", - """ - package orderedrulechainwithouttestruleimport; - - import org.junit.Rule; - import org.junit.rules.TestRule; - import org.junit.runners.model.Statement; - - public class UnorderedRuleChainWithoutTestRuleImport { - @Rule(order = 0) - public final TestRule testRuleTestRule = - (base, description) -> - new Statement() { - @Override - public void evaluate() throws Throwable { - // Do nothing - } - }; - } - """) - .doTest(); - } - - private static String customRuleClasses() { - return "private class BaseCustomRule implements TestRule {\n" - + "@Override\n" - + "public Statement apply(Statement base, Description description) {\n" - + "return new Statement() {\n" - + "@Override\n" - + "public void evaluate() throws Throwable {\n" - + "// Do nothing\n" - + "}\n" - + "};\n" - + "}\n" - + "}\n" - + "private class Rule1 extends BaseCustomRule {\n" - + "private Rule1(String unusedString, TestRule unusedRule) {\n" - + " // Example with parameter\n" - + "}\n" - + "}\n" - + "private class Rule2 extends BaseCustomRule {}\n" - + "private class Rule3 extends BaseCustomRule {}\n" - + "private static class Rule4 implements TestRule {\n" - + "@Override\n" - + "public Statement apply(Statement base, Description description) {\n" - + "return new Statement() {\n" - + "@Override\n" - + "public void evaluate() throws Throwable {\n" - + "// Do nothing\n" - + "}\n" - + "};\n" - + "}\n" - + "}\n" - + "private class Rule5 extends BaseCustomRule {}\n" - + "private class Rule6 extends BaseCustomRule {}\n"; - } -}