All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.errorprone.bugpatterns.inject.dagger.Util Maven / Gradle / Ivy
/*
* Copyright 2016 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.inject.dagger;
import static com.google.common.collect.Iterables.transform;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.constructor;
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
import static com.google.errorprone.matchers.Matchers.hasArgumentWithValue;
import static com.google.errorprone.matchers.Matchers.hasMethod;
import static com.google.errorprone.matchers.Matchers.hasModifier;
import static com.google.errorprone.matchers.Matchers.isType;
import static com.google.errorprone.matchers.Matchers.kindIs;
import static com.google.errorprone.matchers.Matchers.not;
import static com.google.errorprone.util.ASTHelpers.createPrivateConstructor;
import static com.google.errorprone.util.ASTHelpers.getSymbol;
import static com.google.errorprone.util.ASTHelpers.isGeneratedConstructor;
import static com.sun.source.tree.Tree.Kind.INTERFACE;
import static com.sun.source.tree.Tree.Kind.METHOD;
import static java.util.Arrays.asList;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.STATIC;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.matchers.MultiMatcher;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import java.util.EnumSet;
import java.util.Set;
import javax.lang.model.element.Modifier;
/** Matchers and utilities useful to Dagger bug checkers. */
final class Util {
private Util() {}
static final Matcher ANNOTATED_WITH_PRODUCES_OR_PROVIDES =
anyOf(hasAnnotation("dagger.Provides"), hasAnnotation("dagger.producers.Produces"));
static final Matcher ANNOTATED_WITH_MULTIBINDING_ANNOTATION =
anyOf(
hasAnnotation("dagger.multibindings.IntoSet"),
hasAnnotation("dagger.multibindings.ElementsIntoSet"),
hasAnnotation("dagger.multibindings.IntoMap"));
/**
* Matches Dagger 2 {@linkplain dagger.Module modules} and {@linkplain
* dagger.producers.ProducersModule producer modules}.
*/
static final Matcher IS_DAGGER_2_MODULE =
annotations(
AT_LEAST_ONE,
anyOf(
allOf(
isType("dagger.Module"),
not(
hasAnyParameter(
"injects",
"staticInjections",
"overrides",
"addsTo",
"complete",
"library"))),
isType("dagger.producers.ProducerModule")));
/** Matches an annotation that has an argument for at least one of the given parameters. */
private static Matcher hasAnyParameter(String... parameters) {
return anyOf(
transform(
asList(parameters),
new Function>() {
@Override
public Matcher apply(String parameter) {
return hasArgumentWithValue(parameter, Matchers.anything());
}
}));
}
private static final Matcher CLASS_EXTENDS_NOTHING =
new Matcher() {
@Override
public boolean matches(ClassTree t, VisitorState state) {
return t.getExtendsClause() == null;
}
};
/**
* Matches Dagger 2 {@linkplain dagger.Module modules} and {@linkplain
* dagger.producers.ProducersModule producer modules} that could contain abstract binding methods.
*
*
* an interface or a class with no superclass
* no instance {@link dagger.Provides} or {@link dagger.producers.Produces} methods
*
*/
static final Matcher CAN_HAVE_ABSTRACT_BINDING_METHODS =
allOf(
IS_DAGGER_2_MODULE,
anyOf(kindIs(INTERFACE), CLASS_EXTENDS_NOTHING),
not(
hasMethod(
Matchers.allOf(
ANNOTATED_WITH_PRODUCES_OR_PROVIDES, not(hasModifier(STATIC))))));
/** Returns the annotation on {@code classTree} whose type's FQCN is {@code annotationName}. */
static Optional findAnnotation(String annotationName, ClassTree classTree) {
for (AnnotationTree annotationTree : classTree.getModifiers().getAnnotations()) {
ClassSymbol annotationClass = (ClassSymbol) getSymbol(annotationTree.getAnnotationType());
if (annotationClass.fullname.contentEquals(annotationName)) {
return Optional.of(annotationTree);
}
}
return Optional.absent();
}
private static final MultiMatcher HAS_GENERATED_CONSTRUCTOR =
constructor(
AT_LEAST_ONE,
new Matcher() {
@Override
public boolean matches(MethodTree t, VisitorState state) {
return isGeneratedConstructor(t);
}
});
/**
* Returns a fix that changes a concrete class to an abstract class.
*
*
* Removes {@code final} if it was there.
* Adds {@code abstract} if it wasn't there.
* Adds a private empty constructor if the class was {@code final} and had only a default
* constructor.
*
*/
static SuggestedFix.Builder makeConcreteClassAbstract(ClassTree classTree, VisitorState state) {
Set flags = EnumSet.noneOf(Modifier.class);
flags.addAll(classTree.getModifiers().getFlags());
boolean wasFinal = flags.remove(FINAL);
boolean wasAbstract = !flags.add(ABSTRACT);
if (classTree.getKind().equals(INTERFACE) || (!wasFinal && wasAbstract)) {
return SuggestedFix.builder(); // no-op
}
ImmutableList.Builder modifiers = ImmutableList.builder();
for (AnnotationTree annotation : classTree.getModifiers().getAnnotations()) {
modifiers.add(state.getSourceForNode(annotation));
}
modifiers.addAll(flags);
SuggestedFix.Builder makeAbstract = SuggestedFix.builder();
if (((JCModifiers) classTree.getModifiers()).pos == -1) {
makeAbstract.prefixWith(classTree, Joiner.on(' ').join(modifiers.build()));
} else {
makeAbstract.replace(classTree.getModifiers(), Joiner.on(' ').join(modifiers.build()));
}
if (wasFinal && HAS_GENERATED_CONSTRUCTOR.matches(classTree, state)) {
makeAbstract.merge(addPrivateConstructor(classTree));
}
return makeAbstract;
}
// TODO(dpb): Account for indentation level.
private static SuggestedFix.Builder addPrivateConstructor(ClassTree classTree) {
SuggestedFix.Builder fix = SuggestedFix.builder();
String indent = " ";
for (Tree member : classTree.getMembers()) {
if (member.getKind().equals(METHOD) && !isGeneratedConstructor((MethodTree) member)) {
fix.prefixWith(
member, indent + createPrivateConstructor(classTree) + " // no instances\n" + indent);
break;
}
if (!member.getKind().equals(METHOD)) {
indent = "";
}
}
return fix;
}
}