You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2015/10/07 21:26:36 UTC

[16/37] incubator-groovy git commit: ASTMatcher:First test cases for class types

ASTMatcher:First test cases for class types


Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/bac61a14
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/bac61a14
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/bac61a14

Branch: refs/heads/master
Commit: bac61a14d911484a81b4857109e17871c9af1dbf
Parents: 02e53fc
Author: Cedric Champeau <ce...@gmail.com>
Authored: Fri Oct 17 11:58:41 2014 +0200
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:33:10 2015 +0300

----------------------------------------------------------------------
 .../groovy/macro/matcher/ASTMatcher.groovy      | 285 ++++++++++++-------
 .../groovy/macro/matcher/ASTMatcherTest.groovy  | 226 +++++++++++++++
 2 files changed, 405 insertions(+), 106 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/bac61a14/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
index ffcf09a..9158c32 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
@@ -19,10 +19,11 @@ import groovy.transform.CompileStatic
 import groovy.transform.TypeCheckingMode
 import org.codehaus.groovy.ast.*
 import org.codehaus.groovy.ast.expr.*
-import org.codehaus.groovy.ast.stmt.*
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.stmt.Statement
 import org.codehaus.groovy.classgen.BytecodeExpression
 import org.codehaus.groovy.control.SourceUnit
-import org.codehaus.groovy.transform.stc.StaticTypesMarker
 
 @CompileStatic
 class ASTMatcher extends ClassCodeVisitorSupport {
@@ -57,6 +58,10 @@ class ASTMatcher extends ClassCodeVisitorSupport {
         matcher.match
     }
 
+    private boolean failIfNot(boolean value) {
+        match = match && value
+    }
+
     /**
      * Locates all nodes in the given AST which match the pattern AST.
      * This operation can cost a lot, because it tries to match a sub-tree
@@ -73,13 +78,16 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     }
 
     private void doWithNode(Class expectedClass, Object next, Closure cl) {
-        if (match && (next==null || expectedClass.isAssignableFrom(next.class))) {
+        if (expectedClass == null) {
+            expectedClass = Object
+        }
+        if (match && (next == null || expectedClass.isAssignableFrom(next.class))) {
             Object old = current
             current = next
             cl()
             current = old
         } else {
-            match = false
+            failIfNot(false)
         }
     }
 
@@ -87,15 +95,22 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitClass(final ClassNode node) {
         doWithNode(ClassNode, current) {
             visitAnnotations(node)
-        }
-        doWithNode(PackageNode, ((ClassNode) current).package) {
-            visitPackage(node.package)
-        }
-        doWithNode(ModuleNode, ((ClassNode) current).module) {
-            visitImports(node.module)
-        }
-        doWithNode(ClassNode, current) {
+            doWithNode(PackageNode, ((ClassNode) current).package) {
+                visitPackage(node.package)
+            }
+            doWithNode(ModuleNode, ((ClassNode) current).module) {
+                visitImports(node.module)
+            }
+
             def cur = (ClassNode) current
+            def intfs = node.interfaces
+            def curIntfs = cur.interfaces
+            failIfNot(intfs.length == curIntfs.length)
+            if (intfs.length == curIntfs.length) {
+                for (int i = 0; i < intfs.length && match; i++) {
+                    failIfNot(intfs[i] == curIntfs[i])
+                }
+            }
             def nodeProps = node.properties
             def curProps = cur.properties
             if (nodeProps.size() == curProps.size()) {
@@ -109,7 +124,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
                 def nodeFields = node.fields
                 def curFields = cur.fields
-                if (nodeFields.size()==curFields.size()) {
+                if (nodeFields.size() == curFields.size()) {
                     iter = curFields.iterator()
                     for (FieldNode fn : nodeFields) {
                         doWithNode(fn.class, iter.next()) {
@@ -119,7 +134,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
                     def nodeConstructors = node.declaredConstructors
                     def curConstructors = cur.declaredConstructors
-                    if (nodeConstructors.size()==curConstructors.size()) {
+                    if (nodeConstructors.size() == curConstructors.size()) {
                         iter = curConstructors.iterator()
                         for (ConstructorNode cn : nodeConstructors) {
                             doWithNode(cn.class, iter.next()) {
@@ -140,19 +155,16 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         }
                     }
                 }
-
-
-                return
             }
-            match = false
+            failIfNot(cur.name == node.name)
         }
     }
 
     @Override
     protected void visitObjectInitializerStatements(final ClassNode node) {
         doWithNode(ClassNode, current) {
-            def initializers = ((ClassNode)current).objectInitializerStatements
-            if (initializers.size()==node.objectInitializerStatements.size()) {
+            def initializers = ((ClassNode) current).objectInitializerStatements
+            if (initializers.size() == node.objectInitializerStatements.size()) {
                 def iterator = initializers.iterator()
                 for (Statement element : node.objectInitializerStatements) {
                     doWithNode(element.class, iterator.next()) {
@@ -160,7 +172,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                     }
                 }
             } else {
-                match = false
+                failIfNot(false)
             }
         }
     }
@@ -190,7 +202,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         }
                     }
                 } else {
-                    match = false
+                    failIfNot(false)
                     return
                 }
                 imports = module.starImports
@@ -203,7 +215,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         }
                     }
                 } else {
-                    match = false
+                    failIfNot(false)
                     return
                 }
                 imports = module.staticImports
@@ -216,8 +228,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         }
                     }
                 } else {
-                    match = false
-                    return
+                    failIfNot(false)
                 }
                 imports = module.staticStarImports
                 if (imports.size() == node.staticStarImports.size()) {
@@ -229,8 +240,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         }
                     }
                 } else {
-                    match = false
-                    return
+                    failIfNot(false)
                 }
             }
         }
@@ -242,39 +252,39 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             List<AnnotationNode> refAnnotations = node.annotations
             AnnotatedNode cur = (AnnotatedNode) current
             List<AnnotationNode> curAnnotations = cur.annotations
-            if (refAnnotations.size()!=curAnnotations.size()) {
-                match = false
+            if (refAnnotations.size() != curAnnotations.size()) {
+                failIfNot(false)
                 return
             }
             if (refAnnotations.empty) return
             def iter = curAnnotations.iterator()
             for (AnnotationNode an : refAnnotations) {
                 AnnotationNode curNext = iter.next()
+
                 // skip built-in properties
                 if (an.builtIn) {
                     if (!curNext.builtIn) {
-                        match = false
-                        return
+                        failIfNot(false)
                     }
                     continue
                 }
-
+                failIfNot(an.classNode==curNext.classNode)
                 def refEntrySet = an.members.entrySet()
                 def curEntrySet = curNext.members.entrySet()
-                if (refEntrySet.size()==curEntrySet.size()) {
+                if (refEntrySet.size() == curEntrySet.size()) {
                     def entryIt = curEntrySet.iterator()
                     for (Map.Entry<String, Expression> member : refEntrySet) {
                         def next = entryIt.next()
-                        if (next.key==member.key) {
+                        if (next.key == member.key) {
                             doWithNode(member.value.class, next.value) {
                                 member.value.visit(this)
                             }
                         } else {
-                            match = false
+                            failIfNot(false)
                         }
                     }
                 } else {
-                    match = false
+                    failIfNot(false)
                 }
             }
         }
@@ -307,14 +317,14 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             }
             def params = node.parameters
             def curParams = cur.parameters
-            if (params.length==curParams.length) {
+            if (params.length == curParams.length) {
                 params.eachWithIndex { Parameter entry, int i ->
                     doWithNode(entry.class, curParams[i]) {
                         visitAnnotations(entry)
                     }
                 }
             } else {
-                match = false
+                failIfNot(false)
             }
         }
     }
@@ -324,23 +334,23 @@ class ASTMatcher extends ClassCodeVisitorSupport {
         doWithNode(FieldNode, current) {
             visitAnnotations(node)
             def fieldNode = (FieldNode) current
-            if (fieldNode.name==node.name) {
-                Expression init = node.initialExpression
-
-                Expression curInit = fieldNode.initialExpression
-                if (init) {
-                    if (curInit) {
-                        doWithNode(init.class, curInit) {
-                            init.visit(this)
-                        }
-                    } else {
-                        match = false
+            failIfNot(fieldNode.name == node.name)
+            failIfNot(fieldNode.originType == node.originType)
+            failIfNot(fieldNode.modifiers == node.modifiers)
+
+            Expression init = node.initialExpression
+
+            Expression curInit = fieldNode.initialExpression
+            if (init) {
+                if (curInit) {
+                    doWithNode(init.class, curInit) {
+                        init.visit(this)
                     }
-                } else if (curInit) {
-                    match = false
+                } else {
+                    failIfNot(false)
                 }
-            } else {
-                match = false
+            } else if (curInit) {
+                failIfNot(false)
             }
         }
     }
@@ -353,13 +363,13 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
             Statement statement = node.getterBlock
             Statement curStatement = pNode.getterBlock
-            doWithNode(statement.class, curStatement) {
+            doWithNode(statement?.class, curStatement) {
                 visitClassCodeContainer(statement)
             }
 
             statement = node.setterBlock
             curStatement = pNode.setterBlock
-            doWithNode(statement.class, curStatement) {
+            doWithNode(statement?.class, curStatement) {
                 visitClassCodeContainer(statement)
             }
 
@@ -371,10 +381,10 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                         init.visit(this)
                     }
                 } else {
-                    match = false
+                    failIfNot(false)
                 }
             } else if (curInit) {
-                match = false
+                failIfNot(false)
             }
         }
     }
@@ -382,7 +392,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     @Override
     void visitExpressionStatement(final ExpressionStatement statement) {
         visitStatement(statement)
-        doWithNode(statement.expression.class, ((ExpressionStatement)current).expression) {
+        doWithNode(statement.expression.class, ((ExpressionStatement) current).expression) {
             statement.expression.visit(this)
         }
     }
@@ -391,7 +401,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitBlockStatement(BlockStatement block) {
         doWithNode(BlockStatement, current) {
             def statements = ((BlockStatement) current).statements
-            if (statements.size()==block.statements.size()) {
+            if (statements.size() == block.statements.size()) {
                 def iter = statements.iterator()
                 for (Statement statement : block.statements) {
                     doWithNode(statement.class, iter.next()) {
@@ -399,7 +409,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
                     }
                 }
             } else {
-                match = false
+                failIfNot(false)
             }
         }
     }
@@ -417,10 +427,10 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             doWithNode(call.arguments.class, mce.arguments) {
                 call.arguments.visit(this)
             }
-            match = match && (call.methodAsString==mce.methodAsString) &&
-                    (call.safe==mce.safe) &&
+            failIfNot((call.methodAsString == mce.methodAsString) &&
+                    (call.safe == mce.safe) &&
                     (call.spreadSafe == mce.spreadSafe) &&
-                    (call.implicitThis == mce.implicitThis)
+                    (call.implicitThis == mce.implicitThis))
         }
     }
 
@@ -434,8 +444,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
         def cur = (ConstructorCallExpression) current
         doWithNode(call.arguments.class, cur.arguments) {
             call.arguments.visit(this)
-            match = match &&
-                    (call.type == cur.type)
+            failIfNot(call.type == cur.type)
         }
     }
 
@@ -451,8 +460,8 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             doWithNode(rightExpression.class, bin.rightExpression) {
                 rightExpression.visit(this)
             }
-            if (bin.operation.type!=expression.operation.type) {
-                match = false
+            if (bin.operation.type != expression.operation.type) {
+                failIfNot(false)
             }
         }
     }
@@ -482,8 +491,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             def curExpr = (PostfixExpression) current
             doWithNode(origExpr.class, curExpr.expression) {
                 origExpr.visit(this)
-                match = match &&
-                        (expression.operation.type==curExpr.operation.type)
+                failIfNot(expression.operation.type == curExpr.operation.type)
             }
         }
     }
@@ -495,8 +503,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             def curExpr = (PrefixExpression) current
             doWithNode(origExpr.class, curExpr.expression) {
                 origExpr.visit(this)
-                match = match &&
-                        (expression.operation.type==curExpr.operation.type)
+                failIfNot(expression.operation.type == curExpr.operation.type)
             }
         }
     }
@@ -504,7 +511,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     @Override
     public void visitBooleanExpression(final BooleanExpression expression) {
         doWithNode(BooleanExpression, current) {
-            doWithNode(expression.expression.class, ((BooleanExpression)current).expression) {
+            doWithNode(expression.expression.class, ((BooleanExpression) current).expression) {
                 expression.expression.visit(this)
             }
         }
@@ -514,7 +521,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitNotExpression(final NotExpression expression) {
         doWithNode(NotExpression, current) {
             def expr = expression.expression
-            def cur = ((NotExpression)current).expression
+            def cur = ((NotExpression) current).expression
             doWithNode(expr.class, cur) {
                 expr.visit(this)
             }
@@ -534,24 +541,23 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     }
 
     private void checkParameters(Parameter[] nodeParams, Parameter[] curParams) {
-        if (nodeParams==null && curParams!=null || nodeParams!=null && curParams==null) {
-            match = false
+        if (nodeParams == null && curParams != null || nodeParams != null && curParams == null) {
+            failIfNot(false)
             return
         }
         if (nodeParams) {
-            if (curParams.length==nodeParams.length) {
+            if (curParams.length == nodeParams.length) {
                 for (int i = 0; i < nodeParams.length && match; i++) {
                     def n = nodeParams[i]
                     def c = curParams[i]
                     doWithNode(n.class, c) {
-                        match = match &&
-                                (n.name == c.name) &&
+                        failIfNot((n.name == c.name) &&
                                 (n.originType == c.originType) &&
-                                (n.type == c.originType)
+                                (n.type == c.originType))
                     }
                 }
             } else {
-                match = false
+                failIfNot(false)
             }
         }
     }
@@ -569,7 +575,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitListExpression(final ListExpression expression) {
         doWithNode(ListExpression, current) {
             def exprs = expression.expressions
-            doWithNode(exprs.class, ((ListExpression)current).expressions) {
+            doWithNode(exprs.class, ((ListExpression) current).expressions) {
                 visitListOfExpressions(exprs)
             }
         }
@@ -577,14 +583,27 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
     @Override
     public void visitArrayExpression(final ArrayExpression expression) {
-        super.visitArrayExpression(expression);
+        doWithNode(ArrayExpression, current) {
+            def expressions = expression.expressions
+            def size = expression.sizeExpression
+            def cur = (ArrayExpression) current
+            def curExprs = cur.expressions
+            def curSize = cur.sizeExpression
+            doWithNode(expressions.class, curExprs) {
+                visitListOfExpressions(expressions)
+            }
+            doWithNode(size.class, curSize) {
+                visitListOfExpressions(size)
+            }
+            failIfNot(expression.elementType == cur.elementType)
+        }
     }
 
     @Override
     public void visitMapExpression(final MapExpression expression) {
         doWithNode(MapExpression, current) {
             def entries = expression.mapEntryExpressions
-            def curEntries = ((MapExpression)current).mapEntryExpressions
+            def curEntries = ((MapExpression) current).mapEntryExpressions
             doWithNode(entries.class, curEntries) {
                 visitListOfExpressions(entries)
             }
@@ -629,7 +648,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitSpreadExpression(final SpreadExpression expression) {
         doWithNode(SpreadExpression, current) {
             def expr = expression.expression
-            doWithNode(expr.class, ((SpreadExpression)current).expression) {
+            doWithNode(expr.class, ((SpreadExpression) current).expression) {
                 expr.visit(this)
             }
         }
@@ -642,27 +661,60 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
     @Override
     public void visitMethodPointerExpression(final MethodPointerExpression expression) {
-        super.visitMethodPointerExpression(expression);
+        doWithNode(MethodPointerExpression, current) {
+            def cur = (MethodPointerExpression) current
+            def expr = expression.expression
+            def methodName = expression.methodName
+            def curExpr = cur.expression
+            def curName = cur.methodName
+            doWithNode(expr.class, curExpr) {
+                expr.visit(this)
+            }
+            doWithNode(methodName.class, curName) {
+                methodName.visit(this)
+            }
+        }
     }
 
     @Override
     public void visitUnaryMinusExpression(final UnaryMinusExpression expression) {
-        super.visitUnaryMinusExpression(expression);
+        doWithNode(UnaryMinusExpression, current) {
+            def expr = expression.expression
+            doWithNode(expr.class, ((UnaryMinusExpression) current).expression) {
+                expr.visit(this)
+            }
+        }
     }
 
     @Override
     public void visitUnaryPlusExpression(final UnaryPlusExpression expression) {
-        super.visitUnaryPlusExpression(expression);
+        doWithNode(UnaryPlusExpression, current) {
+            def expr = expression.expression
+            doWithNode(expr.class, ((UnaryPlusExpression) current).expression) {
+                expr.visit(this)
+            }
+        }
     }
 
     @Override
     public void visitBitwiseNegationExpression(final BitwiseNegationExpression expression) {
-        super.visitBitwiseNegationExpression(expression);
+        doWithNode(BitwiseNegationExpression, current) {
+            def expr = expression.expression
+            doWithNode(expr.class, ((BitwiseNegationExpression) current).expression) {
+                expr.visit(this)
+            }
+        }
     }
 
     @Override
     public void visitCastExpression(final CastExpression expression) {
-        super.visitCastExpression(expression);
+        doWithNode(CastExpression, current) {
+            def expr = expression.expression
+            doWithNode(expr.class, ((CastExpression) current).expression) {
+                expr.visit(this)
+            }
+            failIfNot(expression.type == ((CastExpression) current).type)
+        }
     }
 
     @Override
@@ -671,9 +723,8 @@ class ASTMatcher extends ClassCodeVisitorSupport {
         doWithNode(ConstantExpression, current) {
             def cur = (ConstantExpression) current
             super.visitConstantExpression(expression)
-            match = match &&
-                    (expression.type==cur.type) &&
-                    (expression.value==cur.value)
+            failIfNot((expression.type == cur.type) &&
+                    (expression.value == cur.value))
         }
     }
 
@@ -683,7 +734,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
         doWithNode(ClassExpression, current) {
             super.visitClassExpression(expression)
             def cexp = (ClassExpression) current
-            match = match && (cexp.type == expression.type)
+            failIfNot(cexp.type == expression.type)
         }
     }
 
@@ -691,10 +742,9 @@ class ASTMatcher extends ClassCodeVisitorSupport {
     public void visitVariableExpression(final VariableExpression expression) {
         doWithNode(VariableExpression, current) {
             def curVar = (VariableExpression) current
-            match = match &&
-                    (expression.name == curVar.name) &&
+            failIfNot((expression.name == curVar.name) &&
                     (expression.type == curVar.type) &&
-                    (expression.originType == curVar.originType)
+                    (expression.originType == curVar.originType))
         }
     }
 
@@ -708,17 +758,28 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             doWithNode(expression.property.class, currentPexp.property) {
                 expression.property.visit(this)
             }
-            match = match &&
-                    (expression.propertyAsString==currentPexp.propertyAsString) &&
-                    (expression.implicitThis==currentPexp.implicitThis) &&
-                    (expression.safe==currentPexp.safe) &&
-                    (expression.spreadSafe==currentPexp.spreadSafe)
+            failIfNot((expression.propertyAsString == currentPexp.propertyAsString) &&
+                    (expression.implicitThis == currentPexp.implicitThis) &&
+                    (expression.safe == currentPexp.safe) &&
+                    (expression.spreadSafe == currentPexp.spreadSafe))
         }
     }
 
     @Override
     public void visitAttributeExpression(final AttributeExpression expression) {
-        super.visitAttributeExpression(expression);
+        doWithNode(AttributeExpression, current) {
+            def currentPexp = (AttributeExpression) current
+            doWithNode(expression.objectExpression.class, currentPexp.objectExpression) {
+                expression.objectExpression.visit(this)
+            }
+            doWithNode(expression.property.class, currentPexp.property) {
+                expression.property.visit(this)
+            }
+            failIfNot((expression.propertyAsString == currentPexp.propertyAsString) &&
+                    (expression.implicitThis == currentPexp.implicitThis) &&
+                    (expression.safe == currentPexp.safe) &&
+                    (expression.spreadSafe == currentPexp.spreadSafe))
+        }
     }
 
     @Override
@@ -728,15 +789,27 @@ class ASTMatcher extends ClassCodeVisitorSupport {
 
     @Override
     public void visitGStringExpression(final GStringExpression expression) {
-        super.visitGStringExpression(expression);
+        doWithNode(GStringExpression, current) {
+            def cur = (GStringExpression) current
+            def strings = expression.strings
+            def values = expression.values
+            def curStrings = cur.strings
+            def curValues = cur.values
+            doWithNode(strings.class, curStrings) {
+                visitListOfExpressions(strings)
+            }
+            doWithNode(values.class, curValues) {
+                visitListOfExpressions(values)
+            }
+        }
     }
 
     @Override
     protected void visitListOfExpressions(final List<? extends Expression> list) {
         if (list == null) return;
         def currentExprs = (List<Expression>) current
-        if (currentExprs.size()!=list.size()) {
-            match = false
+        if (currentExprs.size() != list.size()) {
+            failIfNot(false)
             return
         }
         def iter = currentExprs.iterator()
@@ -745,7 +818,7 @@ class ASTMatcher extends ClassCodeVisitorSupport {
             if (expression instanceof SpreadExpression) {
                 doWithNode(SpreadExpression, next) {
                     Expression spread = ((SpreadExpression) expression).getExpression()
-                    doWithNode(Expression, ((SpreadExpression)current).expression) {
+                    doWithNode(Expression, ((SpreadExpression) current).expression) {
                         spread.visit(this)
                     }
                 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/bac61a14/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy b/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
index 064e01c..ef7c489 100644
--- a/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
+++ b/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
@@ -16,9 +16,18 @@
 
 package org.codehaus.groovy.macro.matcher
 
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.expr.ArrayExpression
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression
+import org.codehaus.groovy.ast.expr.CastExpression
 import org.codehaus.groovy.ast.expr.ClassExpression
+import org.codehaus.groovy.ast.expr.GStringExpression
+import org.codehaus.groovy.ast.expr.MethodPointerExpression
 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression
 import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.macro.transform.MacroClass
 
 class ASTMatcherTest extends GroovyTestCase {
     void testMatchesSimpleVar() {
@@ -77,6 +86,22 @@ class ASTMatcherTest extends GroovyTestCase {
         assert !ASTMatcher.matches(ast1, ast6)
     }
 
+    void testAttributeExpression() {
+        def ast1 = macro { this.@p }
+        def ast2 = macro { this.@p }
+        def ast3 = macro { that.@p }
+        def ast4 = macro { this.@p2 }
+        def ast5 = macro { this?.@p }
+        def ast6 = macro { this*.@p }
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+        assert !ASTMatcher.matches(ast1, ast5)
+        assert !ASTMatcher.matches(ast1, ast6)
+    }
+
     void testClassExpression() {
         def ast1 = macro(CompilePhase.SEMANTIC_ANALYSIS) { String }
         def ast2 = macro(CompilePhase.SEMANTIC_ANALYSIS) { String }
@@ -289,4 +314,205 @@ class ASTMatcherTest extends GroovyTestCase {
         assert !ASTMatcher.matches(ast1, ast3)
         assert !ASTMatcher.matches(ast1, ast4)
     }
+
+    void testArrayExpression() {
+        def ast1 = macro { new Integer[0] }
+        def ast2 = macro { new Integer[0] }
+        def ast3 = macro { new Integer[1] }
+        def ast4 = macro { new int[0] }
+        def ast5 = macro { new Integer[a] }
+        assert ast1 instanceof ArrayExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+        assert !ASTMatcher.matches(ast1, ast5)
+    }
+
+    void testMethodPointerExpression() {
+        def ast1 = macro { this.&foo }
+        def ast2 = macro { this.&foo }
+        def ast3 = macro { that.&foo }
+        def ast4 = macro { this.&bar }
+        assert ast1 instanceof MethodPointerExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testUnaryMinus() {
+        def ast1 = macro { -a }
+        def ast2 = macro { -a }
+        def ast3 = macro { -0 }
+        def ast4 = macro { a }
+        assert ast1 instanceof UnaryMinusExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testUnaryPlus() {
+        def ast1 = macro { +a }
+        def ast2 = macro { +a }
+        def ast3 = macro { +0 }
+        def ast4 = macro { a }
+        assert ast1 instanceof UnaryPlusExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testBitwiseNegate() {
+        def ast1 = macro { ~a }
+        def ast2 = macro { ~a }
+        def ast3 = macro { ~0 }
+        def ast4 = macro { a }
+        assert ast1 instanceof BitwiseNegationExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testCastExpression() {
+        def ast1 = macro { (String) foo }
+        def ast2 = macro { (String) foo }
+        def ast3 = macro { (Integer) foo }
+        def ast4 = macro { (String) bar }
+        assert ast1 instanceof CastExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testGStringExpression() {
+        def ast1 = macro { "123$a" }
+        def ast2 = macro { "123$a" }
+        def ast3 = macro { "$a" }
+        def ast4 = macro { "123$b" }
+        assert ast1 instanceof GStringExpression
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testClassComparison() {
+        def ast1 = new MacroClass() {
+            class A {}
+        }
+        def ast2 = new MacroClass() {
+            class A {}
+        }
+        def ast3 = new MacroClass() {
+            class A implements Serializable {}
+        }
+        def ast4 = new MacroClass() {
+            class B {}
+        }
+        assert ast1 instanceof ClassNode
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+    }
+
+    void testPropertyComparison() {
+        def ast1 = new MacroClass() {
+            class A { String str }
+        }
+        def ast2 = new MacroClass() {
+            class A { String str }
+        }
+        def ast3 = new MacroClass() {
+            class A { String foo }
+        }
+        def ast4 = new MacroClass() {
+            class A { Integer str }
+        }
+        def ast5 = new MacroClass() {
+            class A { String str = null }
+        }
+        def ast6 = new MacroClass() {
+            class A { String str = 'foo' }
+        }
+        def ast7 = new MacroClass() {
+            class A { String str = 'bar' }
+        }
+        def ast8 = new MacroClass() {
+            class A { @Foo String str }
+        }
+        def ast9 = new MacroClass() {
+            class A { @Bar String str }
+        }
+        assert ast1 instanceof ClassNode
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+        assert !ASTMatcher.matches(ast1, ast5)
+        assert !ASTMatcher.matches(ast1, ast6)
+        assert !ASTMatcher.matches(ast6, ast7)
+        assert !ASTMatcher.matches(ast1, ast8)
+        assert ASTMatcher.matches(ast8, ast8)
+        assert !ASTMatcher.matches(ast8, ast9)
+    }
+    void testFieldComparison() {
+        def ast1 = new MacroClass() {
+            class A { public String str }
+        }
+        def ast2 = new MacroClass() {
+            class A { public String str }
+        }
+        def ast3 = new MacroClass() {
+            class A { public String foo }
+        }
+        def ast4 = new MacroClass() {
+            class A { public Integer str }
+        }
+        def ast5 = new MacroClass() {
+            class A { public String str = null }
+        }
+        def ast6 = new MacroClass() {
+            class A { public String str = 'foo' }
+        }
+        def ast7 = new MacroClass() {
+            class A { public String str = 'bar' }
+        }
+        def ast8 = new MacroClass() {
+            class A { public @Foo String str }
+        }
+        def ast9 = new MacroClass() {
+            class A { public @Bar String str }
+        }
+        def ast10 = new MacroClass() {
+            class A { private String str }
+        }
+        assert ast1 instanceof ClassNode
+        assert ASTMatcher.matches(ast1, ast1)
+        assert ASTMatcher.matches(ast1, ast2)
+        assert ASTMatcher.matches(ast2, ast1)
+        assert !ASTMatcher.matches(ast1, ast3)
+        assert !ASTMatcher.matches(ast1, ast4)
+        assert !ASTMatcher.matches(ast1, ast5)
+        assert !ASTMatcher.matches(ast1, ast6)
+        assert !ASTMatcher.matches(ast6, ast7)
+        assert !ASTMatcher.matches(ast1, ast8)
+        assert ASTMatcher.matches(ast8, ast8)
+        assert !ASTMatcher.matches(ast8, ast9)
+        assert !ASTMatcher.matches(ast1, ast10)
+    }
 }