use of javax.lang.model.element.ElementKind.CONSTRUCTOR in project error-prone by google.
the class UnusedMethod method matchCompilationUnit.
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
// Map of symbols to method declarations. Initially this is a map of all of the methods. As we
// go we remove those variables which are used.
Map<Symbol, TreePath> unusedMethods = new HashMap<>();
// Use a TreeScanner to find all local variables and fields.
if (hasNativeMethods(tree)) {
return Description.NO_MATCH;
}
AtomicBoolean ignoreUnusedMethods = new AtomicBoolean(false);
class MethodFinder extends SuppressibleTreePathScanner<Void, Void> {
MethodFinder(VisitorState state) {
super(state);
}
@Override
public Void visitClass(ClassTree tree, Void unused) {
if (exemptedBySuperType(getType(tree), state)) {
return null;
}
return super.visitClass(tree, null);
}
private boolean exemptedBySuperType(Type type, VisitorState state) {
return EXEMPTING_SUPER_TYPES.stream().anyMatch(t -> isSubtype(type, typeFromString(t).get(state), state));
}
@Override
public Void visitMethod(MethodTree tree, Void unused) {
if (hasJUnitParamsParametersForMethodAnnotation(tree.getModifiers().getAnnotations())) {
// Since this method uses @Parameters, there will be another method that appears to
// be unused. Don't warn about unusedMethods at all in this case.
ignoreUnusedMethods.set(true);
}
if (isMethodSymbolEligibleForChecking(tree)) {
unusedMethods.put(getSymbol(tree), getCurrentPath());
}
return super.visitMethod(tree, unused);
}
private boolean hasJUnitParamsParametersForMethodAnnotation(Collection<? extends AnnotationTree> annotations) {
for (AnnotationTree tree : annotations) {
JCAnnotation annotation = (JCAnnotation) tree;
if (annotation.getAnnotationType().type != null && annotation.getAnnotationType().type.toString().equals(JUNIT_PARAMS_ANNOTATION_TYPE)) {
if (annotation.getArguments().isEmpty()) {
// @Parameters, which uses implicit provider methods
return true;
}
for (JCExpression arg : annotation.getArguments()) {
if (arg.getKind() != Kind.ASSIGNMENT) {
// Implicit value annotation, e.g. @Parameters({"1"}); no exemption required.
return false;
}
JCExpression var = ((JCAssign) arg).getVariable();
if (var.getKind() == Kind.IDENTIFIER) {
// @Parameters(source = ...) or @Parameters(method = ...)
if (!((IdentifierTree) var).getName().contentEquals(JUNIT_PARAMS_VALUE)) {
return true;
}
}
}
}
}
return false;
}
private boolean isMethodSymbolEligibleForChecking(MethodTree tree) {
if (exemptedByName(tree.getName())) {
return false;
}
// Assume the method is called if annotated with a called-reflectively annotation.
if (exemptedByAnnotation(tree.getModifiers().getAnnotations())) {
return false;
}
if (shouldKeep(tree)) {
return false;
}
MethodSymbol methodSymbol = getSymbol(tree);
if (isExemptedConstructor(methodSymbol, state) || isGeneratedConstructor(tree) || SERIALIZATION_METHODS.matches(tree, state)) {
return false;
}
// Ignore this method if the last parameter is a GWT JavaScriptObject.
if (!tree.getParameters().isEmpty()) {
Type lastParamType = getType(getLast(tree.getParameters()));
if (lastParamType != null && lastParamType.toString().equals(GWT_JAVASCRIPT_OBJECT)) {
return false;
}
}
return canBeRemoved(methodSymbol, state);
}
private boolean isExemptedConstructor(MethodSymbol methodSymbol, VisitorState state) {
if (!methodSymbol.getKind().equals(CONSTRUCTOR)) {
return false;
}
// instantiating the class at all (e.g. in utility classes).
if (methodSymbol.params().isEmpty()) {
return true;
}
return false;
}
}
new MethodFinder(state).scan(state.getPath(), null);
class FilterUsedMethods extends TreePathScanner<Void, Void> {
@Override
public Void visitMemberSelect(MemberSelectTree memberSelectTree, Void unused) {
Symbol symbol = getSymbol(memberSelectTree);
unusedMethods.remove(symbol);
return super.visitMemberSelect(memberSelectTree, null);
}
@Override
public Void visitMemberReference(MemberReferenceTree tree, Void unused) {
super.visitMemberReference(tree, null);
MethodSymbol symbol = getSymbol(tree);
unusedMethods.remove(symbol);
symbol.getParameters().forEach(unusedMethods::remove);
return null;
}
@Override
public Void visitMethodInvocation(MethodInvocationTree tree, Void unused) {
handle(getSymbol(tree));
return super.visitMethodInvocation(tree, null);
}
@Override
public Void visitNewClass(NewClassTree tree, Void unused) {
handle(getSymbol(tree));
return super.visitNewClass(tree, null);
}
@Override
public Void visitAssignment(AssignmentTree tree, Void unused) {
handle(getSymbol(tree.getVariable()));
return super.visitAssignment(tree, unused);
}
private void handle(Symbol symbol) {
if (symbol instanceof MethodSymbol) {
unusedMethods.remove(symbol);
}
}
@Override
public Void visitMethod(MethodTree tree, Void unused) {
handleMethodSource(tree);
return super.visitMethod(tree, null);
}
/**
* If a method is annotated with @MethodSource, the annotation value refers to another method
* that is used reflectively to supply test parameters, so that method should not be
* considered unused.
*/
private void handleMethodSource(MethodTree tree) {
MethodSymbol sym = getSymbol(tree);
Name name = ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHODSOURCE.get(state);
sym.getRawAttributes().stream().filter(a -> a.type.tsym.getQualifiedName().equals(name)).findAny().flatMap(a -> getAnnotationValue(a, "value")).map(y -> asStrings(y).map(state::getName).map(Name::toString).collect(toImmutableSet())).ifPresent(referencedNames -> unusedMethods.entrySet().removeIf(e -> {
Symbol unusedSym = e.getKey();
String simpleName = unusedSym.getSimpleName().toString();
return referencedNames.contains(simpleName) || referencedNames.contains(unusedSym.owner.getQualifiedName() + "#" + simpleName);
}));
}
}
new FilterUsedMethods().scan(state.getPath(), null);
if (ignoreUnusedMethods.get()) {
return Description.NO_MATCH;
}
fixNonConstructors(unusedMethods.values().stream().filter(t -> !getSymbol(t.getLeaf()).isConstructor()).collect(toImmutableList()), state);
// Group unused constructors by the owning class to generate fixes, so that if we remove the
// last constructor, we add a private one.
ImmutableListMultimap<Symbol, TreePath> unusedConstructors = unusedMethods.values().stream().filter(t -> getSymbol(t.getLeaf()).isConstructor()).collect(toImmutableListMultimap(t -> getSymbol(t.getLeaf()).owner, t -> t));
fixConstructors(unusedConstructors, state);
return Description.NO_MATCH;
}
use of javax.lang.model.element.ElementKind.CONSTRUCTOR in project error-prone by google.
the class UnusedMethod method fixConstructors.
private void fixConstructors(ImmutableListMultimap<Symbol, TreePath> unusedConstructors, VisitorState state) {
for (Map.Entry<Symbol, List<TreePath>> entry : asMap(unusedConstructors).entrySet()) {
Symbol symbol = entry.getKey();
List<TreePath> trees = entry.getValue();
SuggestedFix.Builder fix = SuggestedFix.builder();
int constructorCount = size(scope(symbol.members()).getSymbols(Symbol::isConstructor));
int finalFields = size(scope(symbol.members()).getSymbols(s -> s.getKind().equals(FIELD) && s.getModifiers().contains(FINAL)));
boolean fixable;
if (constructorCount == trees.size()) {
fix.postfixWith(getLast(trees).getLeaf(), format("private %s() {}", symbol.getSimpleName()));
fixable = finalFields == 0;
} else {
fixable = true;
}
String message = String.format("Constructor '%s' is never used.", symbol.getSimpleName());
trees.forEach(t -> fix.merge(replaceIncludingComments(t, "", state)));
state.reportMatch(buildDescription(trees.get(0).getLeaf()).addFix(fixable ? fix.build() : emptyFix()).setMessage(message).build());
}
}
Aggregations