All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.errorprone.bugpatterns.AbstractJUnit4InitMethodNotRun Maven / Gradle / Ivy

There is a newer version: 2.28.0
Show newest version
/*
 * Copyright 2014 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.errorprone.matchers.JUnitMatchers.isJUnit4TestClass;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.enclosingClass;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import static com.google.errorprone.matchers.Matchers.hasAnnotationOnAnyOverriddenMethod;
import static com.google.errorprone.matchers.Matchers.not;

import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
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.AnnotationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.tools.javac.code.Symbol;
import java.io.Serializable;
import java.util.List;
import javax.lang.model.element.Modifier;

/**
 * Base class for JUnit4SetUp/TearDown not run. This will take care of the nitty-gritty about
 * replacing @After with @Before, adding @Before on unannotated methods, making them public if
 * necessary, fixing the imports of other @Before, etc.
 *
 * @author [email protected]
 */
abstract class AbstractJUnit4InitMethodNotRun extends BugChecker implements MethodTreeMatcher {

  private static final String JUNIT_TEST = "org.junit.Test";

  /**
   * Returns a matcher that selects which methods this matcher applies to (e.g. public void setUp()
   * without @Before/@BeforeClass annotation)
   */
  protected abstract Matcher methodMatcher();

  /**
   * Returns the fully qualified class name of the annotation this bugpattern should apply to
   * matched methods.
   *
   * 

If another annotation is on the method that has the same name, the import will be replaced * with the appropriate one (e.g.: com.example.Before becomes org.junit.Before) */ protected abstract String correctAnnotation(); /** * Returns a collection of 'before-and-after' pairs of annotations that should be replaced on * these methods. * *

If this method matcher finds a method annotated with {@link * AnnotationReplacements#badAnnotation}, instead of applying {@link #correctAnnotation()}, * instead replace it with {@link AnnotationReplacements#goodAnnotation} */ protected abstract List annotationReplacements(); /** * Matches if all of the following conditions are true: 1) The method matches {@link * #methodMatcher()}, (looks like setUp() or tearDown(), and none of the overrides in the * hierarchy of the method have the appropriate @Before or @After annotations) 2) The method is * not annotated with @Test 3) The enclosing class has an @RunWith annotation and does not extend * TestCase. This marks that the test is intended to run with JUnit 4. */ @Override public Description matchMethod(MethodTree methodTree, VisitorState state) { boolean matches = allOf( methodMatcher(), not(hasAnnotationOnAnyOverriddenMethod(JUNIT_TEST)), enclosingClass(isJUnit4TestClass)) .matches(methodTree, state); if (!matches) { return Description.NO_MATCH; } // For each annotationReplacement, replace the first annotation that matches. If any of them // matches, don't try and do the rest of the work. Description description; for (AnnotationReplacements replacement : annotationReplacements()) { description = tryToReplaceAnnotation( methodTree, state, replacement.badAnnotation, replacement.goodAnnotation); if (description != null) { return description; } } // Search for another @Before annotation on the method and replace the import // if we find one String correctAnnotation = correctAnnotation(); String unqualifiedClassName = getUnqualifiedClassName(correctAnnotation); for (AnnotationTree annotationNode : methodTree.getModifiers().getAnnotations()) { Symbol annoSymbol = ASTHelpers.getSymbol(annotationNode); if (annoSymbol.getSimpleName().contentEquals(unqualifiedClassName)) { SuggestedFix.Builder suggestedFix = SuggestedFix.builder() .removeImport(annoSymbol.getQualifiedName().toString()) .addImport(correctAnnotation); makeProtectedPublic(methodTree, state, suggestedFix); return describeMatch(annotationNode, suggestedFix.build()); } } // Add correctAnnotation() to the unannotated method // (and convert protected to public if it is) SuggestedFix.Builder suggestedFix = SuggestedFix.builder().addImport(correctAnnotation); makeProtectedPublic(methodTree, state, suggestedFix); suggestedFix.prefixWith(methodTree, "@" + unqualifiedClassName + "\n"); return describeMatch(methodTree, suggestedFix.build()); } private static void makeProtectedPublic( MethodTree methodTree, VisitorState state, SuggestedFix.Builder suggestedFix) { if (Matchers.hasModifier(Modifier.PROTECTED).matches(methodTree, state)) { ModifiersTree modifiers = methodTree.getModifiers(); CharSequence modifiersSource = state.getSourceForNode(modifiers); suggestedFix.replace( modifiers, modifiersSource.toString().replaceFirst("protected", "public")); } } private Description tryToReplaceAnnotation( MethodTree methodTree, VisitorState state, String badAnnotation, String goodAnnotation) { String finalName = getUnqualifiedClassName(goodAnnotation); if (hasAnnotation(badAnnotation).matches(methodTree, state)) { AnnotationTree annotationTree = findAnnotation(methodTree, state, badAnnotation); return describeMatch( annotationTree, SuggestedFix.builder() .addImport(goodAnnotation) .replace(annotationTree, "@" + finalName) .build()); } else { return null; } } private static String getUnqualifiedClassName(String goodAnnotation) { return goodAnnotation.substring(goodAnnotation.lastIndexOf(".") + 1); } private static AnnotationTree findAnnotation( MethodTree methodTree, VisitorState state, String annotationName) { AnnotationTree annotationNode = null; for (AnnotationTree annotation : methodTree.getModifiers().getAnnotations()) { if (ASTHelpers.getSymbol(annotation).equals(state.getSymbolFromString(annotationName))) { annotationNode = annotation; } } return annotationNode; } protected static class AnnotationReplacements implements Serializable { private final String goodAnnotation; private final String badAnnotation; protected AnnotationReplacements(String badAnnotation, String goodAnnotation) { this.goodAnnotation = goodAnnotation; this.badAnnotation = badAnnotation; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy