136136import java .util .Set ;
137137import java .util .StringJoiner ;
138138import java .util .concurrent .atomic .AtomicLong ;
139- import java .util .concurrent .atomic .AtomicReference ;
140139import java .util .function .BiPredicate ;
141140import java .util .function .Function ;
142141import java .util .stream .IntStream ;
@@ -1493,23 +1492,22 @@ protected void typeCheckAssignment(final BinaryExpression assignmentExpression,
14931492 }
14941493
14951494 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
14971496 typeCheckingContext .pushEnclosingBinaryExpression (null );
14981497 for (MapEntryExpression entryExpression : mapExpression .getMapEntryExpressions ()) {
14991498 Expression keyExpression = entryExpression .getKeyExpression ();
15001499 if (!(keyExpression instanceof ConstantExpression )) {
15011500 addStaticTypeError ("Dynamic keys in map-style constructors are unsupported in static type checking" , keyExpression );
15021501 } 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 );
15071506 } else {
1508- MethodNode setter = receiverType .getSetterMethod ("set" + MetaClassHelper .capitalize (pName ), false );
1509- ClassNode targetType = setter != null ? setter .getParameters ()[0 ].getType () : pType .get ();
15101507 Expression valueExpression = entryExpression .getValueExpression ();
1511- ClassNode valueType = getType (valueExpression );
1508+ ClassNode valueType = getType (valueExpression );
15121509
1510+ ClassNode targetType = requestor .propertyType ;
15131511 ClassNode resultType = getResultType (targetType , ASSIGN , valueType ,
15141512 assignX (keyExpression , valueExpression , entryExpression ));
15151513 if (!checkCompatibleAssignmentTypes (targetType , resultType , valueExpression )
@@ -1745,8 +1743,7 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
17451743 List <MethodNode > setters = findSetters (current , setterName , false );
17461744 setters = allowStaticAccessToMember (setters , staticOnly );
17471745
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 );
17501747
17511748 PropertyNode propertyNode = current .getProperty (propertyName );
17521749 propertyNode = allowStaticAccessToMember (propertyNode , staticOnly );
@@ -1771,15 +1768,11 @@ && hasAccessToMember(first(enclosingTypes), getter.getDeclaringClass(), getter.g
17711768 } else if (!readMode && checkGetterOrSetter ) {
17721769 if (!setters .isEmpty ()) {
17731770 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 );
17831776 }
17841777 }
17851778 SetterInfo info = new SetterInfo (current , setterName , setters );
@@ -1923,37 +1916,29 @@ private ClassNode getTypeForSpreadExpression(ClassNode testClass, ClassNode obje
19231916 GenericsType [] gts = iteratorType .getGenericsTypes ();
19241917 ClassNode itemType = (gts != null && gts .length == 1 ? getCombinedBoundType (gts [0 ]) : OBJECT_TYPE );
19251918
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 )));
19321922 }
19331923 }
19341924 }
19351925 return null ;
19361926 }
19371927
19381928 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+ }
19571942 }
19581943 return null ;
19591944 }
@@ -6792,12 +6777,48 @@ private SetterInfo(final ClassNode receiverType, final String name, final List<M
67926777 }
67936778 }
67946779
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+
67956817 /**
67966818 * Wrapper for a Parameter so it can be treated like a VariableExpression
67976819 * and tracked in the {@code ifElseForWhileAssignmentTracker}.
67986820 */
67996821 private class ParameterVariableExpression extends VariableExpression {
6800-
68016822 private final Parameter parameter ;
68026823
68036824 ParameterVariableExpression (final Parameter parameter ) {
0 commit comments