Commits

Anonymous committed ebfeb83

Various improvements and test enhacements.

Comments (0)

Files changed (4)

analysis/src/j1/tutorial/javac/analysis/api/Utilities.java

         return null;
     }
     
+    public static AnnotationMirror findAttachedAnnotation(ProcessingEnvironment context, Element forElement, String annotationFQN) {
+        return findAttachedAnnotation(forElement, annotationFQN);
+    }
+    
     public static AnnotationMirror findAttachedAnnotation(AnalysisContext context, Element forElement, String annotationFQN) {
         return findAttachedAnnotation(forElement, annotationFQN);
     }

analysis/src/j1/tutorial/javac/analysis/rules/RegExpRule.java

 import com.sun.source.tree.BinaryTree;
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.ExpressionTree;
-import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.LiteralTree;
-import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.MethodInvocationTree;
-import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
 import com.sun.source.util.TreePathScanner;
 import j1.tutorial.javac.analysis.api.AnalysisContext;
 import j1.tutorial.javac.analysis.api.Rule;
 
     @Override
     public void analyseFile(final AnalysisContext ctx, final CompilationUnitTree file) {
-        new TreePathScanner<String, Void>() {
-            @Override public String visitLiteral(LiteralTree node, Void p) {
-                if (node.getValue() instanceof String) return (String) node.getValue();
-                return null;
-            }
-            @Override public String visitIdentifier(IdentifierTree node, Void p) {
-                return handlePossibleConstantReference();
-            }
-            @Override public String visitMemberSelect(MemberSelectTree node, Void p) {
-                return handlePossibleConstantReference();
-            }
-            private String handlePossibleConstantReference() {
+        new TreePathScanner<Void, Void>() {
+            @Override public Void visitVariable(VariableTree vt, Void p) {
                 Element el = ctx.getTrees().getElement(getCurrentPath());
                 
-                if (el != null && el.getKind() == ElementKind.FIELD && ((VariableElement) el).getConstantValue() instanceof String) {
-                    return (String) ((VariableElement) el).getConstantValue();
+                if (vt.getInitializer() != null && el != null) {
+                    String constant = getConstant(ctx, new TreePath(getCurrentPath(), vt.getInitializer()));
+
+                    maybeVerifyRegExp(el, constant, vt.getInitializer());
                 }
-                
-                return null;
+                return super.visitVariable(vt, p);
             }
-            @Override public String visitBinary(BinaryTree node, Void p) {
-                String left = scan(node.getLeftOperand(), null);
-                String right = scan(node.getRightOperand(), null);
-                
-                if (node.getKind() == Kind.PLUS && left != null && right != null) {
-                    return left + right;
-                }
-                return null;
-            }
-            @Override public String visitMethodInvocation(MethodInvocationTree node, Void p) {
+            @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
                 Element method = ctx.getTrees().getElement(getCurrentPath());
                 
                 if (method != null && method.getKind() == ElementKind.METHOD) {
                         VariableElement var = paramVars.next();
                         ExpressionTree tree = paramTree.next();
                         
-                        String constant = scan(tree, null);//comment: need to take great care to delegate correctly
-                        
-                        if (Utilities.findAttachedAnnotation(ctx, var, "j1.tutorial.javac.analysis.api.RegExp") != null && constant != null) {
-                            try {
-                                Pattern.compile(constant);
-                            } catch (PatternSyntaxException ex) {
-                                ctx.getTrees().printMessage(Diagnostic.Kind.ERROR, ex.getDescription(), tree, file);
-                            }
-                        }
+                        String constant = getConstant(ctx, new TreePath(getCurrentPath(), tree));
+                        maybeVerifyRegExp(var, constant, tree);
                     }
-                    
-                    scan(node.getMethodSelect(), null);//comment: need to take great care to delegate correctly
-                    
-                    return null;
                 }
                 
                 return super.visitMethodInvocation(node, p);
             }
-            @Override public String reduce(String r1, String r2) {
-                return null;
+
+            private void maybeVerifyRegExp(Element var, String constant, ExpressionTree tree) {
+                if (Utilities.findAttachedAnnotation(ctx, var, "j1.tutorial.javac.analysis.api.RegExp") != null && constant != null) {
+                    try {
+                        Pattern.compile(constant);
+                    } catch (PatternSyntaxException ex) {
+                        ctx.getTrees().printMessage(Diagnostic.Kind.ERROR, ex.getDescription(), tree, file);
+                    }
+                }
             }
         }.scan(file, null);
     }
     
+    private String getConstant(final AnalysisContext ctx, final TreePath path) {
+        switch (path.getLeaf().getKind()) {
+            case STRING_LITERAL: return (String) ((LiteralTree) path.getLeaf()).getValue();
+            case PLUS: {
+                BinaryTree binary = (BinaryTree) path.getLeaf();
+                String left = getConstant(ctx, new TreePath(path, binary.getLeftOperand()));
+                String right = getConstant(ctx, new TreePath(path, binary.getRightOperand()));
+                
+                if (left != null && right != null) return left + right;
+                else return null;
+            }
+            case IDENTIFIER:
+            case MEMBER_SELECT: {
+                Element el = ctx.getTrees().getElement(path);
+                
+                if (el != null && el.getKind() == ElementKind.FIELD && ((VariableElement) el).getConstantValue() instanceof String) {
+                    return (String) ((VariableElement) el).getConstantValue();
+                }
+                
+                return null;
+            }
+        }
+        
+        return null;
+    }
+    
 }

analysis/test/j1/tutorial/javac/analysis/api/RuleTest.java

     
     public RuleResult runProcessor(final Class<? extends Processor> r) throws Exception {
         File processorJar = new File(r.getProtectionDomain().getCodeSource().getLocation().toURI());
-        return run(null, "-processorpath", processorJar.getAbsolutePath(), "-processor", r.getName(), "-proc:only");
+        File apiJar = new File(Friend.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        return run(null, "-processorpath", processorJar.getAbsolutePath() + File.pathSeparatorChar + apiJar.getAbsolutePath(), "-processor", r.getName(), "-proc:only");
     }
     
     private RuleResult run(AnalyzerImpl analyzer, String... extraOptions) throws Exception {
         for (Diagnostic<? extends JavaFileObject> d : dc.getDiagnostics()) {
             if (   !"compiler.err.proc.messager".equals(d.getCode())
                 && !"compiler.warn.proc.messager".equals(d.getCode())) continue;
+            if (d.getSource() == null) continue;
             warnings.add(d.getSource().getName() + ":" + d.getStartPosition() + "-" + d.getEndPosition() + ":" + d.getMessage(Locale.ENGLISH));
         }
         

analysis/test/j1/tutorial/javac/analysis/rules/RegExpRuleNGTest.java

     }
     
     @Test
+    public void testSimpleFieldRegExp() throws Exception {
+        RuleTest.create()
+                .input("test/Test.java",
+                       "package test;\n" +
+                       "import j1.tutorial.javac.analysis.api.RegExp;\n" +
+                       "public class Test {\n" +
+                       "     @RegExp private final String MASK = \"(\";\n" +
+                       "}\n")
+                .run(RegExpRule.class)
+                .assertWarnings("/test/Test.java:121-124:Unclosed group");
+    }
+    
+    @Test
+    public void testFieldRegExpBinOp() throws Exception {
+        RuleTest.create()
+                .input("test/Test.java",
+                       "package test;\n" +
+                       "import j1.tutorial.javac.analysis.api.RegExp;\n" +
+                       "public class Test {\n" +
+                       "     @RegExp private final String MASK = \"(\" + \"a\";\n" +
+                       "}\n")
+                .run(RegExpRule.class)
+                .assertWarnings("/test/Test.java:125-130:Unclosed group");
+    }
+    
+    @Test
+    public void testFieldRegExpReference() throws Exception {
+        RuleTest.create()
+                .input("test/Test.java",
+                       "package test;\n" +
+                       "import j1.tutorial.javac.analysis.api.RegExp;\n" +
+                       "public class Test {\n" +
+                       "     public static final String DEF_MASK = \"(\";\n" +
+                       "     @RegExp private String MASK = DEF_MASK;\n" +
+                       "}\n")
+                .run(RegExpRule.class)
+                .assertWarnings("/test/Test.java:163-171:Unclosed group");
+    }
+    
+    @Test
     public void testBrokenRegexp() throws Exception {
         RuleTest.create()
                 .input("test/Test.java",