You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2020/10/03 14:03:41 UTC

[groovy] branch master updated: GROOVY-9757: run test closure only for containing source unit

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 2c294bc  GROOVY-9757: run test closure only for containing source unit
2c294bc is described below

commit 2c294bc982e03e35ca2f301dd205cd60fe7c3e0f
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Oct 3 09:03:27 2020 -0500

    GROOVY-9757: run test closure only for containing source unit
    
    - add error for early phase request
    
    closes #1378
---
 .../groovy/transform/ASTTestTransformation.groovy  | 146 +++++++--------------
 src/spec/test/TestingASTTransformsTest.groovy      |   4 +-
 2 files changed, 53 insertions(+), 97 deletions(-)

diff --git a/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
index e14a8fe..ec5896e 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
@@ -34,18 +34,16 @@ import org.codehaus.groovy.ast.stmt.Statement
 import org.codehaus.groovy.control.CompilationUnit
 import org.codehaus.groovy.control.CompilePhase
 import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.ErrorCollector
 import org.codehaus.groovy.control.Janitor
-import org.codehaus.groovy.control.ProcessingUnit
 import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.control.CompilationUnit.ISourceUnitOperation
 import org.codehaus.groovy.control.customizers.ImportCustomizer
-import org.codehaus.groovy.control.io.ReaderSource
 import org.codehaus.groovy.runtime.MethodClosure
 import org.codehaus.groovy.syntax.SyntaxException
-import org.codehaus.groovy.tools.Utilities
 
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
+import static org.codehaus.groovy.control.CompilePhase.fromPhaseNumber as toCompilePhase
 
 @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
 class ASTTestTransformation implements ASTTransformation, CompilationUnitAware {
@@ -55,6 +53,7 @@ class ASTTestTransformation implements ASTTransformation, CompilationUnitAware {
     @Override
     void visit(final ASTNode[] nodes, final SourceUnit source) {
         AnnotationNode annotationNode = nodes[0]
+
         def member = annotationNode.getMember('phase')
         CompilePhase phase = null
         if (member) {
@@ -64,7 +63,12 @@ class ASTTestTransformation implements ASTTransformation, CompilationUnitAware {
                 phase = CompilePhase.valueOf(member.propertyAsString)
             }
             annotationNode.setMember('phase', propX(classX(ClassHelper.make(CompilePhase)), phase.toString()))
+
+            if (phase.phaseNumber < compilationUnit.phase) {
+                throw new SyntaxException('ASTTest phase must be at least ' + toCompilePhase(compilationUnit.phase), member)
+            }
         }
+
         member = annotationNode.getMember('value')
         if (member && !(member instanceof ClosureExpression)) {
             throw new SyntaxException('ASTTest value must be a closure', member.lineNumber, member.columnNumber)
@@ -72,114 +76,66 @@ class ASTTestTransformation implements ASTTransformation, CompilationUnitAware {
         if (!member && !annotationNode.getNodeMetaData(ASTTestTransformation)) {
             throw new SyntaxException('Missing test expression', annotationNode.lineNumber, annotationNode.columnNumber)
         }
+
         // convert value into node metadata so that the expression doesn't mix up with other AST xforms like STC
         annotationNode.setNodeMetaData(ASTTestTransformation, member)
         annotationNode.setMember('value', new ClosureExpression(
             Parameter.EMPTY_ARRAY, EmptyStatement.INSTANCE))
         member.variableScope.@parent = null
 
-        def pcallback = compilationUnit.progressCallback
-        def callback = new CompilationUnit.ProgressCallback() {
-            private final Binding binding = new Binding([:].withDefault { null })
-
-            @Override
-            void call(final ProcessingUnit context, final int phaseNumber) {
-                if (phase == null || phaseNumber == phase.phaseNumber) {
-                    ClosureExpression testClosure = nodes[0].getNodeMetaData(ASTTestTransformation)
-                    StringBuilder sb = new StringBuilder()
-                    for (int i = testClosure.lineNumber; i <= testClosure.lastLineNumber; i += 1) {
-                        sb.append(source.source.getLine(i, new Janitor())).append('\n')
-                    }
-                    def testSource = sb[testClosure.columnNumber..<sb.length()]
-                    testSource = testSource[0..<testSource.lastIndexOf('}')]
-
-                    binding['node'] = nodes[1]
-                    binding['sourceUnit'] = source
-                    binding['compilationUnit'] = compilationUnit
-                    binding['compilePhase'] = CompilePhase.fromPhaseNumber(phaseNumber)
-                    binding['lookup'] = new MethodClosure(LabelFinder, 'lookup').curry(nodes[1])
-
-                    def customizer = new ImportCustomizer()
-                    source.AST.imports.each {
-                        customizer.addImport(it.alias, it.type.name)
-                    }
-                    source.AST.starImports.each {
-                        customizer.addStarImports(it.packageName)
-                    }
-                    source.AST.staticImports.each {
-                        customizer.addStaticImport(it.value.alias, it.value.type.name, it.value.fieldName)
-                    }
-                    source.AST.staticStarImports.each {
-                        customizer.addStaticStars(it.value.className)
-                    }
-
-                    def config = new CompilerConfiguration()
-                    config.addCompilationCustomizers(customizer)
-                    new GroovyShell(binding, config).evaluate(testSource)
-                }
-            }
+        ISourceUnitOperation astTester = new ASTTester(astNode: nodes[1], sourceUnit: source, testClosure: annotationNode.getNodeMetaData(ASTTestTransformation))
+        for (int p = (phase ?: CompilePhase.SEMANTIC_ANALYSIS).phaseNumber, q = (phase ?: CompilePhase.FINALIZATION).phaseNumber; p <= q; p += 1) {
+            compilationUnit.addNewPhaseOperation(astTester, p)
         }
-
-        if (pcallback != null) {
-            if (pcallback instanceof ProgressCallbackChain) {
-                pcallback.addCallback(callback)
-            } else {
-                pcallback = new ProgressCallbackChain(pcallback, callback)
-            }
-            callback = pcallback
-        }
-
-        compilationUnit.progressCallback = callback
     }
 
-    private static class AssertionSourceDelegatingSourceUnit extends SourceUnit {
-        private final ReaderSource delegate
+    //--------------------------------------------------------------------------
 
-        AssertionSourceDelegatingSourceUnit(final String name, final ReaderSource source, final CompilerConfiguration config, final GroovyClassLoader loader, final ErrorCollector er) {
-            super(name, '', config, loader, er)
-            delegate = source
-        }
+    private class ASTTester implements ISourceUnitOperation {
+
+        ASTNode astNode
+        SourceUnit sourceUnit
+        ClosureExpression testClosure
+        private final Binding binding = new Binding([:].withDefault { null })
 
         @Override
-        String getSample(final int line, final int column, final Janitor janitor) {
-            String sample = null
-            String text = delegate.getLine(line, janitor)
-
-            if (text != null) {
-                if (column > 0) {
-                    String marker = Utilities.repeatString(' ', column - 1) + '^'
-
-                    if (column > 40) {
-                        int start = column - 30 - 1
-                        int end = (column + 10 > text.length() ? text.length() : column + 10 - 1)
-                        sample = '   ' + text[start..<end] + Utilities.eol() + '   ' + marker[start..<marker.length()]
-                    } else {
-                        sample = '   ' + text + Utilities.eol() + '   ' + marker
-                    }
-                } else {
-                    sample = text
-                }
+        void call(final SourceUnit source) {
+            if (source == sourceUnit) {
+                test()
             }
-            sample
         }
-    }
-
-    private static class ProgressCallbackChain implements CompilationUnit.ProgressCallback {
-        private final List<CompilationUnit.ProgressCallback> chain = [] as LinkedList
 
-        ProgressCallbackChain(final CompilationUnit.ProgressCallback... callbacks) {
-            if (callbacks) {
-                callbacks.each { addCallback(it) }
+        private void test() {
+            def sb = new StringBuilder()
+            for (int i = testClosure.lineNumber, n = testClosure.lastLineNumber; i <= n; i += 1) {
+                sb.append(sourceUnit.source.getLine(i, new Janitor())).append('\n')
+            }
+            sb = sb[testClosure.columnNumber..<sb.length()]
+            String testSource = sb[0..<sb.lastIndexOf('}')]
+
+            binding['node'] = astNode
+            binding['sourceUnit'] = sourceUnit
+            binding['compilationUnit'] = compilationUnit
+            binding['compilePhase'] = toCompilePhase(compilationUnit.phase)
+            binding['lookup'] = new MethodClosure(LabelFinder, 'lookup').curry(astNode)
+
+            def customizer = new ImportCustomizer()
+            sourceUnit.AST.imports.each {
+                customizer.addImport(it.alias, it.type.name)
+            }
+            sourceUnit.AST.starImports.each {
+                customizer.addStarImports(it.packageName)
+            }
+            sourceUnit.AST.staticImports.each {
+                customizer.addStaticImport(it.value.alias, it.value.type.name, it.value.fieldName)
+            }
+            sourceUnit.AST.staticStarImports.each {
+                customizer.addStaticStars(it.value.className)
             }
-        }
-
-        void addCallback(final CompilationUnit.ProgressCallback callback) {
-            chain << callback
-        }
 
-        @Override
-        void call(final ProcessingUnit context, final int phase) {
-            chain*.call(context, phase)
+            def config = new CompilerConfiguration()
+            config.addCompilationCustomizers(customizer)
+            new GroovyShell(binding, config).evaluate(testSource)
         }
     }
 
diff --git a/src/spec/test/TestingASTTransformsTest.groovy b/src/spec/test/TestingASTTransformsTest.groovy
index 58b0fb2..47b91d4 100644
--- a/src/spec/test/TestingASTTransformsTest.groovy
+++ b/src/spec/test/TestingASTTransformsTest.groovy
@@ -54,7 +54,7 @@ new MathsTest().testFib()'''
     }
 
     void testASTTest() {
-        assertScript '''// tag::asttest_basic[]
+        def err = shouldFail '''// tag::asttest_basic[]
 import groovy.transform.ASTTest
 import org.codehaus.groovy.ast.ClassNode
 
@@ -63,11 +63,11 @@ import org.codehaus.groovy.ast.ClassNode
     assert node.name == 'Person'     // <3>
 })
 class Person {
-
 }
 // end::asttest_basic[]
 def p = new Person()
 '''
+        assert err =~ /ASTTest phase must be at least SEMANTIC_ANALYSIS/
     }
 
     void testASTTestWithPackageScope() {