Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Query Expression]Restrict result type being resolved as map without the query-construct-type #41117

Merged
merged 20 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
54ef9e8
Restrict inferring map/stream query result types
pcnfernando Jul 27, 2023
8101187
Use rest type as CET for spread
pcnfernando Jul 28, 2023
fefe640
Revert testng.xml update
pcnfernando Jul 28, 2023
c61801e
Update LS completions
pcnfernando Jul 31, 2023
58b9828
Avoid CE for queries resulting in streams
pcnfernando Jul 31, 2023
01d101e
Update stream type toString representation
pcnfernando Jul 31, 2023
fdf293a
Fix checkstyle failure
pcnfernando Jul 31, 2023
1ff450d
Merge branch 'master' of https://github.com/ballerina-platform/baller…
pcnfernando Jul 31, 2023
25fec0d
Update redundant stream type manipulation
pcnfernando Jul 31, 2023
ee4d09b
Refactor compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang…
pcnfernando Aug 15, 2023
9f90285
Add invalid xml constrained completion
pcnfernando Aug 15, 2023
90b0808
Merge branch 'master' of https://github.com/ballerina-platform/baller…
pcnfernando Aug 15, 2023
049d4ab
Add empty code action test
pcnfernando Aug 15, 2023
5447810
Merge branch 'master' into issue_37670
pcnfernando Aug 16, 2023
2153aef
Fix build failure due to parsing error
pcnfernando Aug 16, 2023
d98303c
Merge branch 'master' of https://github.com/ballerina-platform/baller…
pcnfernando Aug 17, 2023
1e13111
Fix intersection negative test failure
pcnfernando Aug 17, 2023
c897ac8
Merge branch 'master' of https://github.com/ballerina-platform/baller…
pcnfernando Aug 17, 2023
de7f593
Revert intersection type assertion update
pcnfernando Aug 17, 2023
4416569
Merge branch 'master' of https://github.com/ballerina-platform/baller…
pcnfernando Aug 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{
"description": "Query stream evaluation - get value.",
"code": "reportListStream",
"expr": "stream <stream<map<(any|error)>>>"
"expr": "stream<map<(any|error)>>"
},
{
"description": "String from query evaluation.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public Type getIteratorNextReturnType() {
* @param parent The link to the parent node
*/
public String stringValue(BLink parent) {
return "stream <" + getType().toString() + ">";
return getType().toString();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,10 @@ public enum DiagnosticErrorCode implements DiagnosticCode {
QUERY_CONSTRUCT_TYPES_CANNOT_BE_USED_WITH_COLLECT("BCE4049", "query.construct.types.cannot.be.used.with.collect"),
VARIABLE_IS_SEQUENCED_MORE_THAN_ONCE("BCE4050", "variable.is.sequenced.more.than.once"),
INVALID_GROUPING_KEY_TYPE("BCE4051", "invalid.grouping.key.type"),
NAMED_ARG_NOT_ALLOWED_FOR_REST_PARAM("BCE4052", "named.arg.not.allowed.for.rest.param")
NAMED_ARG_NOT_ALLOWED_FOR_REST_PARAM("BCE4052", "named.arg.not.allowed.for.rest.param"),
INVALID_QUERY_CONSTRUCT_INFERRED_MAP("BCE4053", "inferred.query.construct.type.as.map"),
INVALID_QUERY_CONSTRUCT_INFERRED_STREAM("BCE4054", "inferred.query.construct.type.as.stream"),
INVALID_QUERY_CONSTRUCT_TYPE("BCE4055", "invalid.error.query.construct.type")
;

private String diagnosticId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
import java.util.stream.Collectors;

import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL;
import static org.ballerinalang.util.diagnostic.DiagnosticErrorCode.INVALID_QUERY_CONSTRUCT_INFERRED_MAP;
import static org.ballerinalang.util.diagnostic.DiagnosticErrorCode.INVALID_QUERY_CONSTRUCT_TYPE;

/**
* @since 2201.5.0
Expand Down Expand Up @@ -388,8 +390,10 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel
}
if (queryExpr.isMap) { // A query-expr that constructs a mapping must start with the map keyword.
resolvedType = symTable.mapType;
} else if (queryExpr.isStream) {
resolvedType = symTable.streamType;
} else {
resolvedType = getNonContextualQueryType(selectType, collectionType);
resolvedType = getNonContextualQueryType(selectType, collectionType, selectExp.pos);
}
break;
}
Expand Down Expand Up @@ -570,26 +574,40 @@ private BType getResolvedType(BType initType, BType expType, boolean isReadonly,
return initType;
}

private BType getNonContextualQueryType(BType staticType, BType basicType) {
BType resultType;
private BType getNonContextualQueryType(BType constraintType, BType basicType, Location pos) {
pcnfernando marked this conversation as resolved.
Show resolved Hide resolved
switch (Types.getReferredType(basicType).tag) {
case TypeTags.TABLE:
resultType = symTable.tableType;
if (types.isAssignable(constraintType, symTable.mapAllType)) {
return symTable.tableType;
}
break;
case TypeTags.STREAM:
resultType = symTable.streamType;
break;
// todo: Depends on https://github.com/ballerina-platform/ballerina-spec/issues/1252 decision
// dlog.error(pos, INVALID_QUERY_CONSTRUCT_INFERRED_STREAM);
// return symTable.semanticError;
return symTable.streamType;
case TypeTags.MAP:
dlog.error(pos, INVALID_QUERY_CONSTRUCT_INFERRED_MAP);
return symTable.semanticError;
case TypeTags.XML:
resultType = new BXMLType(staticType, null);
if (types.isSubTypeOfBaseType(constraintType, symTable.xmlType.tag)) {
return new BXMLType(constraintType, null);
}
break;
case TypeTags.STRING:
resultType = symTable.stringType;
if (types.isSubTypeOfBaseType(constraintType, TypeTags.STRING)) {
return symTable.stringType;
}
break;
case TypeTags.ARRAY:
case TypeTags.TUPLE:
case TypeTags.OBJECT:
return new BArrayType(constraintType);
default:
resultType = new BArrayType(staticType);
break;
return symTable.semanticError;
}
return resultType;
dlog.error(pos, INVALID_QUERY_CONSTRUCT_TYPE, basicType, constraintType);
return symTable.semanticError;
}

private boolean validateTableType(BTableType tableType, TypeChecker.AnalyzerData data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2087,7 +2087,18 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp
}

BLangExpression spreadOpExpr = ((BLangListConstructorSpreadOpExpr) expr).expr;
BType spreadOpType = checkExpr(spreadOpExpr, data);
BType spreadOpType;
if (restType != null && restType != symTable.noType && remainNonRestCount == 0) {
BType targetType = new BArrayType(restType);
BType possibleType = silentTypeCheckExpr(spreadOpExpr, targetType, data);
if (possibleType == symTable.semanticError) {
spreadOpType = checkExpr(spreadOpExpr, data);
} else {
spreadOpType = checkExpr(spreadOpExpr, targetType, data);
}
} else {
spreadOpType = checkExpr(spreadOpExpr, data);
}
BType spreadOpReferredType = Types.getReferredType(spreadOpType);

switch (spreadOpReferredType.tag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1997,3 +1997,12 @@ error.invalid.grouping.key.type=\

error.function.call.syntax.not.defined=\
function call syntax is not defined for ''{0}''

error.invalid.error.query.construct.type=\
incompatible types: ''{0}'' cannot be constrained with ''{1}''

error.inferred.query.construct.type.as.map=\
query expression that constructs a mapping must start with the map keyword

error.inferred.query.construct.type.as.stream=\
query expression that constructs a stream must start with the stream keyword
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ public Object[][] dataProvider() {
{"createVariableForOptionalFieldAccess1.json"},
{"createVariableForOptionalFieldAccess2.json"},
{"createVariableWithTypeDesc.json"},
{"createVariableInQueryAction1.json"},
{"createVariableInQueryAction2.json"},
{"createVariableInQueryAction3.json"},

// Tuple related
{"createVariableWithTuple1.json"},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"position": {
"line": 2,
"character": 4
},
"source": "createVariableInQueryAction1.bal",
"expected": [
{
"title": "Create variable",
"kind": "quickfix",
"edits": [
{
"range": {
"start": {
"line": 2,
"character": 4
},
"end": {
"line": 2,
"character": 4
}
},
"newText": "int[] listResult = "
}
],
"command": {
"title": "Rename variable",
"command": "ballerina.action.positional.rename",
"arguments": [
"createVariableInQueryAction1.bal",
{
"line": 2,
"character": 10
}
]
},
"resolvable": false
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"position": {
"line": 6,
"character": 4
},
"source": "createVariableInQueryAction2.bal",
"expected": [
{
"title": "Create variable",
"kind": "quickfix",
"edits": [
{
"range": {
"start": {
"line": 6,
"character": 4
},
"end": {
"line": 6,
"character": 4
}
},
"newText": "xml<xml:Element> xmlResult = "
}
],
"command": {
"title": "Rename variable",
"command": "ballerina.action.positional.rename",
"arguments": [
"createVariableInQueryAction2.bal",
{
"line": 6,
"character": 21
}
]
},
"resolvable": false
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"position": {
"line": 2,
"character": 4
},
"source": "createVariableInQueryAction3.bal",
"expected": [
{
"title": "Create variable",
"kind": "quickfix",
"edits": [
{
"range": {
"start": {
"line": 2,
"character": 4
},
"end": {
"line": 2,
"character": 4
}
},
"newText": "xml<xml> xmlResult = "
}
],
"command": {
"title": "Rename variable",
"command": "ballerina.action.positional.rename",
"arguments": [
"createVariableInQueryAction3.bal",
{
"line": 2,
"character": 13
}
]
},
"resolvable": false
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public function func() {
int[] x1 = [1, 2, 3, 4, 5];
from int element in x1 select element;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public function func() {
xml book = xml `<book>
<name>Sherlock Holmes</name>
<author>Sir Arthur Conan Doyle</author>
</book>`;

from var x in book/<name> select x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public function func() {
xml x1 = xml `<book>The Lost World</book>`;
from xml element in x1 select element;
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ public String computeValue() {
* redundant information.
*/
private static String processStringValue(String stringValue) {
if (stringValue.startsWith("stream <stream") && stringValue.endsWith(">>")) {
stringValue = stringValue.replaceFirst("^stream <stream", "stream").replaceFirst(">>$", ">");
}
if (stringValue.startsWith("stream<(") && stringValue.endsWith(")>")) {
stringValue = stringValue.replaceFirst("stream<\\(", "stream <").replaceFirst("\\)>$", ">");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,36 @@ public void testNegativeScenarios() {
"found 'table<record {| |}>'", 461, 19);
validateError(negativeResult, index++, "incompatible types: expected 'string', " +
"found 'table<record {| int a; int b; |}>'", 462, 16);
validateError(negativeResult, index++, "incompatible types: 'string' cannot be constrained with 'int'",
473, 49);
validateError(negativeResult, index++, "incompatible types: 'string' cannot be constrained " +
"with '[int,int,int,int]'", 474, 49);
validateError(negativeResult, index++, "incompatible types: 'xml' cannot be constrained with 'string'",
478, 42);
validateError(negativeResult, index++, "incompatible types: 'xml' cannot be constrained with 'int'",
479, 42);
validateError(negativeResult, index++, "incompatible types: 'xml' cannot be constrained with " +
"'[int,int,int,int]'", 480, 42);
validateError(negativeResult, index++, "incompatible types: 'table<Employee> key(name)' " +
"cannot be constrained with 'int'", 487, 41);
validateError(negativeResult, index++, "incompatible types: 'table<Employee> key(name)' " +
"cannot be constrained with '[string,int]'", 488, 41);
validateError(negativeResult, index++, "incompatible types: 'table<Employee> key(name)' " +
"cannot be constrained with 'table<Employee> key(name)'", 489, 41);
validateError(negativeResult, index++, "query expression that constructs a mapping must " +
"start with the map keyword", 493, 36);
validateError(negativeResult, index++, "query expression that constructs a mapping must " +
"start with the map keyword", 494, 36);
validateError(negativeResult, index++, "query expression that constructs a mapping must " +
"start with the map keyword", 495, 36);
validateError(negativeResult, index++, "incompatible type in select clause: expected " +
"[string,any|error], found 'int'", 496, 40);
validateError(negativeResult, index++, "incompatible type in select clause: expected " +
"[string,any|error], found 'record {| int A; |}'", 497, 40);
validateError(negativeResult, index++, "incompatible types: 'T1' " +
"cannot be constrained with 'int'", 504, 37);
validateError(negativeResult, index++, "incompatible types: 'T1' " +
"cannot be constrained with 'T1'", 505, 37);
Assert.assertEquals(negativeResult.getErrorCount(), index);
}

Expand Down Expand Up @@ -423,6 +453,55 @@ public Object[] dataToTestQueryExprWithQueryConstructTypWithRegExp() {
};
}

@Test(description = "Test query expr returning a stream", dataProvider = "SimpleQueryReturnStreamFunctionList")
public void testQueryReturnStream(String funcName) {
BRunUtil.invoke(result, funcName);
}

@DataProvider(name = "SimpleQueryReturnStreamFunctionList")
public Object[][] simpleQueryReturnStreamFunctionList() {
return new Object[][]{
{"testSimpleQueryReturnStream2"},
{"testSimpleQueryReturnStream3"}
};
}

@Test(description = "Test query expr with stream in from clause returning a stream ")
public void testStreamInFromClauseWithReturnStream2() {
BRunUtil.invoke(result, "testStreamInFromClauseWithReturnStream2");
}

@Test(description = "Test query expr with multiple from, let and where clauses returning a stream ")
public void testMultipleFromWhereAndLetReturnStream2() {
BRunUtil.invoke(result, "testMultipleFromWhereAndLetReturnStream2");
}


@Test(description = "Test query expr with inner join returning a stream ")
public void testInnerJoinAndLimitReturnStream2() {
BRunUtil.invoke(result, "testInnerJoinAndLimitReturnStream2");
}

@Test(description = "Test query expr returning table")
public void testSimpleQueryExprReturnTable2() {
BRunUtil.invoke(result, "testSimpleQueryExprReturnTable2");
}

@Test
public void testConstructTablesWithRecords() {
BRunUtil.invoke(result, "testConstructTablesWithRecords");
}

@Test
public void testConstructMapsWithTuples() {
BRunUtil.invoke(result, "testConstructMapsWithTuples");
}

@Test
public void testInnerQueryConstructedWithCEP() {
BRunUtil.invoke(result, "testInnerQueryConstructedWithCEP");
}

@AfterClass
public void tearDown() {
result = null;
Expand Down
Loading