1 /*
   2  * Copyright (c) 2013, 2014, 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 com.sun.source.tree.LambdaExpressionTree.BodyKind;
  29 import com.sun.source.util.JavacTask;
  30 import com.sun.source.util.TaskEvent.Kind;
  31 import com.sun.tools.javac.code.Kinds;
  32 import com.sun.tools.javac.code.Symbol;
  33 import com.sun.tools.javac.code.Type;
  34 import com.sun.tools.javac.tree.JCTree.JCExpression;
  35 import com.sun.tools.javac.tree.JCTree.JCLambda;
  36 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
  37 import com.sun.tools.javac.tree.JCTree.Tag;
  38 import com.sun.tools.javac.tree.TreeInfo;
  39 import com.sun.tools.javac.tree.TreeScanner;
  40 import com.sun.tools.javac.util.Assert;
  41 
  42 /**This analyzer guards against complex messages (i.e. those that use string concatenation) passed
  43  * to various Assert.check methods.
  44  */
  45 public class AssertCheckAnalyzer extends AbstractCodingRulesAnalyzer {
  46 
  47     enum AssertOverloadKind {
  48         EAGER("crules.should.not.use.eager.string.evaluation"),
  49         LAZY("crules.should.not.use.lazy.string.evaluation"),
  50         NONE(null);
  51 
  52         String errKey;
  53 
  54         AssertOverloadKind(String errKey) {
  55             this.errKey = errKey;
  56         }
  57 
  58         boolean simpleArgExpected() {
  59             return this == AssertOverloadKind.EAGER;
  60         }
  61     }
  62 
  63     public AssertCheckAnalyzer(JavacTask task) {
  64         super(task);
  65         treeVisitor = new AssertCheckVisitor();
  66         eventKind = Kind.ANALYZE;
  67     }
  68 
  69     class AssertCheckVisitor extends TreeScanner {
  70 
  71         @Override
  72         public void visitApply(JCMethodInvocation tree) {
  73             Symbol m = TreeInfo.symbolFor(tree);
  74             AssertOverloadKind ak = assertOverloadKind(m);
  75             if (ak != AssertOverloadKind.NONE &&
  76                 !m.name.contentEquals("error")) {
  77                 JCExpression lastParam = tree.args.last();
  78                 if (isSimpleStringArg(lastParam) != ak.simpleArgExpected()) {
  79                     messages.error(lastParam, ak.errKey);
  80                 }
  81             }
  82 
  83             super.visitApply(tree);
  84         }
  85 
  86         AssertOverloadKind assertOverloadKind(Symbol method) {
  87             if (method == null ||
  88                 !method.owner.getQualifiedName().contentEquals(Assert.class.getName()) ||
  89                 method.type.getParameterTypes().tail == null) {
  90                 return AssertOverloadKind.NONE;
  91             }
  92             Type formal = method.type.getParameterTypes().last();
  93             if (types.isSameType(formal, syms.stringType)) {
  94                 return AssertOverloadKind.EAGER;
  95             } else if (types.isSameType(types.erasure(formal), types.erasure(syms.supplierType))) {
  96                 return AssertOverloadKind.LAZY;
  97             } else {
  98                 return AssertOverloadKind.NONE;
  99             }
 100         }
 101 
 102         boolean isSimpleStringArg(JCExpression e) {
 103             switch (e.getTag()) {
 104                 case LAMBDA:
 105                     JCLambda lambda = (JCLambda)e;
 106                     return (lambda.getBodyKind() == BodyKind.EXPRESSION) &&
 107                             isSimpleStringArg((JCExpression)lambda.body);
 108                 default:
 109                     Symbol argSym = TreeInfo.symbolFor(e);
 110                     return (e.type.constValue() != null ||
 111                             (argSym != null && argSym.kind == Kinds.Kind.VAR));
 112             }
 113         }
 114     }
 115 }