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 56fd1b0 GROOVY-8389: StaticImportVisitor: import static closure
property/field
56fd1b0 is described below
commit 56fd1b05eaf9ee7d53ce743ded0afa4677d1810b
Author: Eric Milles <[email protected]>
AuthorDate: Sun Oct 24 17:30:42 2021 -0500
GROOVY-8389: StaticImportVisitor: import static closure property/field
---
.../groovy/control/StaticImportVisitor.java | 100 +++++++----
src/test/groovy/StaticImportTest.groovy | 196 +++++++++++++++++----
src/test/groovy/bugs/Groovy8389Bug.groovy | 111 ------------
3 files changed, 224 insertions(+), 183 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
index 5a31bc1..8ef0b70 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -204,6 +204,7 @@ public class StaticImportVisitor extends
ClassCodeExpressionTransformer {
if (left instanceof StaticMethodCallExpression) {
StaticMethodCallExpression smce = (StaticMethodCallExpression)
left;
StaticMethodCallExpression result = new
StaticMethodCallExpression(smce.getOwnerType(), smce.getMethod(), right);
+ result.copyNodeMetaData(smce);
setSourcePosition(result, be);
return result;
}
@@ -428,59 +429,80 @@ public class StaticImportVisitor extends
ClassCodeExpressionTransformer {
}
private Expression findStaticMethodImportFromModule(Expression method,
Expression args) {
- ModuleNode module = currentClass.getModule();
- if (module == null || !(method instanceof ConstantExpression)) return
null;
- Map<String, ImportNode> importNodes = module.getStaticImports();
- ConstantExpression ce = (ConstantExpression) method;
+ if (currentClass.getModule() == null) return null;
+ if (!(method instanceof ConstantExpression)) return null;
+ if (!(((ConstantExpression) method).getValue() instanceof String))
return null;
+
Expression expression;
- Object value = ce.getValue();
- // skip non-Strings, e.g. Integer
- if (!(value instanceof String)) return null;
- final String name = (String) value;
+ String name = method.getText();
+ Map<String, ImportNode> staticImports =
currentClass.getModule().getStaticImports();
// look for one of these:
- // import static SomeClass.method [as otherName]
- // when resolving methodCall() or getProp() or setProp()
- if (importNodes.containsKey(name)) {
- ImportNode importNode = importNodes.get(name);
+ // import static MyClass.field [as alias]
+ // import static MyClass.method [as alias]
+ // import static MyClass.property [as alias]
+ // when resolving implicit-this call name(args)
+ if (staticImports.containsKey(name)) {
+ ImportNode importNode = staticImports.get(name);
expression = findStaticMethod(importNode.getType(),
importNode.getFieldName(), args);
- if (expression != null) return expression;
- expression =
findStaticPropertyAccessorGivenArgs(importNode.getType(),
getPropNameForAccessor(importNode.getFieldName()), args);
if (expression != null) {
- return newStaticMethodCallX(importNode.getType(),
importNode.getFieldName(), args);
+ return expression;
+ }
+ if (!inClosure) {
+ expression = findStaticPropertyOrField(importNode.getType(),
importNode.getFieldName());
+ if (expression != null) { // assume name refers to a callable
static field or property
+ MethodCallExpression call = new
MethodCallExpression(expression, "call", args);
+ call.setImplicitThis(false);
+ return call;
+ }
}
}
// look for one of these:
- // import static SomeClass.someProp [as otherName]
- // when resolving getProp() or setProp()
- if (isValidAccessorName(name)) {
- String propName = getPropNameForAccessor(name);
- if (importNodes.containsKey(propName)) {
- ImportNode importNode = importNodes.get(propName);
- ClassNode importClass = importNode.getType();
+ // import static MyClass.property [as alias]
+ // import static MyClass.isProperty [as alias]
+ // import static MyClass.getProperty [as alias]
+ // import static MyClass.setProperty [as alias]
+ // when resolving isName(), getName() or setName(args)
+ boolean accessor = isValidAccessorName(name);
+ if (accessor) {
+ ImportNode importNode = staticImports.get(name);
+ if (importNode != null) {
+ String propName =
getPropNameForAccessor(importNode.getFieldName());
+ expression =
findStaticPropertyAccessorGivenArgs(importNode.getType(), propName, args);
+ if (expression != null) { // expression may refer to getter or
setter, so make new call
+ return newStaticMethodCallX(importNode.getType(),
importNode.getFieldName(), args);
+ }
+ }
+ importNode = staticImports.get(getPropNameForAccessor(name));
+ if (importNode != null) {
+ ClassNode importType = importNode.getType();
String importMember = importNode.getFieldName();
- expression = findStaticMethod(importClass, prefix(name) +
capitalize(importMember), args);
+ expression = findStaticMethod(importType, prefix(name) +
capitalize(importMember), args);
if (expression != null) return expression;
- expression = findStaticPropertyAccessorGivenArgs(importClass,
importMember, args);
+ expression = findStaticPropertyAccessorGivenArgs(importType,
importMember, args);
if (expression != null) {
- return newStaticMethodCallX(importClass, prefix(name) +
capitalize(importMember), args);
+ return newStaticMethodCallX(importType, prefix(name) +
capitalize(importMember), args);
}
}
}
- Map<String, ImportNode> starImports = module.getStaticStarImports();
- ClassNode starImportType;
- if (currentClass.isEnum() &&
starImports.containsKey(currentClass.getName())) {
- ImportNode importNode = starImports.get(currentClass.getName());
- starImportType = importNode == null ? null : importNode.getType();
- expression = findStaticMethod(starImportType, name, args);
+
+ Map<String, ImportNode> staticStarImports =
currentClass.getModule().getStaticStarImports();
+ if (currentClass.isEnum() &&
staticStarImports.containsKey(currentClass.getName())) {
+ ImportNode importNode =
staticStarImports.get(currentClass.getName());
+ expression = findStaticMethod(importNode.getType(), name, args);
return expression;
- } else {
- for (ImportNode importNode : starImports.values()) {
- starImportType = importNode == null ? null :
importNode.getType();
- expression = findStaticMethod(starImportType, name, args);
- if (expression != null) return expression;
- expression =
findStaticPropertyAccessorGivenArgs(starImportType,
getPropNameForAccessor(name), args);
- if (expression != null) {
- return newStaticMethodCallX(starImportType, name, args);
+ }
+ // look for one of these:
+ // import static MyClass.*
+ // when resolving name(args), getName(), etc.
+ for (ImportNode importNode : staticStarImports.values()) {
+ ClassNode importType = importNode.getType();
+ expression = findStaticMethod(importType, name, args);
+ if (expression != null) return expression;
+ if (accessor) {
+ String propName = getPropNameForAccessor(name);
+ expression = findStaticPropertyAccessorGivenArgs(importType,
propName, args);
+ if (expression != null) { // expression may refer to getter or
setter, so ...
+ return newStaticMethodCallX(importType, name, args);
}
}
}
diff --git a/src/test/groovy/StaticImportTest.groovy
b/src/test/groovy/StaticImportTest.groovy
index 1ccfca2..445f86d 100644
--- a/src/test/groovy/StaticImportTest.groovy
+++ b/src/test/groovy/StaticImportTest.groovy
@@ -142,43 +142,85 @@ final class StaticImportTest extends
groovy.test.GroovyTestCase {
}
void testStaticImportProperty() {
- def sources = [
- "class Foo { static x = 'foo'" + " }",
- "class Foo { static x = 'foo'" + "; static getX() { x + '_get' }
}",
- "class Foo { static x = 'foo'" + ";
static void setX(newx) { x = newx + '_set' } }",
- "class Foo { static x = 'foo'" + "; static getX() { x + '_get' };
static void setX(newx) { x = newx + '_set' } }"
- ]
- def imports = [
- "import static Foo.*",
- "import static Foo.getX; import static Foo.setX"
- ]
- def results = [
- "assert x == 'foo'; x = 'bar'; assert getX() == 'bar';
setX('baz'); assert 'baz' == x",
- "assert x == 'foo_get'; x = 'bar'; assert getX() == 'bar_get';
setX('baz'); assert 'baz_get' == x",
- "assert x == 'foo'; x = 'bar'; assert getX() == 'bar_set';
setX('baz'); assert 'baz_set' == x",
- "assert x == 'foo_get'; x = 'bar'; assert getX() == 'bar_set_get';
setX('baz'); assert 'baz_set_get' == x"
- ]
- [0..<sources.size(), 0..<imports.size()].combinations().each { i, j ->
- assertScript sources[i] + "\n" + imports[j] + "\n" + results[i]
+ for (imports in ['import static Foo.*', 'import static Foo.getX;
import static Foo.setX']) {
+ assertScript """
+ $imports
+ class Foo {
+ static x = 'foo'
+ }
+ assert x == 'foo'
+ x = 'bar'
+ assert getX() == 'bar'
+ setX('baz')
+ assert 'baz' == x
+ """
+ assertScript """
+ $imports
+ class Foo {
+ static x = 'foo'
+ static getX() { x + '_get' }
+ }
+ assert x == 'foo_get'
+ x = 'bar'
+ assert getX() == 'bar_get'
+ setX('baz')
+ assert 'baz_get' == x
+ """
+ assertScript """
+ $imports
+ class Foo {
+ static x = 'foo'
+ static void setX(newx) { x = newx + '_set' }
+ }
+ assert x == 'foo'
+ x = 'bar'
+ assert getX() == 'bar_set'
+ setX('baz')
+ assert 'baz_set' == x
+ """
+ assertScript """
+ $imports
+ class Foo {
+ static x = 'foo'
+ static getX() { x + '_get' }
+ static void setX(newx) { x = newx + '_set' }
+ }
+ assert x == 'foo_get'
+ x = 'bar'
+ assert getX() == 'bar_set_get'
+ setX('baz')
+ assert 'baz_set_get' == x
+ """
}
}
void testStaticImportPropertyBooleanAlternative() {
- def sources = [
- "class Foo { static x = null" + "; static boolean isX() { x } }",
- "class Foo { static x = null" + "; static boolean isX() { x };
static void setX(newx) { x = newx } }"
- ]
- def imports = [
- "import static Foo.*",
- "import static Foo.x",
- "import static Foo.isX; import static Foo.setX"
- ]
- def results = [
- "assert !x; x = true ; assert isX(); setX(false); assert !x",
- "assert !x; x = false; assert !isX(); setX(true); assert x"
- ]
- [0..<sources.size(), 0..<imports.size()].combinations().each { i, j ->
- assertScript sources[i] + "\n" + imports[j] + "\n" + results[i]
+ for (imports in ['import static Foo.*', 'import static Foo.x', 'import
static Foo.isX; import static Foo.setX']) {
+ assertScript """
+ $imports
+ class Foo {
+ static x
+ static boolean isX() { !!x }
+ }
+ assert !x
+ x = true
+ assert isX()
+ setX(false)
+ assert !x
+ """
+ assertScript """
+ $imports
+ class Foo {
+ static x
+ static boolean isX() { !!x }
+ static void setX(newx) { x = newx }
+ }
+ assert !x
+ x = false
+ assert !isX()
+ setX(true)
+ assert x
+ """
}
}
@@ -259,6 +301,94 @@ final class StaticImportTest extends
groovy.test.GroovyTestCase {
assert err =~ /No such property: y for class/
}
+ // GROOVY-8389
+ void testStaticImportPropertyWithClosure() {
+ assertScript '''
+ import static Foo.bar
+ import static Foo.baz
+ class Foo {
+ static Closure<String> bar = { -> 'property' }
+ static Closure<String> baz = { -> 'property' }
+ }
+ String bar() {
+ 'method'
+ }
+ @groovy.transform.CompileStatic
+ def test() {
+ bar() + ':' + baz()
+ }
+ String result = test()
+ assert result == 'method:property'
+ '''
+ }
+
+ // GROOVY-8389
+ void testStaticImportMethodVsLocalMethod() {
+ assertScript '''
+ import static Foo.bar
+ class Foo {
+ static bar = 'foo'
+ }
+ def bar() {
+ 'bar'
+ }
+ @groovy.transform.CompileStatic
+ def test() {
+ bar()
+ }
+ assert test() == 'bar'
+ '''
+
+ assertScript '''
+ import static Foo.bar
+ class Foo {
+ static bar = 'foo'
+ }
+ static bar() {
+ 'bar'
+ }
+ @groovy.transform.CompileStatic
+ def test() {
+ bar()
+ }
+ assert test() == 'bar'
+ '''
+
+ assertScript '''
+ import static Foo.baz
+ import static Foo.bar
+ class Foo {
+ static bar() { 'foobar' }
+ static baz() { 'foobaz' }
+ }
+ def bar() {
+ 'bar'
+ }
+ @groovy.transform.CompileStatic
+ def test() {
+ "${bar()}${baz()}"
+ }
+ assert test() == 'barfoobaz'
+ '''
+
+ assertScript '''
+ import static Foo.baz
+ import static Foo.bar
+ class Foo {
+ static bar() { 'foobar' }
+ static baz() { 'foobaz' }
+ }
+ static bar() {
+ 'bar'
+ }
+ @groovy.transform.CompileStatic
+ def test() {
+ "${bar()}${baz()}"
+ }
+ assert test() == 'barfoobaz'
+ '''
+ }
+
void testConstructorArgsAliasing() {
// not recommended style to use statics in constructors but supported
assertScript '''
diff --git a/src/test/groovy/bugs/Groovy8389Bug.groovy
b/src/test/groovy/bugs/Groovy8389Bug.groovy
deleted file mode 100644
index 749234d..0000000
--- a/src/test/groovy/bugs/Groovy8389Bug.groovy
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package groovy.bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy8389Bug extends GroovyTestCase {
- void testLocalMethodInvoked() {
- assertScript '''
- import static A.bar
-
- class A {
- static bar = "A"
- }
-
- def bar() {
- "bar"
- }
-
- @groovy.transform.CompileStatic
- def usage() {
- bar()
- }
-
- assert "bar" == usage()
- '''
- }
-
- void testLocalMethodInvoked2() {
- assertScript '''
- import static A.bar
-
- class A {
- static bar = "A"
- }
-
- static bar() {
- "bar"
- }
-
- @groovy.transform.CompileStatic
- def usage() {
- bar()
- }
-
- assert "bar" == usage()
- '''
- }
-
- void testLocalMethodInvoked3() {
- assertScript '''
- import static A.baz
- import static A.bar
-
- class A {
- static bar() { "Abar" }
- static baz() { "Abaz" }
- }
-
- def bar() {
- "bar"
- }
-
- @groovy.transform.CompileStatic
- def usage() {
- "${bar()}${baz()}"
- }
-
- assert usage() == 'barAbaz'
- '''
- }
-
- void testLocalMethodInvoked4() {
- assertScript '''
- import static A.baz
- import static A.bar
-
- class A {
- static bar() { "Abar" }
- static baz() { "Abaz" }
- }
-
- static bar() {
- "bar"
- }
-
- @groovy.transform.CompileStatic
- def usage() {
- "${bar()}${baz()}"
- }
-
- assert usage() == 'barAbaz'
- '''
- }
-}