diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index cbb7c1acc120..04024079b891 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -251,7 +251,6 @@ public enum DiagnosticErrorCode implements DiagnosticCode { INCOMPATIBLE_TYPES_CONVERSION_WITH_SUGGESTION("BCE2503", "incompatible.types.conversion.with.suggestion"), UNSAFE_CAST_ATTEMPT("BCE2504", "unsafe.cast.attempt"), - INVALID_LITERAL_FOR_TYPE("BCE2506", "invalid.literal.for.type"), INCOMPATIBLE_MAPPING_CONSTRUCTOR("BCE2507", "incompatible.mapping.constructor.expression"), MAPPING_CONSTRUCTOR_COMPATIBLE_TYPE_NOT_FOUND("BCE2508", "mapping.constructor.compatible.type.not.found"), CANNOT_INFER_TYPES_FOR_TUPLE_BINDING("BCE2509", "cannot.infer.types.for.tuple.binding"), @@ -333,7 +332,6 @@ public enum DiagnosticErrorCode implements DiagnosticCode { INVALID_ANY_VAR_DEF("BCE2574", "invalid.any.var.def"), INVALID_RECORD_LITERAL("BCE2575", "invalid.record.literal"), INVALID_FIELD_IN_RECORD_BINDING_PATTERN("BCE2576", "invalid.field.in.record.binding.pattern"), - INVALID_RECORD_LITERAL_BINDING_PATTERN("BCE2577", "invalid.record.literal.in.binding.pattern"), DUPLICATE_KEY_IN_MAPPING_CONSTRUCTOR("BCE2578", "duplicate.key.in.mapping.constructor"), DUPLICATE_KEY_IN_TABLE_LITERAL("BCE2579", "duplicate.key.in.table.literal"), DUPLICATE_KEY_IN_RECORD_LITERAL_SPREAD_OP("BCE2580", "duplicate.key.in.record.literal.spread.op"), diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java index 3e2a6bb5831c..d75255f64b92 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java @@ -2199,11 +2199,6 @@ public void visit(BLangSimpleVariableDef varDefNode, AnalyzerData data) { @Override public void visit(BLangRecordVariableDef varDefNode, AnalyzerData data) { - // TODO: 10/18/18 Need to support record literals as well - if (varDefNode.var.expr != null && varDefNode.var.expr.getKind() == RECORD_LITERAL_EXPR) { - dlog.error(varDefNode.pos, DiagnosticErrorCode.INVALID_LITERAL_FOR_TYPE, "record binding pattern"); - return; - } analyzeNode(varDefNode.var, data); } @@ -2386,15 +2381,9 @@ public void visit(BLangRecordDestructure recordDeStmt, AnalyzerData data) { setTypeOfVarRef(recordDeStmt.varRef, data); SymbolEnv currentEnv = data.env; - typeChecker.checkExpr(recordDeStmt.varRef, currentEnv, symTable.noType, data.prevEnvs, + data.typeChecker.checkExpr(recordDeStmt.varRef, currentEnv, symTable.noType, data.prevEnvs, data.commonAnalyzerData); - - if (recordDeStmt.expr.getKind() == RECORD_LITERAL_EXPR) { - // TODO: 10/18/18 Need to support record literals as well - dlog.error(recordDeStmt.expr.pos, DiagnosticErrorCode.INVALID_RECORD_LITERAL_BINDING_PATTERN); - return; - } - typeChecker.checkExpr(recordDeStmt.expr, currentEnv, symTable.noType, data.prevEnvs, + data.typeChecker.checkExpr(recordDeStmt.expr, currentEnv, symTable.noType, data.prevEnvs, data.commonAnalyzerData); checkRecordVarRefEquivalency(recordDeStmt.pos, recordDeStmt.varRef, recordDeStmt.expr.getBType(), recordDeStmt.expr.pos, data); 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 656fe22211ea..38ae3cc9824d 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 @@ -3292,8 +3292,7 @@ public void visit(BLangRecordVarRef varRefExpr, AnalyzerData data) { SOURCE); if (restParam == null) { - bRecordType.sealed = true; - bRecordType.restFieldType = symTable.noType; + bRecordType.restFieldType = symTable.anyOrErrorType; } else if (restParam.getBType() == symTable.semanticError) { bRecordType.restFieldType = symTable.mapType; } else { diff --git a/compiler/ballerina-lang/src/main/resources/compiler.properties b/compiler/ballerina-lang/src/main/resources/compiler.properties index 145d2b2e0bb6..e5e34ca0052b 100644 --- a/compiler/ballerina-lang/src/main/resources/compiler.properties +++ b/compiler/ballerina-lang/src/main/resources/compiler.properties @@ -513,9 +513,6 @@ error.array.literal.not.allowed=\ error.string.template.literal.not.allowed=\ string template literals not allowed here -error.invalid.literal.for.type=\ - invalid literal for type ''{0}'' - error.invalid.literal.for.match.pattern=\ invalid literal for match pattern; allowed literals are simple, tuple and record only @@ -1277,9 +1274,6 @@ error.invalid.record.binding.pattern=\ error.invalid.field.in.record.binding.pattern=\ invalid record binding pattern; unknown field ''{0}'' in record type ''{1}'' -error.invalid.record.literal.in.binding.pattern=\ - record literal is not supported for record binding pattern - error.no.matching.record.ref.found=\ no matching record reference found for field ''{0}'' diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/varref/RecordVariableReferenceTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/varref/RecordVariableReferenceTest.java index efaf09280bdf..365a24f33648 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/varref/RecordVariableReferenceTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/varref/RecordVariableReferenceTest.java @@ -28,6 +28,7 @@ import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** @@ -136,43 +137,24 @@ public void testRestParameterType() { Assert.assertTrue((Boolean) returns); } - // TODO: Uncomment below tests once record literal is supported with var ref -// -// @Test(description = "Test simple record variable definition") -// public void testVarAssignmentOfRecordLiteral() { -// Object returns = JvmRunUtil.invoke(result, "testVarAssignmentOfRecordLiteral"); -// Assert.assertEquals(returns.toString(), "Peter"); -// Assert.assertTrue((Boolean) returns[1]]); -// Assert.assertEquals(returns[2], 12); -// Assert.assertEquals(returns[3].toString(), "Y"); -// } -// -// @Test(description = "Test simple record variable definition") -// public void testVarAssignmentOfRecordLiteral2() { -// Object returns = JvmRunUtil.invoke(result, "testVarAssignmentOfRecordLiteral2"); -// Assert.assertEquals(returns.toString(), "Peter"); -// Assert.assertTrue((Boolean) returns[1]]); -// Assert.assertEquals(( ((BMap) returns[2]).get(StringUtils.fromString("age"))), 12); -// Assert.assertEquals(((BMap) returns[2]).get(StringUtils.fromString("format")).toString(), "Y"); -// } -// -// @Test(description = "Test simple record variable definition") -// public void testVarAssignmentOfRecordLiteral3() { -// Object returns = JvmRunUtil.invoke(result, "testVarAssignmentOfRecordLiteral3"); -// Assert.assertEquals(returns.toString(), "Peter"); -// Assert.assertTrue((Boolean) returns[1]]); -// Assert.assertEquals(( ((BMap) returns[2]).get(StringUtils.fromString("age"))), 12); -// Assert.assertEquals(((BMap) returns[2]).get(StringUtils.fromString("format")).toString(), "Y"); -// } -// -// @Test(description = "Test simple record variable definition") -// public void testVarAssignmentOfRecordLiteral4() { -// Object returns = JvmRunUtil.invoke(result, "testVarAssignmentOfRecordLiteral4"); -// Assert.assertEquals(returns.toString(), "Peter"); -// Assert.assertTrue((Boolean) returns[1]]); -// Assert.assertEquals(( ((BMap) returns[2]).get(StringUtils.fromString("age"))), 12); -// Assert.assertEquals(((BMap) returns[2]).get(StringUtils.fromString("format")).toString(), "Y"); -// } + @Test(description = "Test variable assignment with record literal", dataProvider = "VarAssignmentOfRecordLiteral") + public void testVarAssignmentOfRecordLiteral(String funcName) { + BRunUtil.invoke(result, funcName); + } + + @DataProvider(name = "VarAssignmentOfRecordLiteral") + public Object[] varAssignmentOfRecordLiteralProvider() { + return new Object[]{ + "testVarAssignmentOfRecordLiteral1", + "testVarAssignmentOfRecordLiteral2", + "testVarAssignmentOfRecordLiteral3", + "testVarAssignmentOfRecordLiteral4", + "testVarAssignmentOfRecordLiteral5", + "testVarAssignmentOfRecordLiteral6", + "testVarAssignmentOfRecordLiteral7", + "testVarAssignmentOfRecordLiteral8", + }; + } @Test public void testRecordFieldBindingPatternsWithIdentifierEscapes() { @@ -189,6 +171,11 @@ public void testMappingBindingWithSingleNameFieldBinding() { BRunUtil.invoke(result, "testMappingBindingWithSingleNameFieldBinding"); } + @Test + public void testMappingBindingPatternAgainstOpenRecordInTupleDestructuring() { + BRunUtil.invoke(result, "testMappingBindingPatternAgainstOpenRecordInTupleDestructuring"); + } + @Test public void testRecordVariablesSemanticsNegative() { resultSemanticsNegative = BCompileUtil.compile( @@ -210,7 +197,10 @@ public void testRecordVariablesSemanticsNegative() { BAssertUtil.validateError(resultSemanticsNegative, ++i, "incompatible types: expected 'record type', found 'int'", 96, 38); BAssertUtil.validateError(resultSemanticsNegative, ++i, - "record literal is not supported for record binding pattern", 97, 38); + "incompatible types: expected 'Bar', found 'string'", 97, 12); + BAssertUtil.validateError(resultSemanticsNegative, ++i, + "incompatible types: expected 'string', found 'record {| int var1; [string,int,boolean] var2; |}'", + 97, 27); BAssertUtil.validateError(resultSemanticsNegative, ++i, "incompatible types: expected 'Person', found 'Age'", 106, 19); BAssertUtil.validateError(resultSemanticsNegative, ++i, @@ -281,6 +271,34 @@ public void testNegativeRecordVariables() { Assert.assertEquals(resultNegative.getDiagnostics().length, i); } + @Test + public void testDestructuringWithRecordReferenceNegative() { + CompileResult resultNegative = BCompileUtil.compile( + "test-src/expressions/varref/record_bp_in_list_and_record_destructuring_assignment_negative.bal"); + int i = 0; + BAssertUtil.validateError(resultNegative, i++, "incompatible types: " + + "expected '[record {| int a; int b; (any|error)...; |}]', " + + "found '[record {| int a; int b?; |}]'", 22, 15); + BAssertUtil.validateError(resultNegative, i++, "incompatible types: " + + "expected '[int,[record {| int b; (any|error)...; |}]]', " + + "found '[int,[record {| int b?; anydata...; |}]]'", 24, 18); + BAssertUtil.validateError(resultNegative, i++, "incompatible types: " + + "expected '[record {| int a; int b; int c; (any|error)...; |}]', " + + "found '[record {| int a; int b; anydata...; |}]'", 32, 18); + BAssertUtil.validateError(resultNegative, i++, "incompatible types: " + + "expected '[int,[record {| int b; int c; (any|error)...; |}]]', " + + "found '[int,[record {| int b; anydata...; |}]]'", 34, 21); + BAssertUtil.validateError(resultNegative, i++, "invalid field binding pattern; can only bind required fields", + 41, 9); + BAssertUtil.validateError(resultNegative, i++, "invalid field binding pattern; can only bind required fields", + 43, 14); + BAssertUtil.validateError(resultNegative, i++, "invalid record binding pattern; " + + "unknown field 'b' in record type 'record {| int a; anydata...; |}'", 50, 5); + BAssertUtil.validateError(resultNegative, i++, "invalid record binding pattern; " + + "unknown field 'b' in record type 'record {| |} & readonly'", 52, 13); + Assert.assertEquals(resultNegative.getDiagnostics().length, i); + } + @AfterClass public void tearDown() { result = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java index e6cb778128a3..bebc32dcd459 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java @@ -248,66 +248,64 @@ public void testNegativeCases() { 65, 33); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected '[int,int]', found '[int...]'", 70, 36); - BAssertUtil.validateError(negativeResult, i++, "record literal is not supported for record binding pattern", - 79, 32); BAssertUtil.validateError(negativeResult, i++, "invalid operation: type " + - "'seq record {| string name; int price1; |}' does not support field access", 87, 29); + "'seq record {| string name; int price1; |}' does not support field access", 78, 29); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 87, 29); + "list constructor or function invocation", 78, 29); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int[]', found 'seq int?'", - 108, 32); - BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 116, 40); + 99, 32); + BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 107, 40); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element" + - " list constructor or function invocation", 116, 40); - BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 120, 33); + " list constructor or function invocation", 107, 40); + BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 111, 33); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element" + - " list constructor or function invocation", 120, 33); + " list constructor or function invocation", 111, 33); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'seq int'", - 133, 36); + 124, 36); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 133, 36); + "list constructor or function invocation", 124, 36); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 133, 45); + "list constructor or function invocation", 124, 45); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'seq int'", - 136, 28); + 127, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 136, 28); + "list constructor or function invocation", 127, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 136, 37); + "list constructor or function invocation", 127, 37); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'seq int'", - 139, 28); + 130, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 139, 28); + "list constructor or function invocation", 130, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 139, 37); + "list constructor or function invocation", 130, 37); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'seq int'", - 142, 28); + 133, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 142, 28); + "list constructor or function invocation", 133, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 142, 37); + "list constructor or function invocation", 133, 37); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'seq int'", - 145, 26); + 136, 26); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 145, 26); + "list constructor or function invocation", 136, 26); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 145, 35); - BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 148, 36); - BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after rest argument", 151, 39); - BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 154, 37); - BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 154, 40); + "list constructor or function invocation", 136, 35); + BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 139, 36); + BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after rest argument", 142, 39); + BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 145, 37); + BAssertUtil.validateError(negativeResult, i++, "arguments not allowed after seq argument", 145, 40); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected '(any|error)[]', found 'int'", - 157, 37); + 148, 37); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected " + - "'([int,int...]|record {| int n; |})', found '[int...]'", 167, 43); + "'([int,int...]|record {| int n; |})', found '[int...]'", 158, 43); BAssertUtil.validateError(negativeResult, i++, "invalid grouping key type 'error', expected a subtype of " + - "'anydata'", 175, 26); + "'anydata'", 166, 26); BAssertUtil.validateError(negativeResult, i++, "invalid grouping key type 'error', expected a subtype of " + - "'anydata'", 178, 26); + "'anydata'", 169, 26); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'string', found 'seq string'", - 200, 24); + 191, 24); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element " + - "list constructor or function invocation", 200, 24); + "list constructor or function invocation", 191, 24); Assert.assertEquals(negativeResult.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java index 2ab2c48a7c4c..c23f187fced2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java @@ -200,6 +200,11 @@ public void testIfStmtInsideDoClause() { BRunUtil.invoke(result, "testIfStmtInsideDoClause"); } + @Test + public void testRecordDestructureWithRecordLiteralInsideDoClause() { + BRunUtil.invoke(result, "testRecordDestructureWithRecordLiteralInsideDoClause"); + } + @Test(dataProvider = "dataToTestQueryActionWithVar") public void testQueryActionWithVar(String functionName) { BRunUtil.invoke(result, functionName); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/variabledef/RecordVariableDefinitionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/variabledef/RecordVariableDefinitionTest.java index 6a508e484178..7096133c10e4 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/variabledef/RecordVariableDefinitionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/variabledef/RecordVariableDefinitionTest.java @@ -284,6 +284,11 @@ public void testResolvingRestField() { BRunUtil.invoke(result, "testRestFieldResolving"); } + @Test + public void testRecordTypedBindingPatternAgainstRecordLiteralInRecordDestructuring() { + BRunUtil.invoke(result, "testRecordTypedBindingPatternAgainstRecordLiteralInRecordDestructuring"); + } + @Test public void testNegativeRecordVariables() { String redeclaredSymbol = "redeclared symbol "; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/TupleDestructureTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/TupleDestructureTest.java index bfd4f120d661..90b19a38ad17 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/TupleDestructureTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/TupleDestructureTest.java @@ -87,6 +87,8 @@ public void testTupleDestructure() { Assert.assertEquals(returns.get(0).toString(), "true"); Assert.assertEquals(returns.get(1).toString(), "string value"); Assert.assertEquals(returns.get(2).toString(), "[25,12.5]"); + + BRunUtil.invoke(result, "tupleDestructureTest10"); } @Test(description = "Test positive tuple destructure scenarios") diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record-variable-reference.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record-variable-reference.bal index af40932b0398..61d8e84e0c94 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record-variable-reference.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record-variable-reference.bal @@ -38,7 +38,7 @@ function testVariableAssignment() returns [string, boolean, int, string] { } function getPerson() returns Person { - return {name: "Peter", married: true, age: {age:12, format: "Y"}, extra: ["ds", 4]}; + return {name: "Peter", married: true, age: {age: 12, format: "Y"}, extra: ["ds", 4]}; } type Foo record { @@ -191,56 +191,206 @@ function testRestParameterType() returns boolean { map other1 = {}; map other2 = {}; - IntRestRecord rec1 = { name: "A", married: true, "age": 19, "token": 200 }; - { name, ...other1 } = rec1; + IntRestRecord rec1 = {name: "A", married: true, "age": 19, "token": 200}; + {name, ...other1} = rec1; any a1 = other1; return a1 is map; } -// TODO: Uncomment below tests once record literal is supported with var ref +function testVarAssignmentOfRecordLiteral1() { + string fName; + boolean married; + int theAge; + string format; -//function testVarAssignmentOfRecordLiteral() returns (string, boolean, int, string) { -// string fName; -// boolean married; -// int theAge; -// string format; -// map theMap; -// -// {name: fName, age: {age: theAge, format}, married, ...theMap} = {name: "Peter", married: true, age: {age:12, format: "Y"}}; -// return (fName, married, theAge, format); -//} -// -//function testVarAssignmentOfRecordLiteral2() returns (string, boolean, Age) { -// string fName; -// boolean married; -// Age age; -// map theMap; -// -// {name: fName, age, married, ...theMap} = {name: "Peter", married: true, age: {age:12, format: "Y"}}; -// return (fName, married, age); -//} -// -//function testVarAssignmentOfRecordLiteral3() returns (string, boolean, map) { -// string fName; -// boolean married; -// map age; -// map theMap; -// -// {name: fName, age, married, ...theMap} = {name: "Peter", married: true, age: {age:12, format: "Y"}}; -// return (fName, married, age); -//} -// -//function testVarAssignmentOfRecordLiteral4() returns (string, boolean, json) { -// string fName; -// boolean married; -// json age; -// map theMap; -// -// {name: fName, age, married, ...theMap} = {name: "Peter", married: true, age: {age:12, format: "Y"}}; -// return (fName, married, age); -//} + { + name: fName, + age: { + age: theAge, + format + }, + married + } = { + name: "Peter", + married: true, + age: {age: 12, format: "Y"} + }; + + assertEquality("Peter", fName); + assertEquality(true, married); + assertEquality(12, theAge); + assertEquality("Y", format); +} + +function testVarAssignmentOfRecordLiteral2() { + string fName; + boolean married; + Age age; + map theMap; + + { + name: fName, + age, + married, + ...theMap + } = { + name: "Peter", + married: true, + age: {age: 12, format: "Y"} + }; + assertEquality("Peter", fName); + assertEquality(true, married); + assertEquality(12, age.age); + assertEquality("Y", age.format); + assertEquality({}, theMap); +} + +function testVarAssignmentOfRecordLiteral3() { + string fName; + boolean married; + map age; + map theMap; + + { + name: fName, + age, + married, + ...theMap + } = { + name: "Peter", + married: true, + age: {age: 12, format: "Y"} + }; + assertEquality("Peter", fName); + assertEquality(true, married); + assertEquality({age: 12, format: "Y"}, age); + assertEquality({}, theMap); +} + +function testVarAssignmentOfRecordLiteral4() { + string fName; + boolean married; + json age; + map theMap; + + { + name: fName, + age, + ...theMap + } = { + name: "Peter", + married: true, + age: {age: 12, format: "Y"} + }; + assertEquality("Peter", fName); + assertEquality({age: 12, format: "Y"}, age); + assertEquality({married: true}, theMap); +} + +function testVarAssignmentOfRecordLiteral5() { + string fName; + map theMap; + + { + name: fName, + ...theMap + } = { + name: "Peter", + married: true, + age: {age: 12, format: "Y"}, + city: "New York", + state: "New" + }; + assertEquality("Peter", fName); + assertEquality({ + married: true, + age: {age: 12, format: "Y"}, + city: "New York", + state: "New" + }, theMap); +} + +function testVarAssignmentOfRecordLiteral6() { + int departmentId; + string departmentName; + int floorId; + + { + department: { + id: departmentId, + name: departmentName, + location: {floorId} + } + } = { + name: "John Doe", + department: { + id: 123, + name: "Marketing", + location: {floorId: 2, area: "B"} + } + }; + assertEquality(123, departmentId); + assertEquality("Marketing", departmentName); + assertEquality(2, floorId); +} + +function testVarAssignmentOfRecordLiteral7() { + int empId; + map otherEmpDetails; + map otherDepDetails; + + {id: empId, department: {...otherDepDetails}, ...otherEmpDetails} = { + id: 1, + name: "John Doe", + age: 30, + department: { + id: 123, + name: "Marketing", + location: {floor: 2, area: "B"} + } + }; + assertEquality(1, empId); + assertEquality({ + id: 123, + name: "Marketing", + location: {floor: 2, area: "B"} + }, otherDepDetails); + assertEquality({ + name: "John Doe", + age: 30 + }, otherEmpDetails); +} + +function testVarAssignmentOfRecordLiteral8() { + int id; + string name; + map otherDetails; + + {id, name, ...otherDetails} = { + id: 1, + name: "John Doe", + age: 30, + ...{ + department: { + id: 123, + name: "Marketing", + location: {floor: 2, area: "B"} + } + } + }; + assertEquality(1, id); + assertEquality("John Doe", name); + assertEquality({ + age: 30, + department: { + id: 123, + name: "Marketing", + location: {floor: 2, area: "B"} + } + }, otherDetails); +} type EmployeeDetails record {| string emp\:name; @@ -293,7 +443,7 @@ function testReadOnlyRecordWithMappingBindingPatternInDestructuringAssignment() string y; {x, y} = f1; - assertEquality( [1, 2], x); + assertEquality([1, 2], x); assertEquality("s1", y); readonly & record { @@ -304,15 +454,15 @@ function testReadOnlyRecordWithMappingBindingPatternInDestructuringAssignment() int[] & readonly x2; string y2; {a, b: {x: x2, y: y2}} = r; - assertEquality( [12, 34, 56], a); - assertEquality( [1, 2], x2); + assertEquality([12, 34, 56], a); + assertEquality([1, 2], x2); assertEquality("s1", y2); int[] c; int[] d; {a: c, b: {x: d, y: y2}} = r; - assertEquality( [12, 34, 56], c); - assertEquality( [1, 2], d); + assertEquality([12, 34, 56], c); + assertEquality([1, 2], d); assertEquality("s1", y2); } @@ -369,7 +519,92 @@ function testMappingBindingWithSingleNameFieldBinding() { assertEquality("bar4", b); } -function assertEquality(anydata expected, anydata actual) { +function testMappingBindingPatternAgainstOpenRecordInTupleDestructuring() { + [Some, Some] r1 = [{var1: "A", var2: "B"}, {var1: "C", var2: "D"}]; + string xVar1; + string xVar11; + [{var1: xVar1}, {var1: xVar11}] = r1; + assertEquality("A", xVar1); + assertEquality("C", xVar11); + + [record {int a; int b;}] r2 = [{a: 1, b: 2, "c": "three", "d": 0.003}]; + int a; + int b; + [{a, b}] = r2; + assertEquality(1, a); + assertEquality(2, b); + + [record {|int a; int b;|}] r3 = [{a: 3, b: 4}]; + [{a, b}] = [...r3]; + assertEquality(3, a); + assertEquality(4, b); + + record {|int b; string c;|} rec1 = {b: 2, c: "C"}; + string c; + [{a, b, c}] = [{a: 1, ...rec1}]; + assertEquality(1, a); + assertEquality(2, b); + assertEquality("C", c); + + [record {int a; int b; stream c;}] r4 = [{a: 5, b: 6, c: new}]; + [{a, b}] = r4; + assertEquality(5, a); + assertEquality(6, b); + + string fname; + string lname; + string id; + int age; + string nextId; + [record {string id; string fname; string lname;}, record {|string id; int age;|}] r5 + = [{id: "u1001", fname: "John", lname: "doe"}, {id: "u1002", age: 10}]; + [{id, fname, lname}, {id: nextId, age}] = r5; + assertEquality("u1001", id); + assertEquality("John", fname); + assertEquality("doe", lname); + assertEquality("u1002", nextId); + assertEquality(10, age); + + int p1; + int p2; + int p3; + int p4; + int p5; + [[record {int p1; int p2;}], [[record {int p3; int p4;}], record {int p5;}]] r6 + = [[{p1: 10, p2: 12}], [[{p3: 13, p4: 14}], {p5: 15}]]; + [[{p1, p2}], [[{p3, p4}], {p5}]] = r6; + assertEquality(10, p1); + assertEquality(12, p2); + assertEquality(13, p3); + assertEquality(14, p4); + assertEquality(15, p5); + + [record {|int a; int b; anydata...;|}] r7 = [{a: 1, b: 2}]; + [{a, b}] = r7; + assertEquality(1, a); + assertEquality(2, b); + + [record {|int a; int b; anydata...;|}] r8 = [{a: 10, b: 12}]; + [{a, b}] = [...r8]; + assertEquality(10, a); + assertEquality(12, b); + + int x1; + int x2; + var getMap = function() returns record {record {int x1; int x2;} x3;} => {x3: {x1: 2, x2: 3}}; + {x3: {x1, x2}} = getMap(); + assertEquality(2, x1); + assertEquality(3, x2); + assertEquality({x1: 2, x2: 3}, {x1, x2}); + + // TODO: Uncomment after fixing #40312 + // int d; + // error err = error("Transaction Failure", x = {d: 0}); + + // error(x = {d}) = err; +} + +function assertEquality(anydata expected, any actual) { if expected == actual { return; } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record_bp_in_list_and_record_destructuring_assignment_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record_bp_in_list_and_record_destructuring_assignment_negative.bal new file mode 100644 index 000000000000..b152aba963c2 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/varref/record_bp_in_list_and_record_destructuring_assignment_negative.bal @@ -0,0 +1,53 @@ +// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). +// +// 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. + +function testTupleDestructuringWithOptionalFieldBindingPattern() { + [record {|int a; int b?;|}] l1 = [{a: 1, b: 2}]; + int a; + int b; + int c; + [{a, b}]= l1; + [int, [record {int b?;}]] l2 = [1, [{b: 1}]]; + [a, [{b}]] = l2; +} + +function testTupleDestructuringWithUnspecifiedFields() { + [record {int a; int b;}] l1 = [{a: 1, b: 2, c: 3}]; + int a; + int b; + int c; + [{a, b, c}]= l1; + [int, [record {int b;}]] l2 = [1, [{b: 1}]]; + [a, [{b, c}]] = l2; +} + +function testRecordDestructuringWithOptionalFieldBindingPattern() { + record {int a; int b?;} r1 = {a: 1, b: 2}; + int a; + int b; + {a, b} = r1; + record {|int a; record {|int b?;|} bb;|} r2 = {a: 1, bb: {b: 2}}; + {a, bb: {b}} = r2; +} + +function testRecordDestructuringWithUnspecifiedFields() { + record {int a;} r1 = {a: 1}; + int a; + int b; + {a, b} = r1; + record {|int a; record {||} bb;|} r2 = {a: 1, bb: {}}; + {a, bb: {b}} = r2; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/group_by_clause_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/group_by_clause_negative.bal index ab9a29a0059f..4560d392af1c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/group_by_clause_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/group_by_clause_negative.bal @@ -71,15 +71,6 @@ function testTupleDestructure() { }; } -function testRecordDestructure() { - _ = from var {name, price} in [{name: "Saman", price: 11}] - group by name - do { - int[] prices; - {prices} = {prices: [price]}; // error - }; -} - function testSeqVarInInvalidPositions2() { var input = [{name: "Saman", price1: 11}]; var xx = from var item in input diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal index 0efc2aed6906..a6d26b07d99e 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal @@ -882,6 +882,17 @@ function testQueryActionWithCollectClauseInsideLeClause2() { }; } +function testRecordDestructureWithRecordLiteralInsideDoClause() { + int[] prices; + _ = from var {name, price} in [{name: "Doe", price: 12}, {name: "John", price: 10}] + do { + {prices} = {prices: [price]}; + }; + + assertEquality(1, prices.length()); + assertEquality(10, prices[0]); +} + function assertEquality(any|error expected, any|error actual) { if expected is anydata && actual is anydata && expected == actual { return; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/variabledef/record-variable-definition-stmt.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/variabledef/record-variable-definition-stmt.bal index 0cea1180c702..2f4d25efb1ec 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/variabledef/record-variable-definition-stmt.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/variabledef/record-variable-definition-stmt.bal @@ -774,6 +774,65 @@ function testNestedRecordVariableWithAnonymousType() { assertTrue(married); } +type PersonWithContact record {| + string name; + record {| + "fax"|"mobile" 'type; + string number; + |}[2] contact; +|}; + +function testRecordTypedBindingPatternAgainstRecordLiteralInRecordDestructuring() { + record {|string state; int postalCode;|} {state, postalCode} = {state: "Alabama", postalCode: 35004}; + assertEquality("Alabama", state); + assertEquality(35004, postalCode); + + record {|string street; float...;|} {street, ...coordinates} = {street: "Highway 61", "lat": 37.30, "lon": -90.40}; + assertEquality("Highway 61", street); + assertEquality({lat: 37.30, lon: -90.40}, coordinates); + + record {| + string street; + float lat; + float lon; + |} {street: street1, ...otherdata} = {street: "Highway 61", "lat": 37.30, "lon": -90.40}; + assertEquality("Highway 61", street1); + assertEquality({lat: 37.30, lon: -90.40}, otherdata); + + record {string state;} {state: state2, ...other} = {state: "Colorado", "postalCode": 80001}; + assertEquality("Colorado", state2); + assertEquality({postalCode: 80001}, other); + + var {country, ...rest} = {country: "USA", postalCode: 80001}; + assertEquality("USA", country); + assertEquality({postalCode: 80001}, rest); + + Address {postalCode: postalCode1, street: street3} = + {postalCode: 80001, street: {streetName: "Highway 61", city: "New York City"}}; + assertEquality(80001, postalCode1); + assertEquality({streetName: "Highway 61", city: "New York City"}, street3); + + Address {street: {city, ...otherStreetDetails}, ...otherAddressDetails} = + {postalCode: 80001, street: {streetName: "Highway 61", city: "New York City"}}; + assertEquality("New York City", city); + assertEquality({streetName: "Highway 61"}, otherStreetDetails); + assertEquality({postalCode: 80001}, otherAddressDetails); + + PersonWithContact {name, contact} = { + name: "John", + contact: [{'type: "fax", number: "056895634"}, {'type: "mobile", number: "0458967234"}] + }; + assertEquality("John", name); + assertEquality([{'type: "fax", number: "056895634"}, {'type: "mobile", number: "0458967234"}], contact); + + record {|string name;table employees;|} {name: companyName, employees} = { + name: "Xavier", + employees: table [{name: "John", married: true}, {name: "Doe", married: false}] + }; + assertEquality("Xavier", companyName); + assertEquality(table [{name: "John", married: true}, {name: "Doe", married: false}], employees); +} + ////////////////////////////////////////////////////////////////////////////////////// const ASSERTION_ERROR_REASON = "AssertionError"; @@ -782,17 +841,10 @@ function assertTrue(boolean actual) { assertEquality(true, actual); } -function assertEquality(any|error expected, any|error actual) { - if expected is anydata && actual is anydata && expected == actual { - return; - } - - if expected === actual { +function assertEquality(anydata expected, any actual) { + if expected == actual { return; } - string expectedValAsString = expected is error ? expected.toString() : expected.toString(); - string actualValAsString = actual is error ? actual.toString() : actual.toString(); - panic error(ASSERTION_ERROR_REASON, - message = "expected '" + expectedValAsString + "', found '" + actualValAsString + "'"); + panic error("expected '" + expected.toBalString() + "', found '" + actual.toBalString() + "'"); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/tuples/tuple_destructure_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/tuples/tuple_destructure_test.bal index c5e69864beaf..d08f1943a28e 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/tuples/tuple_destructure_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/tuples/tuple_destructure_test.bal @@ -109,3 +109,35 @@ function tupleDestructureTest9() returns [boolean, string, [int, float]] { return [a, b, c]; } + +function tupleDestructureTest10() { + Employee[2] employees = [employee1, employee2]; + + Employee e1; + Employee e2; + [e1, e2] = [...employees]; + assertEquality(true, e1.intern); + + Employee e3; + Employee e4; + [e3, e4] = [...[employee1, employee2]]; + assertEquality(true, e3.intern); + + Employee e5; + Employee e6; + [[e5, e6]] = [...[...[employees]]]; + assertEquality(false, e6.intern); + + Employee e7; + Employee e8; + [[e7, e8]] = [[...[...[employee1, employee2]]]]; + assertEquality(false, e8.intern); +} + +function assertEquality(anydata expected, anydata actual) { + if expected == actual { + return; + } + + panic error("expected '" + expected.toBalString() + "', found '" + actual.toBalString() + "'"); +}