@@ -1214,7 +1214,7 @@ protected void inferDiamondType(final ConstructorCallExpression cce, final Class
1214
1214
// check if constructor call expression makes use of the diamond operator
1215
1215
if (cceType .getGenericsTypes () != null && cceType .getGenericsTypes ().length == 0 ) {
1216
1216
ArgumentListExpression argumentListExpression = InvocationWriter .makeArgumentList (cce .getArguments ());
1217
- /* GRECLIPSE edit -- GROOVY-9948
1217
+ /* GRECLIPSE edit -- GROOVY-9948, GROOVY-9983
1218
1218
if (argumentListExpression.getExpressions().isEmpty()) {
1219
1219
adjustGenerics(lType, cceType);
1220
1220
} else {
@@ -1232,7 +1232,7 @@ protected void inferDiamondType(final ConstructorCallExpression cce, final Class
1232
1232
type = inferReturnTypeGenerics (type , constructor , argumentListExpression );
1233
1233
if (type .isUsingGenerics ()) {
1234
1234
// GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
1235
- if (checkCompatibleAssignmentTypes (lType , type , cce ) && !GenericsUtils .buildWildcardType (lType ).isCompatibleWith (type )) {
1235
+ if (GenericsUtils . hasUnresolvedGenerics ( type ) || checkCompatibleAssignmentTypes (lType , type , cce ) && !GenericsUtils .buildWildcardType (lType ).isCompatibleWith (type )) {
1236
1236
// allow covariance of each type parameter, but maintain semantics for nested generics
1237
1237
1238
1238
ClassNode pType = GenericsUtils .parameterizeType (lType , type );
@@ -2264,7 +2264,7 @@ public void visitField(final FieldNode node) {
2264
2264
}
2265
2265
}
2266
2266
2267
- // GRECLIPSE add
2267
+ // GRECLIPSE add -- GROOVY-9977, GROOVY-9983, GROOVY-9995
2268
2268
private void visitInitialExpression (final Expression value , final Expression target , final ASTNode position ) {
2269
2269
if (value != null ) {
2270
2270
ClassNode lType = target .getType ();
@@ -2273,21 +2273,25 @@ private void visitInitialExpression(final Expression value, final Expression tar
2273
2273
} else if (isClosureWithType (lType ) && value instanceof ClosureExpression ) {
2274
2274
storeInferredReturnType (value , getCombinedBoundType (lType .getGenericsTypes ()[0 ]));
2275
2275
}
2276
+
2277
+ typeCheckingContext .pushEnclosingBinaryExpression (assignX (target , value , position ));
2278
+
2276
2279
value .visit (this );
2277
2280
ClassNode rType = getType (value );
2278
2281
if (value instanceof ConstructorCallExpression ) {
2279
2282
inferDiamondType ((ConstructorCallExpression ) value , lType );
2280
2283
}
2281
2284
2282
- BinaryExpression bexp = binX (
2283
- target ,
2284
- Token .newSymbol ("=" , position .getLineNumber (), position .getColumnNumber ()),
2285
- value
2286
- );
2287
- bexp .setSourcePosition (position );
2285
+ BinaryExpression bexp = typeCheckingContext .popEnclosingBinaryExpression ();
2288
2286
typeCheckAssignment (bexp , target , lType , value , getResultType (lType , ASSIGN , rType , bexp ));
2289
2287
}
2290
2288
}
2289
+
2290
+ private static BinaryExpression assignX (final Expression lhs , final Expression rhs , final ASTNode pos ) {
2291
+ BinaryExpression exp = (BinaryExpression ) GeneralUtils .assignX (lhs , rhs );
2292
+ exp .setSourcePosition (pos );
2293
+ return exp ;
2294
+ }
2291
2295
// GRECLIPSE end
2292
2296
2293
2297
@ Override
@@ -4742,9 +4746,9 @@ public void visitTernaryExpression(final TernaryExpression expression) {
4742
4746
falseExpression .visit (this );
4743
4747
ClassNode resultType ;
4744
4748
ClassNode typeOfFalse = getType (falseExpression );
4745
- // GRECLIPSE edit
4746
- // ClassNode typeOfTrue = getType(trueExpression);
4747
- // GRECLIPSE end
4749
+ /* GRECLIPSE edit
4750
+ ClassNode typeOfTrue = getType(trueExpression);
4751
+ */
4748
4752
// handle instanceof cases
4749
4753
if (hasInferredReturnType (falseExpression )) {
4750
4754
typeOfFalse = falseExpression .getNodeMetaData (StaticTypesMarker .INFERRED_RETURN_TYPE );
@@ -4772,7 +4776,7 @@ public void visitTernaryExpression(final TernaryExpression expression) {
4772
4776
}
4773
4777
*/
4774
4778
if (isNullConstant (trueExpression ) && isNullConstant (falseExpression )) { // GROOVY-5523
4775
- resultType = checkForTargetType (expression , UNKNOWN_PARAMETER_TYPE );
4779
+ resultType = checkForTargetType (trueExpression , UNKNOWN_PARAMETER_TYPE );
4776
4780
} else if (isNullConstant (trueExpression ) || (isEmptyCollection (trueExpression )
4777
4781
&& isOrImplements (typeOfTrue , typeOfFalse ))) { // [] : List/Collection/Iterable
4778
4782
resultType = wrapTypeIfNecessary (checkForTargetType (falseExpression , typeOfFalse ));
@@ -4790,7 +4794,7 @@ && isOrImplements(typeOfFalse, typeOfTrue))) { // List/Collection/Iterable : []
4790
4794
}
4791
4795
4792
4796
private ClassNode checkForTargetType (final Expression expr , final ClassNode type ) {
4793
- /* GRECLIPSE edit -- GROOVY-9972
4797
+ /* GRECLIPSE edit -- GROOVY-9972, GROOVY-9983
4794
4798
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4795
4799
if (enclosingBinaryExpression instanceof DeclarationExpression
4796
4800
&& isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
@@ -4812,23 +4816,19 @@ && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperatio
4812
4816
ClassNode sourceType = type , targetType = null ;
4813
4817
MethodNode enclosingMethod = typeCheckingContext .getEnclosingMethod ();
4814
4818
BinaryExpression enclosingBinaryExpression = typeCheckingContext .getEnclosingBinaryExpression ();
4815
- if (enclosingBinaryExpression instanceof DeclarationExpression
4819
+ if (enclosingBinaryExpression != null
4816
4820
&& isAssignment (enclosingBinaryExpression .getOperation ().getType ())
4817
4821
&& isTypeSource (expr , enclosingBinaryExpression .getRightExpression ())) {
4818
- targetType = enclosingBinaryExpression .getLeftExpression ().getType ();
4819
- } else if (currentField != null
4820
- && isTypeSource (expr , currentField .getInitialExpression ())) {
4821
- targetType = currentField .getType ();
4822
- } else if (currentProperty != null
4823
- && isTypeSource (expr , currentProperty .getInitialExpression ())) {
4824
- targetType = currentProperty .getType ();
4825
- } else if (enclosingMethod != null ) {
4826
- // TODO: try enclosingMethod's code with isTypeSource(expr, ...)
4827
- targetType = enclosingMethod .getReturnType ();
4828
- } // TODO: closure parameter default expression
4822
+ targetType = getDeclaredOrInferredType (enclosingBinaryExpression .getLeftExpression ());
4823
+ } else if (enclosingMethod != null
4824
+ && !enclosingMethod .isVoidMethod ()
4825
+ && isTypeSource (expr , enclosingMethod )) {
4826
+ targetType = enclosingMethod .getReturnType ();
4827
+ }
4828
+
4829
+ if (targetType == null ) return sourceType ;
4829
4830
4830
4831
if (expr instanceof ConstructorCallExpression ) {
4831
- if (targetType == null ) targetType = sourceType ;
4832
4832
inferDiamondType ((ConstructorCallExpression ) expr , targetType );
4833
4833
} else if (targetType != null && !isPrimitiveType (getUnwrapper (targetType ))
4834
4834
&& !targetType .equals (OBJECT_TYPE ) && missesGenericsTypes (sourceType )) {
@@ -4860,6 +4860,32 @@ private static boolean isTypeSource(final Expression expr, final Expression righ
4860
4860
}
4861
4861
return expr == right ;
4862
4862
}
4863
+
4864
+ private static boolean isTypeSource (final Expression expr , final MethodNode mNode ) {
4865
+ boolean [] returned = new boolean [1 ];
4866
+
4867
+ mNode .getCode ().visit (new org .codehaus .groovy .ast .CodeVisitorSupport () {
4868
+ @ Override
4869
+ public void visitReturnStatement (final ReturnStatement returnStatement ) {
4870
+ if (isTypeSource (expr , returnStatement .getExpression ())) {
4871
+ returned [0 ] = true ;
4872
+ }
4873
+ }
4874
+ @ Override
4875
+ public void visitClosureExpression (final ClosureExpression expression ) {
4876
+ }
4877
+ });
4878
+
4879
+ if (!returned [0 ]) {
4880
+ new ReturnAdder (returnStatement -> {
4881
+ if (isTypeSource (expr , returnStatement .getExpression ())) {
4882
+ returned [0 ] = true ;
4883
+ }
4884
+ }).visitMethod (mNode );
4885
+ }
4886
+
4887
+ return returned [0 ];
4888
+ }
4863
4889
// GRECLIPSE end
4864
4890
4865
4891
private static boolean isEmptyCollection (Expression expr ) {
@@ -6176,6 +6202,12 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
6176
6202
actuals [i ] = getLiteralResultType (pt , at , LINKEDHASHMAP_CLASSNODE );
6177
6203
} else if (a instanceof ConstructorCallExpression ) {
6178
6204
inferDiamondType ((ConstructorCallExpression ) a , pt ); // GROOVY-10086
6205
+ } else if (a instanceof TernaryExpression && at .isUsingGenerics () && at .getGenericsTypes ().length == 0 ) {
6206
+ // GROOVY-9983: double diamond scenario -- "m(flag ? new Type<>(...) : new Type<>(...))"
6207
+ typeCheckingContext .pushEnclosingBinaryExpression (assignX (varX (p ), a , a ));
6208
+ a .visit (this ); // re-visit with target type witness
6209
+ typeCheckingContext .popEnclosingBinaryExpression ();
6210
+ actuals [i ] = getType (a );
6179
6211
}
6180
6212
6181
6213
// check for method call with known target
0 commit comments