Code review comment: Will handle this separately after clarifying. https://github.com/ballerina-platform/ballerina-lang/issues/29758 will be used to track this

Code before review:
public void setForeachTypedBindingPatternType(BLangForeach foreachNode) {
BType collectionType = foreachNode.collection.type;
BType varType;
switch (collectionType.tag) {
case TypeTags.STRING:
varType = symTable.stringType;
break;
case TypeTags.ARRAY:
BArrayType arrayType = (BArrayType) collectionType;
varType = arrayType.eType;
break;
case TypeTags.TUPLE:
BTupleType tupleType = (BTupleType) collectionType;
LinkedHashSet<BType> tupleTypes = new LinkedHashSet<>(tupleType.tupleTypes);
if (tupleType.restType != null) {
tupleTypes.add(tupleType.restType);
}
varType = tupleTypes.size() == 1 ?
tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes);
break;
case TypeTags.MAP:
BMapType bMapType = (BMapType) collectionType;
varType = bMapType.constraint;
break;
case TypeTags.RECORD:
BRecordType recordType = (BRecordType) collectionType;
varType = inferRecordFieldType(recordType);
break;
case TypeTags.XML:
BType constraint = ((BXMLType) collectionType).constraint;
while (constraint.tag == TypeTags.XML) {
collectionType = constraint;
constraint = ((BXMLType) collectionType).constraint;
}
switch (constraint.tag) {
case TypeTags.XML_ELEMENT:
varType = symTable.xmlElementType;
break;
case TypeTags.XML_COMMENT:
varType = symTable.xmlCommentType;
break;
case TypeTags.XML_TEXT:
varType = symTable.xmlTextType;
break;
case TypeTags.XML_PI:
varType = symTable.xmlPIType;
break;
case TypeTags.NEVER:
varType = symTable.neverType;
break;
default:
Set<BType> collectionTypes = getEffectiveMemberTypes((BUnionType) constraint);
Set<BType> builtinXMLConstraintTypes = getEffectiveMemberTypes
((BUnionType) ((BXMLType) symTable.xmlType).constraint);
if (collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes)) {
varType = symTable.xmlType;
} else {
LinkedHashSet<BType> collectionTypesInSymTable = new LinkedHashSet<>();
for (BType subType : collectionTypes) {
switch (subType.tag) {
case TypeTags.XML_ELEMENT:
collectionTypesInSymTable.add(symTable.xmlElementType);
break;
case TypeTags.XML_COMMENT:
collectionTypesInSymTable.add(symTable.xmlCommentType);
break;
case TypeTags.XML_TEXT:
collectionTypesInSymTable.add(symTable.xmlTextType);
break;
case TypeTags.XML_PI:
collectionTypesInSymTable.add(symTable.xmlPIType);
break;
}
}
varType = BUnionType.create(null, collectionTypesInSymTable);
}
}
break;
case TypeTags.XML_TEXT:
varType = symTable.xmlTextType;
break;
case TypeTags.TABLE:
BTableType tableType = (BTableType) collectionType;
varType = tableType.constraint;
break;
case TypeTags.STREAM:
BStreamType streamType = (BStreamType) collectionType;
if (streamType.constraint.tag == TypeTags.NONE) {
varType = symTable.anydataType;
break;
}
varType = streamType.constraint;
List<BType> completionType = getAllTypes(streamType.error);
if (completionType.stream().anyMatch(type -> type.tag != TypeTags.NIL)) {
BType actualType = BUnionType.create(null, varType, streamType.error);
dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES,
varType, actualType);
}
break;
case TypeTags.OBJECT:
BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType);
if (nextMethodReturnType != null) {
foreachNode.resultType = getRecordType(nextMethodReturnType);
BType valueType = (foreachNode.resultType != null)
? ((BRecordType) foreachNode.resultType).fields.get("value").type : null;
BType errorType = getErrorType(nextMethodReturnType);
if (errorType != null) {
BType actualType = BUnionType.create(null, valueType, errorType);
dlog.error(foreachNode.collection.pos,
DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION,
actualType, errorType);
}
foreachNode.nillableResultType = nextMethodReturnType;
foreachNode.varType = valueType;
return;
}
case TypeTags.SEMANTIC_ERROR:
foreachNode.varType = symTable.semanticError;
foreachNode.resultType = symTable.semanticError;
foreachNode.nillableResultType = symTable.semanticError;
return;
default:
foreachNode.varType = symTable.semanticError;
foreachNode.resultType = symTable.semanticError;
foreachNode.nillableResultType = symTable.semanticError;
dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION,
collectionType);
return;
}
BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType,
names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC));
BUnionType nextMethodReturnType =
(BUnionType) getResultTypeOfNextInvocation((BObjectType) iteratorSymbol.retType);
foreachNode.varType = varType;
foreachNode.resultType = getRecordType(nextMethodReturnType);
foreachNode.nillableResultType = nextMethodReturnType;
}

Line under review: if (completionType.stream().anyMatch(type -> type.tag != TypeTags.NIL)) {

Code after addressing the review:
public void setForeachTypedBindingPatternType(BLangForeach foreachNode) {
BType collectionType = foreachNode.collection.type;
BType varType;
switch (collectionType.tag) {
case TypeTags.STRING:
varType = symTable.stringType;
break;
case TypeTags.ARRAY:
BArrayType arrayType = (BArrayType) collectionType;
varType = arrayType.eType;
break;
case TypeTags.TUPLE:
BTupleType tupleType = (BTupleType) collectionType;
LinkedHashSet<BType> tupleTypes = new LinkedHashSet<>(tupleType.tupleTypes);
if (tupleType.restType != null) {
tupleTypes.add(tupleType.restType);
}
varType = tupleTypes.size() == 1 ?
tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes);
break;
case TypeTags.MAP:
BMapType bMapType = (BMapType) collectionType;
varType = bMapType.constraint;
break;
case TypeTags.RECORD:
BRecordType recordType = (BRecordType) collectionType;
varType = inferRecordFieldType(recordType);
break;
case TypeTags.XML:
BType constraint = ((BXMLType) collectionType).constraint;
while (constraint.tag == TypeTags.XML) {
collectionType = constraint;
constraint = ((BXMLType) collectionType).constraint;
}
switch (constraint.tag) {
case TypeTags.XML_ELEMENT:
varType = symTable.xmlElementType;
break;
case TypeTags.XML_COMMENT:
varType = symTable.xmlCommentType;
break;
case TypeTags.XML_TEXT:
varType = symTable.xmlTextType;
break;
case TypeTags.XML_PI:
varType = symTable.xmlPIType;
break;
case TypeTags.NEVER:
varType = symTable.neverType;
break;
default:
Set<BType> collectionTypes = getEffectiveMemberTypes((BUnionType) constraint);
Set<BType> builtinXMLConstraintTypes = getEffectiveMemberTypes
((BUnionType) ((BXMLType) symTable.xmlType).constraint);
if (collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes)) {
varType = symTable.xmlType;
} else {
LinkedHashSet<BType> collectionTypesInSymTable = new LinkedHashSet<>();
for (BType subType : collectionTypes) {
switch (subType.tag) {
case TypeTags.XML_ELEMENT:
collectionTypesInSymTable.add(symTable.xmlElementType);
break;
case TypeTags.XML_COMMENT:
collectionTypesInSymTable.add(symTable.xmlCommentType);
break;
case TypeTags.XML_TEXT:
collectionTypesInSymTable.add(symTable.xmlTextType);
break;
case TypeTags.XML_PI:
collectionTypesInSymTable.add(symTable.xmlPIType);
break;
}
}
varType = BUnionType.create(null, collectionTypesInSymTable);
}
}
break;
case TypeTags.XML_TEXT:
varType = symTable.xmlTextType;
break;
case TypeTags.TABLE:
BTableType tableType = (BTableType) collectionType;
varType = tableType.constraint;
break;
case TypeTags.STREAM:
BStreamType streamType = (BStreamType) collectionType;
if (streamType.constraint.tag == TypeTags.NONE) {
varType = symTable.anydataType;
break;
}
varType = streamType.constraint;
List<BType> completionType = getAllTypes(streamType.error);
if (completionType.stream().anyMatch(type -> type.tag != TypeTags.NIL)) {
BType actualType = BUnionType.create(null, varType, streamType.error);
dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES,
varType, actualType);
}
break;
case TypeTags.OBJECT:
BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType);
if (nextMethodReturnType != null) {
foreachNode.resultType = getRecordType(nextMethodReturnType);
BType valueType = (foreachNode.resultType != null)
? ((BRecordType) foreachNode.resultType).fields.get("value").type : null;
BType errorType = getErrorType(nextMethodReturnType);
if (errorType != null) {
BType actualType = BUnionType.create(null, valueType, errorType);
dlog.error(foreachNode.collection.pos,
DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION,
actualType, errorType);
}
foreachNode.nillableResultType = nextMethodReturnType;
foreachNode.varType = valueType;
return;
}
case TypeTags.SEMANTIC_ERROR:
foreachNode.varType = symTable.semanticError;
foreachNode.resultType = symTable.semanticError;
foreachNode.nillableResultType = symTable.semanticError;
return;
default:
foreachNode.varType = symTable.semanticError;
foreachNode.resultType = symTable.semanticError;
foreachNode.nillableResultType = symTable.semanticError;
dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION,
collectionType);
return;
}
BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType,
names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC));
BUnionType nextMethodReturnType =
(BUnionType) getResultTypeOfNextInvocation((BObjectType) iteratorSymbol.retType);
foreachNode.varType = varType;
foreachNode.resultType = getRecordType(nextMethodReturnType);
foreachNode.nillableResultType = nextMethodReturnType;
}