Skip to content

Commit

Permalink
Merge pull request #41117 from pcnfernando/issue_37670
Browse files Browse the repository at this point in the history
[Query Expression]Restrict result type being resolved as `map` without the query-construct-type
  • Loading branch information
pcnfernando authored Aug 18, 2023
2 parents ab7e94f + 4416569 commit e6f3165
Show file tree
Hide file tree
Showing 29 changed files with 784 additions and 55 deletions.
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,9 +809,12 @@ public enum DiagnosticErrorCode implements DiagnosticCode {
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"),
CANNOT_USE_ALTERNATE_WAIT_ACTION_WITHIN_MULTIPLE_WAIT_ACTION("BCE4053",
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"),
CANNOT_USE_ALTERNATE_WAIT_ACTION_WITHIN_MULTIPLE_WAIT_ACTION("BCE4056",
"cannot.use.alternate.wait.action.within.multiple.wait.action"),
EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4054", "future.expression.expected")
EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4057", "future.expression.expected")
;

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 @@ -439,8 +441,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 All @@ -465,7 +469,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel
if (errorTypes.size() > 1) {
BType actualQueryType = silentTypeCheckExpr(queryExpr, symTable.noType, data);
if (actualQueryType != symTable.semanticError) {
types.checkType(queryExpr, actualQueryType,
types.checkType(queryExpr, actualQueryType,
BUnionType.create(null, new LinkedHashSet<>(expTypes)));
errorTypes.forEach(expType -> {
if (expType.tag == TypeTags.UNION) {
Expand All @@ -483,7 +487,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel
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()) {
Expand Down Expand Up @@ -648,26 +652,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) {
switch (Types.getImpliedType(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 @@ -2099,7 +2099,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.getImpliedType(spreadOpType);

switch (spreadOpReferredType.tag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2003,3 +2003,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,10 @@ public Object[][] dataProvider() {
{"createVariableForOptionalFieldAccess1.json"},
{"createVariableForOptionalFieldAccess2.json"},
{"createVariableWithTypeDesc.json"},
{"createVariableInQueryAction1.json"},
{"createVariableInQueryAction2.json"},
{"createVariableInQueryAction3.json"},
{"createVariableInQueryAction4.json"},

// Tuple related
{"createVariableWithTuple1.json"},
Expand Down Expand Up @@ -166,7 +170,8 @@ public Object[][] negativeDataProvider() {
{"createVariableNegative1.json"},
{"createVariableNegative2.json"},
{"createVariableNegative3.json"},
{"createVariableNegative4.json"}
{"createVariableNegative4.json"},
{"createVariableInQueryActionNegative.json"}
};
}
}
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,8 @@
{
"position": {
"line": 2,
"character": 4
},
"source": "createVariableInQueryAction4.bal",
"expected": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"position": {
"line": 2,
"character": 4
},
"source": "createVariableInQueryAction4.bal",
"expected": []
}
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
@@ -0,0 +1,4 @@
public function func() {
xml x1 = xml `<book>The Lost World</book>`;
from xml element in x1 select element.toBalString();
}
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
Loading

0 comments on commit e6f3165

Please sign in to comment.