1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package crules;
  27 
  28 import java.util.Arrays;
  29 import java.util.HashSet;
  30 import java.util.Set;
  31 
  32 import com.sun.source.util.JavacTask;
  33 import com.sun.source.util.TaskEvent.Kind;
  34 import com.sun.tools.javac.code.Kinds;
  35 import com.sun.tools.javac.code.Symbol;
  36 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  37 import com.sun.tools.javac.code.Type;
  38 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
  39 import com.sun.tools.javac.tree.JCTree.JCExpression;
  40 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
  41 import com.sun.tools.javac.tree.JCTree.Tag;
  42 import com.sun.tools.javac.tree.TreeInfo;
  43 import com.sun.tools.javac.tree.TreeScanner;
  44 import com.sun.tools.javac.util.AbstractLog;
  45 import com.sun.tools.javac.util.JCDiagnostic;
  46 
  47 /**This analyzer guards against legacy Log.error/warning/note methods that don't use the typed keys.*/
  48 public class LegacyLogMethodAnalyzer extends AbstractCodingRulesAnalyzer {
  49 
  50     public LegacyLogMethodAnalyzer(JavacTask task) {
  51         super(task);
  52         treeVisitor = new LegacyLogMethodVisitor();
  53         eventKind = Kind.ANALYZE;
  54     }
  55 
  56     private static final Set<String> LEGACY_METHOD_NAMES = new HashSet<>(
  57             Arrays.asList("error", "mandatoryWarning", "warning", "mandatoryNote", "note", "fragment"));
  58 
  59     class LegacyLogMethodVisitor extends TreeScanner {
  60 
  61         @Override
  62         public void visitClassDef(JCClassDecl tree) {
  63             if (!tree.sym.packge().fullname.toString().startsWith("com.sun.tools.javac."))
  64                 return ;
  65             super.visitClassDef(tree);
  66         }
  67 
  68         @Override
  69         public void visitApply(JCMethodInvocation tree) {
  70             checkLegacyLogMethod(tree);
  71             super.visitApply(tree);
  72         }
  73 
  74         void checkLegacyLogMethod(JCMethodInvocation tree) {
  75             Symbol method = TreeInfo.symbolFor(tree);
  76             if (method == null ||
  77                 method.kind != Kinds.Kind.MTH ||
  78                 !typeToCheck(method.owner.type) ||
  79                 !LEGACY_METHOD_NAMES.contains(method.name.toString()) ||
  80                 !((MethodSymbol) method).isVarArgs() ||
  81                 method.type.getParameterTypes().size() < 2) {
  82                 return ;
  83             }
  84             JCExpression key = tree.args.get(method.type.getParameterTypes().size() - 2);
  85             if (key.hasTag(Tag.LITERAL)) {
  86                 messages.error(tree, "crules.use.of.legacy.log.method", tree);
  87             }
  88         }
  89 
  90         boolean typeToCheck(Type type) {
  91             Symbol abstractLog = elements.getTypeElement(AbstractLog.class.getName());
  92             Symbol diagnosticFactory = elements.getTypeElement(JCDiagnostic.Factory.class.getName().replace('$', '.'));
  93             return types.isSubtype(type, abstractLog.type) ||
  94                    types.isSubtype(type, diagnosticFactory.type);
  95         }
  96     }
  97 }