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 2022/10/06 16:54:31 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-7164, GROOVY-10787: STC: resolve placeholders for property lookup
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new 31e0554a8f GROOVY-7164, GROOVY-10787: STC: resolve placeholders for property lookup
31e0554a8f is described below
commit 31e0554a8f4309d6a45395c7f24717ba4a7648c3
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Oct 6 10:58:09 2022 -0500
GROOVY-7164, GROOVY-10787: STC: resolve placeholders for property lookup
---
.../transform/stc/PropertyLookupVisitor.java | 60 -----------------
.../transform/stc/StaticTypeCheckingVisitor.java | 77 +++++++++++++++-------
.../transform/stc/ConstructorsSTCTest.groovy | 30 ++++++---
3 files changed, 75 insertions(+), 92 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/PropertyLookupVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/PropertyLookupVisitor.java
deleted file mode 100644
index 83f1d2db64..0000000000
--- a/src/main/java/org/codehaus/groovy/transform/stc/PropertyLookupVisitor.java
+++ /dev/null
@@ -1,60 +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 org.codehaus.groovy.transform.stc;
-
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.FieldNode;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.PropertyNode;
-import org.codehaus.groovy.control.SourceUnit;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * A visitor used as a callback to {@link org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor#existsProperty(org.codehaus.groovy.ast.expr.PropertyExpression, boolean, org.codehaus.groovy.ast.ClassCodeVisitorSupport)}
- * which will return set the type of the found property in the provided reference.
- */
-class PropertyLookupVisitor extends ClassCodeVisitorSupport {
- private final AtomicReference<ClassNode> result;
-
- public PropertyLookupVisitor(final AtomicReference<ClassNode> result) {
- this.result = result;
- }
-
- @Override
- protected SourceUnit getSourceUnit() {
- return null;
- }
-
- @Override
- public void visitMethod(final MethodNode node) {
- result.set(node.getReturnType());
- }
-
- @Override
- public void visitProperty(final PropertyNode node) {
- result.set(node.getType());
- }
-
- @Override
- public void visitField(final FieldNode field) {
- result.set(field.getType());
- }
-}
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 1269a9eb8c..329c3a6608 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -135,7 +135,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -1328,23 +1327,22 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void checkGroovyConstructorMap(final Expression receiver, final ClassNode receiverType, final MapExpression mapExpression) {
- // workaround for map-style checks putting setter info on wrong AST nodes
+ // workaround for map-style checks putting setter info on wrong AST node
typeCheckingContext.pushEnclosingBinaryExpression(null);
for (MapEntryExpression entryExpression : mapExpression.getMapEntryExpressions()) {
Expression keyExpression = entryExpression.getKeyExpression();
if (!(keyExpression instanceof ConstantExpression)) {
addStaticTypeError("Dynamic keys in map-style constructors are unsupported in static type checking", keyExpression);
} else {
- String pName = keyExpression.getText();
- AtomicReference<ClassNode> pType = new AtomicReference<>();
- if (!existsProperty(propX(varX("_", receiverType), pName), false, new PropertyLookupVisitor(pType))) {
- addStaticTypeError("No such property: " + pName + " for class: " + prettyPrintTypeName(receiverType), receiver);
+ String propName = keyExpression.getText();
+ PropertyLookup requestor = new PropertyLookup(receiverType);
+ if (!existsProperty(propX(varX("_", receiverType), propName), false, requestor)) {
+ addStaticTypeError("No such property: " + propName + " for class: " + prettyPrintTypeName(receiverType), receiver);
} else {
- ClassNode targetType = Optional.ofNullable(receiverType.getSetterMethod(getSetterName(pName), false))
- .map(setter -> setter.getParameters()[0].getType()).orElseGet(pType::get);
Expression valueExpression = entryExpression.getValueExpression();
- ClassNode valueType = getType(valueExpression);
+ ClassNode valueType = getType(valueExpression);
+ ClassNode targetType = requestor.propertyType;
ClassNode resultType = getResultType(targetType, ASSIGN, valueType,
assignX(keyExpression, valueExpression, entryExpression));
if (!checkCompatibleAssignmentTypes(targetType, resultType, valueExpression)
@@ -1562,8 +1560,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
List<MethodNode> setters = findSetters(current, getSetterName(propertyName), /*enforce void:*/false);
setters = allowStaticAccessToMember(setters, staticOnly);
- // need to visit even if we only look for setters for compatibility
- if (visitor != null && getter != null) visitor.visitMethod(getter);
+ if (readMode && getter != null && visitor != null) visitor.visitMethod(getter);
PropertyNode property = current.getProperty(propertyName);
property = allowStaticAccessToMember(property, staticOnly);
@@ -1583,15 +1580,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
} else {
if (!setters.isEmpty()) {
if (visitor != null) {
- if (field != null) {
- visitor.visitField(field);
- } else {
- for (MethodNode setter : setters) {
- // visiting setter will not infer the property type since return type is void, so visit a dummy field instead
- FieldNode virtual = new FieldNode(propertyName, 0, setter.getParameters()[0].getOriginType(), current, null);
- virtual.setDeclaringClass(setter.getDeclaringClass());
- visitor.visitField(virtual);
- }
+ for (MethodNode setter : setters) {
+ // visiting setter will not infer the property type since return type is void, so visit a dummy field instead
+ FieldNode virtual = new FieldNode(propertyName, 0, setter.getParameters()[0].getOriginType(), current, null);
+ virtual.setDeclaringClass(setter.getDeclaringClass());
+ visitor.visitField(virtual);
}
}
SetterInfo info = new SetterInfo(current, getSetterName(propertyName), setters);
@@ -1713,9 +1706,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
GenericsType[] gts = compositeType.getGenericsTypes();
ClassNode itemType = (gts != null && gts.length == 1 ? getCombinedBoundType(gts[0]) : OBJECT_TYPE);
- AtomicReference<ClassNode> propertyType = new AtomicReference<>();
- if (existsProperty(propX(varX("{}", itemType), prop), true, new PropertyLookupVisitor(propertyType))) {
- return extension.buildListType(propertyType.get());
+ PropertyLookup requestor = new PropertyLookup(itemType);
+ if (existsProperty(propX(varX("{}", itemType), prop), true, requestor)) {
+ return extension.buildListType(requestor.propertyType);
}
return null;
}
@@ -6042,12 +6035,48 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
+ private class PropertyLookup extends ClassCodeVisitorSupport {
+ ClassNode propertyType, receiverType;
+
+ PropertyLookup(final ClassNode type) {
+ receiverType = type;
+ }
+
+ @Override
+ protected SourceUnit getSourceUnit() {
+ return StaticTypeCheckingVisitor.this.getSourceUnit();
+ }
+
+ @Override
+ public void visitField(final FieldNode node) {
+ storePropertyType(node.getType(), node.isStatic() ? null : node.getDeclaringClass());
+ }
+
+ @Override
+ public void visitMethod(final MethodNode node) {
+ storePropertyType(node.getReturnType(), node.isStatic() ? null : node.getDeclaringClass());
+ }
+
+ @Override
+ public void visitProperty(final PropertyNode node) {
+ storePropertyType(node.getOriginType(), node.isStatic() ? null : node.getDeclaringClass());
+ }
+
+ private void storePropertyType(ClassNode type, final ClassNode declaringClass) {
+ if (declaringClass != null && GenericsUtils.hasUnresolvedGenerics(type)) { // GROOVY-10787
+ Map<GenericsTypeName, GenericsType> spec = extractPlaceHolders(receiverType, declaringClass);
+ type = applyGenericsContext(spec, type);
+ }
+ // TODO: if (propertyType != null) merge types
+ propertyType = type;
+ }
+ }
+
/**
* Wrapper for a Parameter so it can be treated like a VariableExpression
* and tracked in the {@code ifElseForWhileAssignmentTracker}.
*/
private class ParameterVariableExpression extends VariableExpression {
-
private final Parameter parameter;
ParameterVariableExpression(final Parameter parameter) {
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index a6897a8983..353b0cc4b2 100644
--- a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
@@ -496,25 +496,39 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- //GROOVY-7164
- void testDefaultConstructorWhenSetterParamAndFieldHaveDifferentTypes() {
+ // GROOVY-7164
+ void testMapStyleConstructorWhenSetterParamAndFieldHaveDifferentTypes() {
assertScript '''
- class Test {
+ class C {
private long timestamp
Date getTimestamp() {
return timestamp ? new Date(timestamp) : null
}
- void setTimestamp (Date timestamp) {
+ void setTimestamp(Date timestamp) {
this.timestamp = timestamp.time
}
+ }
+ new C(timestamp: new Date())
+ '''
+ }
- def main() {
- new Test(timestamp: new Date())
- }
+ // GROOVY-10787
+ void testMapStyleConstructorWithParameterizedProperty() {
+ assertScript '''
+ abstract class A<X extends Serializable> {
+ X x
+ }
+ class C<Y extends Serializable> extends A<Y> {
}
- new Test().main()
+
+ def <Z extends Number> C<Z> fn(List<Z> list_of_z) {
+ new C(x: list_of_z.first())
+ }
+
+ def c = fn([42])
+ assert c.x == 42
'''
}