266266import static org .codehaus .groovy .syntax .Types .INTDIV_EQUAL ;
267267import static org .codehaus .groovy .syntax .Types .KEYWORD_IN ;
268268import static org .codehaus .groovy .syntax .Types .KEYWORD_INSTANCEOF ;
269+ import static org .codehaus .groovy .syntax .Types .LOGICAL_OR ;
269270import static org .codehaus .groovy .syntax .Types .MINUS_MINUS ;
270271import static org .codehaus .groovy .syntax .Types .MOD ;
271272import static org .codehaus .groovy .syntax .Types .MOD_EQUAL ;
@@ -848,10 +849,13 @@ public Expression transform(final Expression expr) {
848849 return ;
849850 }
850851
852+ // GROOVY-7971, GROOVY-8965, GROOVY-10702, GROOVY-11754, et al.
853+ if (op == LOGICAL_OR ) typeCheckingContext .pushTemporaryTypeInfo ();
854+
851855 ClassNode lType ;
852856 leftExpression .visit (this );
853857 var setterInfo = removeSetterInfo (leftExpression );
854- if (setterInfo != null ) {
858+ if (setterInfo != null ) { assert op != LOGICAL_OR ;
855859 if (ensureValidSetter (expression , leftExpression , rightExpression , setterInfo )) {
856860 return ;
857861 }
@@ -863,7 +867,16 @@ public Expression transform(final Expression expr) {
863867 } else {
864868 lType = getType (leftExpression );
865869 }
866- rightExpression .visit (this );
870+ if (op != LOGICAL_OR ) {
871+ rightExpression .visit (this );
872+ } else {
873+ var lhs = typeCheckingContext .temporaryIfBranchTypeInformation .pop ();
874+ typeCheckingContext .pushTemporaryTypeInfo ();
875+ rightExpression .visit (this );
876+
877+ var rhs = typeCheckingContext .temporaryIfBranchTypeInformation .pop ();
878+ propagateTemporaryTypeInfo (lhs , rhs ); // `instanceof` on either side?
879+ }
867880 }
868881
869882 ClassNode rType = isNullConstant (rightExpression )
@@ -992,6 +1005,43 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
9921005 }
9931006 }
9941007
1008+ private void propagateTemporaryTypeInfo (final Map <Object , List <ClassNode >> lhs ,
1009+ final Map <Object , List <ClassNode >> rhs ) {
1010+ // TODO: deal with (x !instanceof T)
1011+ lhs .keySet ().removeIf (k -> k instanceof Object []);
1012+ rhs .keySet ().removeIf (k -> k instanceof Object []);
1013+
1014+ Function <Object , List <ClassNode >> getOrAdd = (key ) ->
1015+ typeCheckingContext .temporaryIfBranchTypeInformation .peek ().computeIfAbsent (key , x -> new LinkedList <>());
1016+
1017+ for (var entry : lhs .entrySet ()) {
1018+ if (rhs .containsKey (entry .getKey ())) {
1019+ // main case: (x instanceof A || x instanceof B) produces A|B type
1020+ List <ClassNode > types = getOrAdd .apply (entry .getKey ());
1021+ types .addAll (entry .getValue ());
1022+ types .addAll (rhs .get (entry .getKey ()));
1023+ } else if (entry .getKey () instanceof Variable ) {
1024+ // edge case: (x instanceof A || ...) produces A|typeof(x) type
1025+ List <ClassNode > types = getOrAdd .apply (entry .getKey ());
1026+ types .addAll (entry .getValue ());
1027+ Variable v = (Variable ) entry .getKey ();
1028+ types .add (v instanceof ASTNode ? getType ((ASTNode ) v ) : v .getType ());
1029+ }
1030+ }
1031+
1032+ rhs .keySet ().removeAll (lhs .keySet ());
1033+
1034+ for (var entry : rhs .entrySet ()) {
1035+ if (entry .getKey () instanceof Variable ) {
1036+ // edge case: (... || x instanceof B) produces typeof(x)|B type
1037+ List <ClassNode > types = getOrAdd .apply (entry .getKey ());
1038+ Variable v = (Variable ) entry .getKey ();
1039+ types .add (v instanceof ASTNode ? getType ((ASTNode ) v ) : v .getType ());
1040+ types .addAll (entry .getValue ());
1041+ }
1042+ }
1043+ }
1044+
9951045 private void validateResourceInARM (final BinaryExpression expression , final ClassNode lType ) {
9961046 if (expression instanceof DeclarationExpression
9971047 && TryCatchStatement .isResource (expression )
@@ -1173,8 +1223,8 @@ private boolean ensureValidSetter(final Expression expression, final Expression
11731223 }
11741224 addStaticTypeError (message , leftExpression );
11751225 } else {
1176- ClassNode [] tergetTypes = visibleSetters .stream ().map (setterType ).toArray (ClassNode []::new );
1177- addAssignmentError (tergetTypes .length == 1 ? tergetTypes [0 ] : new UnionTypeClassNode (tergetTypes ), getType (valueExpression ), expression );
1226+ ClassNode [] targetTypes = visibleSetters .stream ().map (setterType ).toArray (ClassNode []::new );
1227+ addAssignmentError (targetTypes .length == 1 ? targetTypes [0 ] : new UnionTypeClassNode (targetTypes ), getType (valueExpression ), expression );
11781228 }
11791229 return true ;
11801230 }
0 commit comments