diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java index 2cb38e81a2f3..09bd64fc498d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java @@ -288,14 +288,15 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta List resolvedTypes = new ArrayList<>(); BType selectType; BLangExpression collectionNode = (BLangExpression) ((BLangFromClause) clauses.get(0)).getCollection(); - for (BType type : safeResultTypes) { - solveSelectTypeAndResolveType(queryExpr, selectExp, type, collectionNode.getBType(), selectTypes, - resolvedTypes, env, data, false); - } + solveSelectTypeAndResolveType(queryExpr, selectExp, safeResultTypes, collectionNode.getBType(), selectTypes, + resolvedTypes, env, data, false); + if (selectTypes.size() == 1) { + selectType = selectTypes.get(0); + checkExpr(selectExp, env, selectType, data); List collectionTypes = getCollectionTypes(clauses); BType completionType = getCompletionType(collectionTypes, types.getQueryConstructType(queryExpr), data); - selectType = selectTypes.get(0); + if (queryExpr.isStream) { return new BStreamType(TypeTags.STREAM, selectType, completionType, null); } else if (queryExpr.isTable) { @@ -327,89 +328,162 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta } } - void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression selectExp, BType expType, + void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression selectExp, List expTypes, BType collectionType, List selectTypes, List resolvedTypes, SymbolEnv env, TypeChecker.AnalyzerData data, boolean isReadonly) { - BType selectType, resolvedType; - BType type = Types.getImpliedType(expType); - switch (type.tag) { - case TypeTags.ARRAY: - BType elementType = ((BArrayType) type).eType; - selectType = checkExpr(selectExp, env, elementType, data); - BType queryResultType = new BArrayType(selectType); - resolvedType = getResolvedType(queryResultType, type, isReadonly, env); - break; - case TypeTags.TABLE: - selectType = checkExpr(selectExp, env, types.getSafeType(((BTableType) type).constraint, - true, true), data); - resolvedType = getResolvedType(symTable.tableType, type, isReadonly, env); - break; - case TypeTags.STREAM: - selectType = checkExpr(selectExp, env, types.getSafeType(((BStreamType) type).constraint, - true, true), data); - resolvedType = symTable.streamType; - break; - case TypeTags.MAP: - List memberTypeList = new ArrayList<>(2); - BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, - symTable.semanticError, null, symTable.builtinPos, SymbolOrigin.VIRTUAL); - memberTypeList.add(new BTupleMember(symTable.stringType, stringVarSymbol)); - BType memberType = ((BMapType) type).getConstraint(); - BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(memberType); - memberTypeList.add(new BTupleMember(memberType, varSymbol)); - BTupleType newExpType = new BTupleType(null, memberTypeList); - selectType = checkExpr(selectExp, env, newExpType, data); - resolvedType = getResolvedType(selectType, type, isReadonly, env); - break; - case TypeTags.STRING: - selectType = checkExpr(selectExp, env, type, data); - resolvedType = symTable.stringType; - break; - case TypeTags.XML: - case TypeTags.XML_COMMENT: - case TypeTags.XML_ELEMENT: - case TypeTags.XML_PI: - case TypeTags.XML_TEXT: - selectType = checkExpr(selectExp, env, type, data); - if (types.isAssignable(selectType, symTable.xmlType)) { - resolvedType = getResolvedType(new BXMLType(selectType, null), type, isReadonly, env); - } else { - resolvedType = selectType; - } - break; - case TypeTags.INTERSECTION: - type = ((BIntersectionType) type).effectiveType; - solveSelectTypeAndResolveType(queryExpr, selectExp, type, collectionType, selectTypes, - resolvedTypes, env, data, Symbols.isFlagOn(type.flags, Flags.READONLY)); - return; - case TypeTags.NONE: - default: - // contextually expected type not given (i.e var). - selectType = checkExprSilent(nodeCloner.cloneNode(selectExp), type, data); - if (selectType != symTable.semanticError) { - selectType = checkExpr(selectExp, env, type, data); - } else { - selectType = checkExpr(selectExp, env, data); + LinkedHashSet possibleSelectTypes = new LinkedHashSet<>(); + List possibleResolvedTypes = new ArrayList<>(); + LinkedHashSet errorTypes = new LinkedHashSet<>(); + for (BType expType : expTypes) { + BType selectType, resolvedType; + BType type = Types.getReferredType(expType); + switch (type.tag) { + case TypeTags.ARRAY: + BType elementType = ((BArrayType) type).eType; + selectType = checkExprSilent(selectExp, env, elementType, data); + if (selectType == symTable.semanticError) { + errorTypes.add(elementType); + continue; + } + BType queryResultType = new BArrayType(selectType); + resolvedType = getResolvedType(queryResultType, type, isReadonly, env); + break; + case TypeTags.TABLE: + BType tableConstraint = types.getSafeType(((BTableType) type).constraint, + true, true); + selectType = checkExprSilent(selectExp, env, tableConstraint, data); + if (selectType == symTable.semanticError) { + errorTypes.add(tableConstraint); + continue; + } + resolvedType = getResolvedType(symTable.tableType, type, isReadonly, env); + break; + case TypeTags.STREAM: + BType streamConstraint = types.getSafeType(((BStreamType) type).constraint, + true, true); + selectType = checkExprSilent(selectExp, env, streamConstraint, data); + if (selectType == symTable.semanticError) { + errorTypes.add(streamConstraint); + continue; + } + resolvedType = symTable.streamType; + break; + case TypeTags.MAP: + List memberTypeList = new ArrayList<>(2); + BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, + symTable.semanticError, null, symTable.builtinPos, SymbolOrigin.VIRTUAL); + memberTypeList.add(new BTupleMember(symTable.stringType, stringVarSymbol)); + BType memberType = ((BMapType) type).getConstraint(); + BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(memberType); + memberTypeList.add(new BTupleMember(memberType, varSymbol)); + BTupleType newExpType = new BTupleType(null, memberTypeList); + selectType = checkExprSilent(selectExp, env, newExpType, data); + if (selectType == symTable.semanticError) { + errorTypes.add(newExpType); + continue; + } + resolvedType = getResolvedType(selectType, type, isReadonly, env); + break; + case TypeTags.STRING: + case TypeTags.XML: + case TypeTags.XML_COMMENT: + case TypeTags.XML_ELEMENT: + case TypeTags.XML_PI: + case TypeTags.XML_TEXT: + selectType = checkExprSilent(selectExp, env, type, data); + if (selectType == symTable.semanticError) { + errorTypes.add(type); + continue; + } + + BType refSelectType = Types.getReferredType(selectType); + if (TypeTags.isXMLTypeTag(refSelectType.tag) || TypeTags.isStringTypeTag(refSelectType.tag)) { + selectType = type; + } + + if (types.isAssignable(selectType, symTable.xmlType)) { + resolvedType = getResolvedType(new BXMLType(selectType, null), type, isReadonly, env); + } else { + resolvedType = selectType; + } + break; + case TypeTags.INTERSECTION: + type = ((BIntersectionType) type).effectiveType; + solveSelectTypeAndResolveType(queryExpr, selectExp, List.of(type), collectionType, selectTypes, + resolvedTypes, env, data, Symbols.isFlagOn(type.flags, Flags.READONLY)); + return; + case TypeTags.NONE: + default: + // contextually expected type not given (i.e var). + BType inferredSelectType = symTable.semanticError; + selectType = checkExprSilent(selectExp, env, type, data); + if (type != symTable.noType) { + inferredSelectType = checkExprSilent(selectExp, env, symTable.noType, data); + } + + if (selectType != symTable.semanticError && inferredSelectType != symTable.semanticError + && inferredSelectType != symTable.noType) { + selectType = types.getTypeIntersection( + Types.IntersectionContext.typeTestIntersectionCalculationContext(), + selectType, inferredSelectType, env); + } else { + BType checkedType = symTable.semanticError; + if (selectType == symTable.semanticError) { + checkedType = checkExpr(selectExp, env, data); + } + + if (checkedType != symTable.semanticError && expTypes.size() == 1) { + selectType = checkedType; + } + } + + if (queryExpr.isMap) { // A query-expr that constructs a mapping must start with the map keyword. + resolvedType = symTable.mapType; + } else { + resolvedType = getNonContextualQueryType(selectType, collectionType); + } + break; + } + if (selectType != symTable.semanticError) { + + if (resolvedType.tag == TypeTags.STREAM) { + queryExpr.isStream = true; } - if (queryExpr.isMap) { // A query-expr that constructs a mapping must start with the map keyword. - resolvedType = symTable.mapType; - } else { - resolvedType = getNonContextualQueryType(selectType, collectionType); + if (resolvedType.tag == TypeTags.TABLE) { + queryExpr.isTable = true; } - break; - } - if (selectType != symTable.semanticError) { - if (resolvedType.tag == TypeTags.STREAM) { - queryExpr.isStream = true; + possibleSelectTypes.add(selectType); + possibleResolvedTypes.add(resolvedType); } - if (resolvedType.tag == TypeTags.TABLE) { - queryExpr.isTable = true; + } + if (!possibleSelectTypes.isEmpty() && !possibleResolvedTypes.isEmpty()) { + selectTypes.addAll(possibleSelectTypes); + resolvedTypes.addAll(possibleResolvedTypes); + return; + } + if (!errorTypes.isEmpty()) { + if (errorTypes.size() > 1) { + BType actualQueryType = silentTypeCheckExpr(queryExpr, symTable.noType, data); + if (actualQueryType != symTable.semanticError) { + types.checkType(queryExpr, actualQueryType, + BUnionType.create(null, new LinkedHashSet<>(expTypes))); + errorTypes.forEach(expType -> { + if (expType.tag == TypeTags.UNION) { + checkExpr(nodeCloner.cloneNode(selectExp), env, expType, data); + } + }); + checkExpr(selectExp, env, data); + return; + } } - selectTypes.add(selectType); - resolvedTypes.add(resolvedType); + errorTypes.forEach(expType -> { + checkExpr(selectExp, env, expType, data); + selectExp.typeChecked = false; + }); + selectExp.typeChecked = true; } } - + private BType getQueryTableType(BLangQueryExpr queryExpr, BType constraintType, BType resolvedType, SymbolEnv env) { final BTableType tableType = new BTableType(TypeTags.TABLE, constraintType, null); if (!queryExpr.fieldNameIdentifierList.isEmpty()) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java index bcbb9875e117..944d381af4a2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java @@ -3648,6 +3648,14 @@ protected BType checkExprSilent(BLangExpression expr, BType expType, AnalyzerDat return type; } + protected BType checkExprSilent(BLangExpression expr, SymbolEnv env, BType expType, AnalyzerData data) { + SymbolEnv prevEnv = data.env; + data.env = env; + BType type = checkExprSilent(nodeCloner.cloneNode(expr), expType, data); + data.env = prevEnv; + return type; + } + private BLangRecordLiteral createRecordLiteralForErrorConstructor(BLangErrorConstructorExpr errorConstructorExpr) { BLangRecordLiteral recordLiteral = (BLangRecordLiteral) TreeBuilder.createRecordLiteralNode(); for (NamedArgNode namedArg : errorConstructorExpr.getNamedArgs()) { diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java index 41f751eb47a6..ee5cb48fc591 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java @@ -76,7 +76,11 @@ public Object[] dataToTestQueryActionOrExpr() { "testQueryActionWithQueryExpression", "testQueryActionWithRegexpLangLibs", "testQueryExprWithRegExpLangLibs", - "testQueryActionWithInterpolationRegexpLangLibs" + "testQueryActionWithInterpolationRegexpLangLibs", + "testQueryActionOrExpressionWithUnionRecordResultType", + "testQueryActionOrExprWithAnyOrErrResultType", + "testNestedQueryActionOrExprWithClientResourceAccessAction", + "testQueryActionWithQueryExpression" }; } @@ -131,6 +135,40 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "action invocation as an expression not allowed here", 315, 23); validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 316, 21); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 320, 50); + validateError(negativeResult, i++, "incompatible types: expected '(T3[]|T4[])', found '(T3|T4)[]'", + 339, 13); + validateError(negativeResult, i++, "incompatible types: expected '(int[]|string[])', found '(int|string)[]'", + 359, 24); + validateError(negativeResult, i++, "incompatible types: expected '(int[]|string[])', found '(int|boolean)[]'", + 363, 24); + validateError(negativeResult, i++, + "incompatible types: expected '(string[]|decimal[])', found '(int|float)[]'", + 367, 28); + validateError(negativeResult, i++, "incompatible types: expected '(table|table)', " + + "found 'table<(FooType|BarType)> key(id)'", 371, 39); + validateError(negativeResult, i++, "ambiguous type '[string:Char, string]'", 376, 78); + validateError(negativeResult, i++, "incompatible types: expected 'boolean', " + + "found 'T3'", 382, 11); + validateError(negativeResult, i++, "incompatible types: expected 'T3', " + + "found 'T4'", 384, 13); + validateError(negativeResult, i++, "incompatible types: expected 'T3', " + + "found 'T2'", 386, 13); + validateError(negativeResult, i++, "missing non-defaultable required record field 't3OrT4'", + 386, 17); + validateError(negativeResult, i++, "incompatible types: expected 'T3', " + + "found 'T1'", 388, 12); + validateError(negativeResult, i++, "missing non-defaultable required record field 't3s'", + 388, 16); + validateError(negativeResult, i++, "incompatible types: expected '(T3[]|T4[])', " + + "found '(T4|T2|T1)[]'", 394, 13); + validateError(negativeResult, i++, "missing non-defaultable required record field 't3OrT4'", + 398, 17); + validateError(negativeResult, i++, "missing non-defaultable required record field 't3s'", + 400, 16); + validateError(negativeResult, i++, "ambiguous type '(Baz|Qux)'", 439, 16); + validateError(negativeResult, i++, "ambiguous type '(Baz|Qux)'", 446, 16); + validateError(negativeResult, i++, "incompatible types: expected '(boolean[]|float[])', " + + "found 'record {| string c; |}[]'", 452, 8); Assert.assertEquals(negativeResult.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java index 1133e85532dd..19b85e3debec 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java @@ -60,8 +60,6 @@ public void testFromClauseWithInvalidType() { validateError(compileResult, index++, "incompatible types: expected 'string', found 'int'", 278, 24); validateError(compileResult, index++, "a type compatible with mapping constructor expressions " + "not found in type 'string'", 292, 24); - validateError(compileResult, index++, "ambiguous type '[xml, xml]'", 314, 24); - validateError(compileResult, index++, "ambiguous type '[string, string]'", 327, 24); validateError(compileResult, index++, "redeclared symbol 'fname'", 351, 36); validateError(compileResult, index++, "redeclared symbol 'age'", 364, 21); validateError(compileResult, index++, "redeclared symbol 'age'", 381, 44); @@ -116,6 +114,20 @@ public void testFromClauseWithInvalidType() { Assert.assertEquals(compileResult.getErrorCount(), index - warnCount); } + @Test + public void testAmbiguousTypesInUnionExpectedType() { + CompileResult compileResult = BCompileUtil.compile("test-src/query/query_ambiguous_type_negative.bal"); + int index = 0; + validateError(compileResult, index++, "ambiguous type '[string:Char, string]'", 26, 58); + validateError(compileResult, index++, "ambiguous type '[string, string:Char]'", 27, 58); + validateError(compileResult, index++, "ambiguous type '[string, string:Char]'", 28, 103); + validateError(compileResult, index++, "ambiguous type '[xml, xml:Element]'", 32, 16); + validateError(compileResult, index++, "incompatible types: expected 'int', found 'string:Char'", 41, 65); + validateError(compileResult, index++, "incompatible types: expected '(stream|string)', found " + + "'stream'", 42, 36); + Assert.assertEquals(compileResult.getErrorCount(), index); + } + @Test public void testFromClauseWithInvalidAssignmentToFinalVar() { CompileResult compileResult = BCompileUtil.compile("test-src/query/query_dataflow_negative.bal"); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java index a56bcbf709c7..39307ff0c501 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java @@ -73,14 +73,11 @@ public void testNegativeQueryExprForXML() { "found 'xml:ProcessingInstruction'", 51, 16); validateError(negativeResult, index++, "incompatible types: expected '(xml:Element|error)', found '(xml|error)'", 81, 27); - // issue - #40012 - // validateError(negativeResult, index++, - // "ambiguous type '[xml:Element, xml:Element]'", 88, 16); - // validateError(negativeResult, index++, - // "incompatible types: expected 'xml:Text', found 'xml:Element'", 94, 16); - // validateError(negativeResult, index++, - // "ambiguous type '[xml:Element, xml:Element]'", 99, 16); - Assert.assertEquals(negativeResult.getErrorCount(), index + 3); + validateError(negativeResult, index++, + "ambiguous type '[xml:Element, xml]'", 88, 16); + validateError(negativeResult, index++, + "ambiguous type '[xml:Element, xml]'", 99, 16); + Assert.assertEquals(negativeResult.getErrorCount(), index); } @Test(description = "Test simple query expression for XMLs - #1") diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal index ef474de2f722..d37d6d8c5ac3 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal @@ -915,7 +915,6 @@ function testQueryActionWithQueryExpression() { assertEquality([4, 25], res2); } - function testQueryActionWithRegexpLangLibs() { string[] res = []; @@ -950,6 +949,63 @@ function testQueryActionWithInterpolationRegexpLangLibs() { assertEquality(["aa", "aaab", "aac"], res); } +type T1 record { + T3[] t3s; +}; + +type T2 record { + T3[]|T4[] t3OrT4; +}; + +type T3 record { + string str; +}; + +type T4 record { + boolean foo; +}; + +function transform(T1 t1) returns T2 => { + t3OrT4: from var t3sItem in t1.t3s + select { + str: "transformed_" + t3sItem.str + } +}; + +function testQueryActionOrExpressionWithUnionRecordResultType() { + T1 t1 = {t3s: [{str: "str1"}, {str: "str2"}]}; + T2 t2 = transform(t1); + assertEquality([{str: "transformed_str1"}, {str: "transformed_str2"}], t2.t3OrT4); +} + +type Student record { + string firstName; + string lastName; + int intakeYear; + float gpa; +}; + +function calGraduationYear(int year) returns int => year + 5; + +function getBestStudents() returns any|error { + Student s1 = {firstName: "Martin", lastName: "Sadler", intakeYear: 1990, gpa: 3.5}; + Student s2 = {firstName: "Ranjan", lastName: "Fonseka", intakeYear: 2001, gpa: 1.9}; + Student s3 = {firstName: "Michelle", lastName: "Guthrie", intakeYear: 2002, gpa: 3.7}; + Student s4 = {firstName: "George", lastName: "Fernando", intakeYear: 2005, gpa: 4.0}; + Student[] studentList = [s1, s2, s3]; + + return from var student in studentList + where student.gpa >= 2.0 + let string degreeName = "Bachelor of Medicine", int graduationYear = calGraduationYear(student.intakeYear) + order by student.gpa descending + limit 2 + select {name: student.firstName + " " + student.lastName, degree: degreeName, graduationYear}; +} + +function testQueryActionOrExprWithAnyOrErrResultType() { + assertTrue(getBestStudents() is record {|string name; string degree; int graduationYear;|}[]); +} + const ASSERTION_ERROR_REASON = "AssertionError"; function assertEquality(anydata expected, anydata actual) { diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr_semantic_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr_semantic_negative.bal index 7897e51e5ae5..9bca423f287b 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr_semantic_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr_semantic_negative.bal @@ -318,3 +318,139 @@ function testQueryActionOrExprWithActionsInJoinClause() { } int[][] globalQuery = from var i in 1...3 select start f1(); + +type T1 record { + T3[] t3s; +}; + +type T2 record { + T3[]|T4[] t3OrT4; +}; + +type T3 record { + string str; +}; + +type T4 record { + boolean foo; +}; + +function transformT1(T1 t1) returns T2 => { + t3OrT4: from var t3sItem in t1.t3s + select t3sItem.str == "" + ? { + str: "" + } : { + foo: false + } +}; + +type FooType record {| + readonly int id; + string foo; +|}; + +type BarType record {| + readonly int id; + string bar; +|}; + +function testQueryExprWithUnionInSelectClause1() { + int[]|string[] _ = from var i in [1, 2, 3, 4] select i % 2 == 0 ? i : "odd"; +} + +function testQueryExprWithUnionInSelectClause2() { + int[]|string[] _ = from var i in [1, 2, 3, 4] select i % 2 == 0 ? i : false; +} + +function testQueryExprWithUnionInSelectClause3() { + string[]|decimal[] _ = from var i in [1, 2, 3, 4] select i % 2 == 0 ? i : 1.0; +} + +function testQueryExprWithUnionInSelectClause4() { + table|table _ = table key(id) from var i in [1, 2, 3, 4] + select i % 2 == 0 ? {foo: "Foo", id: i} : {bar: "Bar", id: i}; +} + +function testQueryWithAmbiguousType2() { + string:Char[]|string _ = from var letter in ["a", "b", "c", "AA"] select letter == "B" ? letter : "L"; +} + +function transform(T1 t1) returns T2 => { + t3OrT4: from var t3sItem in t1.t3s + select t3sItem.str == "" + ? { + str: "" + } ? { + foo: false + } : { + foo: false + }: { + foo: false + } +}; + +function transform2(T1 t1) returns T2 => { + t3OrT4: from var t3sItem in t1.t3s + select t3sItem.str == "" + ? returnBool() ? { + foo: false + } : { + foo: false + }: { + foo: false + } +}; + +function returnBool() returns boolean => true; + +type Foo record { + Baz[] a; +}; + +type Bar record { + (Baz|Qux)[]|int[] b; +}; + +type Baz record { + string c; +}; + +type Qux record { + string c; + int d?; +}; + +type Baar record { + (Baz|Qux)[]|Quux[] b; +}; + +type Quux record { + string c; + int d; +}; + +type Xyz record { + boolean[]|float[] b; +}; + +function transformFoo1(Foo foo) returns Bar => { + b: from var _ in foo.a + select { + c: "str" + } +}; + +function transformFoo2(Foo foo) returns Baar => { + b: from var _ in foo.a + select { + c: "str" + } +}; + +function transformFoo3(Foo foo) returns Xyz => { + b: from var _ in foo.a + select { + c: "str" + } +}; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_ambiguous_type_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_ambiguous_type_negative.bal new file mode 100644 index 000000000000..f1dc4d7289a0 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_ambiguous_type_negative.bal @@ -0,0 +1,43 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +type Book record {| + string title; + string author; +|}; + +function testQueryExprForAmbiguousTypes() returns error? { + string:Char chr = "a"; + string[] strArr = ["a", "b", "c"]; + + string:Char[]|string _ = from var i in strArr select chr; + string|string:Char[] _ = from var j in strArr select chr; + [string:Char, string:Char]|int|string|(int|string)[] _ = from var x in ["a", "b", 1, "2"] select chr; + + Book[] bookList = []; + xml|xml:Element xmlValue = from Book book in bookList + select xml ` + ${book.author} + ${book.title} + `; +} + +function testQueryExprForErrStream() returns error? { + int[] numList = [1, 2, 3]; + string:Char chr = "a"; + stream _ = stream from var i in numList select chr; + stream|string _ = stream from var i in numList select chr; +}