136
136
import java .util .Set ;
137
137
import java .util .StringJoiner ;
138
138
import java .util .concurrent .atomic .AtomicLong ;
139
- import java .util .concurrent .atomic .AtomicReference ;
140
139
import java .util .function .BiPredicate ;
141
140
import java .util .function .Function ;
142
141
import java .util .stream .IntStream ;
@@ -1493,23 +1492,22 @@ protected void typeCheckAssignment(final BinaryExpression assignmentExpression,
1493
1492
}
1494
1493
1495
1494
protected void checkGroovyConstructorMap (final Expression receiver , final ClassNode receiverType , final MapExpression mapExpression ) {
1496
- // workaround for map-style checks putting setter info on wrong AST nodes
1495
+ // workaround for map-style checks putting setter info on wrong AST node
1497
1496
typeCheckingContext .pushEnclosingBinaryExpression (null );
1498
1497
for (MapEntryExpression entryExpression : mapExpression .getMapEntryExpressions ()) {
1499
1498
Expression keyExpression = entryExpression .getKeyExpression ();
1500
1499
if (!(keyExpression instanceof ConstantExpression )) {
1501
1500
addStaticTypeError ("Dynamic keys in map-style constructors are unsupported in static type checking" , keyExpression );
1502
1501
} else {
1503
- String pName = keyExpression .getText ();
1504
- AtomicReference < ClassNode > pType = new AtomicReference <>( );
1505
- if (!existsProperty (new PropertyExpression (varX ("_" , receiverType ), pName ), false , new PropertyLookupVisitor ( pType ) )) {
1506
- addStaticTypeError ("No such property: " + pName + " for class: " + receiverType . getText ( ), receiver );
1502
+ String propName = keyExpression .getText ();
1503
+ PropertyLookup requestor = new PropertyLookup ( receiverType );
1504
+ if (!existsProperty (new PropertyExpression (varX ("_" , receiverType ), propName ), false , requestor )) {
1505
+ addStaticTypeError ("No such property: " + propName + " for class: " + prettyPrintTypeName ( receiverType ), receiver );
1507
1506
} else {
1508
- MethodNode setter = receiverType .getSetterMethod ("set" + MetaClassHelper .capitalize (pName ), false );
1509
- ClassNode targetType = setter != null ? setter .getParameters ()[0 ].getType () : pType .get ();
1510
1507
Expression valueExpression = entryExpression .getValueExpression ();
1511
- ClassNode valueType = getType (valueExpression );
1508
+ ClassNode valueType = getType (valueExpression );
1512
1509
1510
+ ClassNode targetType = requestor .propertyType ;
1513
1511
ClassNode resultType = getResultType (targetType , ASSIGN , valueType ,
1514
1512
assignX (keyExpression , valueExpression , entryExpression ));
1515
1513
if (!checkCompatibleAssignmentTypes (targetType , resultType , valueExpression )
@@ -1745,8 +1743,7 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
1745
1743
List <MethodNode > setters = findSetters (current , setterName , false );
1746
1744
setters = allowStaticAccessToMember (setters , staticOnly );
1747
1745
1748
- // need to visit even if we only look for a setters for compatibility
1749
- if (visitor != null && getter != null ) visitor .visitMethod (getter );
1746
+ if (readMode && getter != null && visitor != null ) visitor .visitMethod (getter );
1750
1747
1751
1748
PropertyNode propertyNode = current .getProperty (propertyName );
1752
1749
propertyNode = allowStaticAccessToMember (propertyNode , staticOnly );
@@ -1771,15 +1768,11 @@ && hasAccessToMember(first(enclosingTypes), getter.getDeclaringClass(), getter.g
1771
1768
} else if (!readMode && checkGetterOrSetter ) {
1772
1769
if (!setters .isEmpty ()) {
1773
1770
if (visitor != null ) {
1774
- if (field != null ) {
1775
- visitor .visitField (field );
1776
- } else {
1777
- for (MethodNode setter : setters ) {
1778
- // visiting setter will not infer the property type since return type is void, so visit a dummy field instead
1779
- FieldNode virtual = new FieldNode (propertyName , 0 , setter .getParameters ()[0 ].getOriginType (), current , null );
1780
- virtual .setDeclaringClass (setter .getDeclaringClass ());
1781
- visitor .visitField (virtual );
1782
- }
1771
+ for (MethodNode setter : setters ) {
1772
+ // visiting setter will not infer the property type since return type is void, so visit a dummy field instead
1773
+ FieldNode virtual = new FieldNode (propertyName , 0 , setter .getParameters ()[0 ].getOriginType (), current , null );
1774
+ virtual .setDeclaringClass (setter .getDeclaringClass ());
1775
+ visitor .visitField (virtual );
1783
1776
}
1784
1777
}
1785
1778
SetterInfo info = new SetterInfo (current , setterName , setters );
@@ -1923,37 +1916,29 @@ private ClassNode getTypeForSpreadExpression(ClassNode testClass, ClassNode obje
1923
1916
GenericsType [] gts = iteratorType .getGenericsTypes ();
1924
1917
ClassNode itemType = (gts != null && gts .length == 1 ? getCombinedBoundType (gts [0 ]) : OBJECT_TYPE );
1925
1918
1926
- PropertyExpression subExp = new PropertyExpression (varX ("{}" , itemType ), pexp .getPropertyAsString ());
1927
- AtomicReference <ClassNode > result = new AtomicReference <>();
1928
- if (existsProperty (subExp , true , new PropertyLookupVisitor (result ))) {
1929
- ClassNode listType = LIST_TYPE .getPlainNodeReference ();
1930
- listType .setGenericsTypes (new GenericsType []{new GenericsType (wrapTypeIfNecessary (result .get ()))});
1931
- return listType ;
1919
+ PropertyLookup requestor = new PropertyLookup (itemType );
1920
+ if (existsProperty (new PropertyExpression (varX ("{}" , itemType ), pexp .getPropertyAsString ()), true , requestor )) {
1921
+ return GenericsUtils .makeClassSafe0 (LIST_TYPE , new GenericsType (wrapTypeIfNecessary (requestor .propertyType )));
1932
1922
}
1933
1923
}
1934
1924
}
1935
1925
return null ;
1936
1926
}
1937
1927
1938
1928
private ClassNode getTypeForListPropertyExpression (ClassNode testClass , ClassNode objectExpressionType , PropertyExpression pexp ) {
1939
- if (!implementsInterfaceOrIsSubclassOf (testClass , LIST_TYPE )) return null ;
1940
- /* GRECLIPSE edit -- GROOVY-9821
1941
- ClassNode intf = GenericsUtils.parameterizeType(objectExpressionType, LIST_TYPE.getPlainNodeReference());
1942
- GenericsType[] types = intf.getGenericsTypes();
1943
- if (types == null || types.length != 1) return OBJECT_TYPE;
1944
-
1945
- PropertyExpression subExp = new PropertyExpression(varX("{}", types[0].getType()), pexp.getPropertyAsString());
1946
- */
1947
- GenericsType [] types = (testClass .equals (LIST_TYPE ) ? testClass : GenericsUtils .parameterizeType (testClass , LIST_TYPE )).getGenericsTypes ();
1948
- ClassNode itemType = (types != null && types .length == 1 ? getCombinedBoundType (types [0 ]) : OBJECT_TYPE );
1949
-
1950
- PropertyExpression subExp = new PropertyExpression (varX ("{}" , itemType ), pexp .getPropertyAsString ());
1951
- // GRECLIPSE end
1952
- AtomicReference <ClassNode > result = new AtomicReference <ClassNode >();
1953
- if (existsProperty (subExp , true , new PropertyLookupVisitor (result ))) {
1954
- ClassNode intf = LIST_TYPE .getPlainNodeReference ();
1955
- intf .setGenericsTypes (new GenericsType []{new GenericsType (wrapTypeIfNecessary (result .get ()))});
1956
- return intf ;
1929
+ if (implementsInterfaceOrIsSubclassOf (testClass , LIST_TYPE )) {
1930
+ /* GRECLIPSE edit -- GROOVY-9821
1931
+ ClassNode listType = GenericsUtils.parameterizeType(objectExpressionType, LIST_TYPE.getPlainNodeReference());
1932
+ GenericsType[] gts = listType.getGenericsTypes();
1933
+ ClassNode itemType = (gts != null && gts.length == 1 ? gts[0].getType() : OBJECT_TYPE);
1934
+ */
1935
+ GenericsType [] gts = (testClass .equals (LIST_TYPE ) ? testClass : GenericsUtils .parameterizeType (testClass , LIST_TYPE )).getGenericsTypes ();
1936
+ ClassNode itemType = (gts != null && gts .length == 1 ? getCombinedBoundType (gts [0 ]) : OBJECT_TYPE );
1937
+ // GRECLIPSE end
1938
+ PropertyLookup requestor = new PropertyLookup (itemType );
1939
+ if (existsProperty (new PropertyExpression (varX ("{}" , itemType ), pexp .getPropertyAsString ()), true , requestor )) {
1940
+ return GenericsUtils .makeClassSafe0 (LIST_TYPE , new GenericsType (wrapTypeIfNecessary (requestor .propertyType )));
1941
+ }
1957
1942
}
1958
1943
return null ;
1959
1944
}
@@ -6792,12 +6777,48 @@ private SetterInfo(final ClassNode receiverType, final String name, final List<M
6792
6777
}
6793
6778
}
6794
6779
6780
+ private class PropertyLookup extends ClassCodeVisitorSupport {
6781
+ ClassNode propertyType , receiverType ;
6782
+
6783
+ PropertyLookup (final ClassNode type ) {
6784
+ receiverType = type ;
6785
+ }
6786
+
6787
+ @ Override
6788
+ protected SourceUnit getSourceUnit () {
6789
+ return StaticTypeCheckingVisitor .this .getSourceUnit ();
6790
+ }
6791
+
6792
+ @ Override
6793
+ public void visitField (final FieldNode node ) {
6794
+ storePropertyType (node .getType (), node .isStatic () ? null : node .getDeclaringClass ());
6795
+ }
6796
+
6797
+ @ Override
6798
+ public void visitMethod (final MethodNode node ) {
6799
+ storePropertyType (node .getReturnType (), node .isStatic () ? null : node .getDeclaringClass ());
6800
+ }
6801
+
6802
+ @ Override
6803
+ public void visitProperty (final PropertyNode node ) {
6804
+ storePropertyType (node .getOriginType (), node .isStatic () ? null : node .getDeclaringClass ());
6805
+ }
6806
+
6807
+ private void storePropertyType (ClassNode type , final ClassNode declaringClass ) {
6808
+ if (declaringClass != null && GenericsUtils .hasUnresolvedGenerics (type )) { // GROOVY-10787
6809
+ Map <GenericsTypeName , GenericsType > spec = extractPlaceHolders (null , receiverType , declaringClass );
6810
+ type = applyGenericsContext (spec , type );
6811
+ }
6812
+ // TODO: if (propertyType != null) merge types
6813
+ propertyType = type ;
6814
+ }
6815
+ }
6816
+
6795
6817
/**
6796
6818
* Wrapper for a Parameter so it can be treated like a VariableExpression
6797
6819
* and tracked in the {@code ifElseForWhileAssignmentTracker}.
6798
6820
*/
6799
6821
private class ParameterVariableExpression extends VariableExpression {
6800
-
6801
6822
private final Parameter parameter ;
6802
6823
6803
6824
ParameterVariableExpression (final Parameter parameter ) {
0 commit comments