/* * Copyright 2014 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.refaster; import com.google.auto.value.AutoValue; import com.google.errorprone.BugPattern.SeverityLevel; import com.google.errorprone.DescriptionListener; import com.google.errorprone.fixes.Fix; import com.google.errorprone.fixes.SuggestedFix; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ClassTree; import com.sun.source.tree.DoWhileLoopTree; import com.sun.source.tree.IfTree; import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.Tree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.util.SimpleTreeVisitor; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; /** * Scanner that outputs suggested fixes generated by a {@code RefasterMatcher}. * * @author lowasser@google.com (Louis Wasserman) */ @AutoValue abstract class RefasterScanner<M extends TemplateMatch, T extends Template<M>> extends TreeScanner<Void, Context> { static <M extends TemplateMatch, T extends Template<M>> RefasterScanner<M, T> create( RefasterRule<M, T> rule, DescriptionListener listener) { return new AutoValue_RefasterScanner<>(rule, listener); } abstract RefasterRule<M, T> rule(); abstract DescriptionListener listener(); @Override public Void visitClass(ClassTree node, Context context) { Symbol sym = ASTHelpers.getSymbol(node); if (sym == null || !sym.getQualifiedName().contentEquals(rule().qualifiedTemplateClass())) { return super.visitClass(node, context); } else { return null; } } @Override public Void scan(Tree tree, Context context) { if (tree == null) { return null; } JCCompilationUnit compilationUnit = context.get(JCCompilationUnit.class); for (T beforeTemplate : rule().beforeTemplates()) { for (M match : beforeTemplate.match((JCTree) tree, context)) { if (rule().rejectMatchesWithComments()) { String matchContents = match.getRange(compilationUnit); if (matchContents.contains("//") || matchContents.contains("/*")) { continue; } } Fix fix; if (rule().afterTemplate() == null) { fix = SuggestedFix.delete(match.getLocation()); } else { fix = rule().afterTemplate().replace(match); } listener().onDescribed(new Description( match.getLocation(), rule().qualifiedTemplateClass(), fix, SeverityLevel.WARNING)); } } return super.scan(tree, context); } private static final SimpleTreeVisitor<Tree, Void> SKIP_PARENS = new SimpleTreeVisitor<Tree, Void>() { @Override public Tree visitParenthesized(ParenthesizedTree node, Void v) { return node.getExpression().accept(this, null); } @Override protected Tree defaultAction(Tree node, Void v) { return node; } }; /* * Matching on the parentheses surrounding the condition of an if, while, or do-while * is nonsensical, as those parentheses are obligatory and should never be changed. */ @Override public Void visitDoWhileLoop(DoWhileLoopTree node, Context context) { scan(node.getStatement(), context); scan(SKIP_PARENS.visit(node.getCondition(), null), context); return null; } @Override public Void visitWhileLoop(WhileLoopTree node, Context context) { scan(SKIP_PARENS.visit(node.getCondition(), null), context); scan(node.getStatement(), context); return null; } @Override public Void visitSynchronized(SynchronizedTree node, Context context) { scan(SKIP_PARENS.visit(node.getExpression(), null), context); scan(node.getBlock(), context); return null; } @Override public Void visitIf(IfTree node, Context context) { scan(SKIP_PARENS.visit(node.getCondition(), null), context); scan(node.getThenStatement(), context); scan(node.getElseStatement(), context); return null; } }