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 
AssertOverloadKind(String errKey)54         AssertOverloadKind(String errKey) {
55             this.errKey = errKey;
56         }
57 
simpleArgExpected()58         boolean simpleArgExpected() {
59             return this == AssertOverloadKind.EAGER;
60         }
61     }
62 
AssertCheckAnalyzer(JavacTask task)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
visitApply(JCMethodInvocation tree)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 
assertOverloadKind(Symbol method)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 
isSimpleStringArg(JCExpression e)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 }
116