Skip to content

Commit

Permalink
Merge pull request #41841 from poorna2152/fix_-41571
Browse files Browse the repository at this point in the history
Fix invalid error for inaccessible module definitions in mapping constructor
  • Loading branch information
poorna2152 authored Apr 3, 2024
2 parents f421aaa + 73e2aab commit ceb5155
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2576,7 +2576,7 @@ public BType checkMappingConstructorCompatibility(BType bType, BLangRecordLitera
if (!erroredExpType) {
reportIncompatibleMappingConstructorError(mappingConstructor, bType, data);
}
validateSpecifiedFields(mappingConstructor, symTable.semanticError, data);
defineInferredRecordType(mappingConstructor, symTable.noType, data);
return symTable.semanticError;
} else if (compatibleTypes.size() != 1) {
dlog.error(mappingConstructor.pos, DiagnosticErrorCode.AMBIGUOUS_TYPES, bType);
Expand Down Expand Up @@ -7956,9 +7956,7 @@ private TypeSymbolPair checkRecordLiteralKeyExpr(BLangExpression keyExpr, boolea
Name fieldName;

if (computedKey) {
checkExpr(keyExpr, symTable.stringType, data);

if (keyExpr.getBType() == symTable.semanticError) {
if (exprIncompatible(symTable.stringType, keyExpr, data)) {
return new TypeSymbolPair(null, symTable.semanticError);
}

Expand Down Expand Up @@ -8021,13 +8019,9 @@ private BType getAllFieldType(BRecordType recordType) {

private boolean checkValidJsonOrMapLiteralKeyExpr(BLangExpression keyExpr, boolean computedKey, AnalyzerData data) {
if (computedKey) {
checkExpr(keyExpr, symTable.stringType, data);

if (keyExpr.getBType() == symTable.semanticError) {
return false;
}
return true;
} else if (keyExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF ||
return !exprIncompatible(symTable.stringType, keyExpr, data);
}
if (keyExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF ||
(keyExpr.getKind() == NodeKind.LITERAL && (keyExpr).getBType().tag == TypeTags.STRING)) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.
*/

package org.ballerinalang.test.bala.expressions;

import org.ballerinalang.test.BAssertUtil;
import org.ballerinalang.test.BCompileUtil;
import org.ballerinalang.test.BRunUtil;
import org.ballerinalang.test.CompileResult;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
* Bala test cases for mapping constructor expression.
*
* @since 2201.9.0
*/
public class MappingConstructorExprBalaTest {

@BeforeClass
public void setup() {
BCompileUtil.compileAndCacheBala("test-src/bala/test_projects/test_project");
}

@Test
public void testModuleConstantsInMappingConstructor() {
CompileResult compileResult =
BCompileUtil.compile("test-src/bala/test_bala/expressions/mapping_constructor_expr.bal");
BRunUtil.invoke(compileResult, "testModuleConstantsInMappingConstructor");
}

@Test
public void testMappingConstructorExprNegative() {
CompileResult negativeCompileResult = BCompileUtil.compile(
"test-src/bala/test_bala/expressions/mapping_constructor_expr_negative.bal");
int i = 0;
BAssertUtil.validateError(negativeCompileResult, i++,
"attempt to refer to non-accessible symbol 'MAPPING_NAME'", 24, 23);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_NAME'", 24, 23);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible mapping constructor expression for type 'map<string>?'", 25, 22);
BAssertUtil.validateError(negativeCompileResult, i++,
"attempt to refer to non-accessible symbol 'MAPPING_NAME'", 25, 24);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_NAME'", 25, 24);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible mapping constructor expression for type 'map<string>?'", 26, 22);
BAssertUtil.validateError(negativeCompileResult, i++,
"attempt to refer to non-accessible symbol 'MAPPING_NAME'", 26, 24);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_NAME'", 26, 24);
BAssertUtil.validateError(negativeCompileResult, i++, "attempt to refer to non-accessible symbol 'MAPPING_C'",
26, 43);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_C'", 26, 43);
BAssertUtil.validateError(negativeCompileResult, i++, "missing non-defaultable required record field 'name'",
27, 14);
BAssertUtil.validateError(negativeCompileResult, i++,
"attempt to refer to non-accessible symbol 'MAPPING_NAME'", 27, 16);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_NAME'", 27, 16);
BAssertUtil.validateError(negativeCompileResult, i++, "attempt to refer to non-accessible symbol 'MAPPING_C'",
27, 35);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_C'", 27, 35);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible mapping constructor expression for type '(map<string>|Rec)'", 28, 25);
BAssertUtil.validateError(negativeCompileResult, i++,
"attempt to refer to non-accessible symbol 'MAPPING_NAME'", 28, 27);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_NAME'", 28, 27);
BAssertUtil.validateError(negativeCompileResult, i++, "attempt to refer to non-accessible symbol 'MAPPING_C'",
28, 46);
BAssertUtil.validateError(negativeCompileResult, i++, "undefined symbol 'MAPPING_C'", 28, 46);
Assert.assertEquals(negativeCompileResult.getErrorCount(), i);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,36 @@ public Object[][] mappingConstructorTests() {
public void diagnosticsTest() {
CompileResult result = BCompileUtil.compile(
"test-src/expressions/mappingconstructor/mapping_constructor_negative.bal");
Assert.assertEquals(result.getErrorCount(), 14);
validateError(result, 0, "incompatible mapping constructor expression for type '(string|Person)'", 33, 23);
validateError(result, 1, "ambiguous type '(PersonTwo|PersonThree)'", 37, 31);
validateError(result, 2,
"a type compatible with mapping constructor expressions not found in type '(int|float)'", 41, 19);
validateError(result, 3, "ambiguous type '(map<int>|map<string>)'", 45, 31);
validateError(result, 4, "ambiguous type '(map<(int|string)>|map<(string|boolean)>)'", 47, 46);
validateError(result, 5, "unknown type 'NoRecord'", 55, 5);
validateError(result, 6, "incompatible types: 'int' cannot be cast to 'string'", 55, 22);
validateError(result, 7, "invalid field access: 'salary' is not a required field in record 'PersonThree', use" +
" member access to access a field that may have been specified as a rest field", 55, 41);
validateError(result, 8, "undefined symbol 'c'", 55, 55);
validateError(result, 9, "unknown type 'Foo'", 59, 5);
validateError(result, 10, "incompatible types: 'string' cannot be cast to 'boolean'", 59, 17);
validateError(result, 11, "unknown type 'Foo'", 60, 5);
validateError(result, 12, "incompatible types: 'int' cannot be cast to 'boolean'", 60, 30);
validateError(result, 13, "ambiguous type '(any|map)'", 64, 22);
int index = 0;
validateError(result, index++, "incompatible mapping constructor expression for type '(string|Person)'", 33,
23);
validateError(result, index++, "ambiguous type '(PersonTwo|PersonThree)'", 37, 31);
validateError(result, index++,
"a type compatible with mapping constructor expressions not found in type '(int|float)'", 41, 19);
validateError(result, index++, "ambiguous type '(map<int>|map<string>)'", 45, 31);
validateError(result, index++, "ambiguous type '(map<(int|string)>|map<(string|boolean)>)'", 47, 46);
validateError(result, index++, "unknown type 'NoRecord'", 55, 5);
validateError(result, index++, "incompatible types: 'int' cannot be cast to 'string'", 55, 22);
validateError(result, index++,
"invalid field access: 'salary' is not a required field in record 'PersonThree', use" +
" member access to access a field that may have been specified as a rest field", 55, 41);
validateError(result, index++, "undefined symbol 'c'", 55, 55);
validateError(result, index++, "unknown type 'Foo'", 59, 5);
validateError(result, index++, "incompatible types: 'string' cannot be cast to 'boolean'", 59, 17);
validateError(result, index++, "unknown type 'Foo'", 60, 5);
validateError(result, index++, "incompatible types: 'int' cannot be cast to 'boolean'", 60, 30);
validateError(result, index++, "ambiguous type '(any|map)'", 64, 22);
validateError(result, index++, "incompatible mapping constructor expression for type 'map<string>?'", 68,
22);
validateError(result, index++, "undefined symbol 'NAME'", 68, 24);
validateError(result, index++, "incompatible mapping constructor expression for type 'map<string>?'", 69,
22);
validateError(result, index++, "undefined symbol 'NAME'", 69, 24);
validateError(result, index++, "undefined symbol 'NAME'", 69, 32);
validateError(result, index++, "missing non-defaultable required record field 'name'", 70, 34);
validateError(result, index++, "undefined symbol 'NAME'", 70, 36);
validateError(result, index++, "undefined symbol 'NAME'", 70, 44);
Assert.assertEquals(result.getErrorCount(), index);
}

@Test
Expand Down Expand Up @@ -394,6 +407,8 @@ public void testReadOnlyFieldsSemanticNegative() {
33, 35);
validateError(compileResult, index++,
"incompatible mapping constructor expression for type '(Employee|Details)'", 34, 27);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'Details'", 34, 64);
validateError(compileResult, index++, "incompatible types: expected '(Employee & readonly)', found 'Employee'",
40, 18);
validateError(compileResult, index++, "incompatible types: expected '(Details & readonly)', found 'Details'",
Expand All @@ -403,6 +418,8 @@ public void testReadOnlyFieldsSemanticNegative() {
validateError(compileResult, index++,
"incompatible mapping constructor expression for type '(map<string>|map<(Details|string)>)'",
55, 42);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'Details'", 55, 79);
validateError(compileResult, index++,
"incompatible types: expected '(map<(Details|string)> & readonly)', " +
"found 'map<(Details|string)>'", 61, 18);
Expand All @@ -414,13 +431,21 @@ public void testReadOnlyFieldsSemanticNegative() {
"incompatible types: expected '(any & readonly)', found 'stream<boolean>'", 77, 57);
validateError(compileResult, index++, "incompatible mapping constructor expression for type '(" +
"record {| future<any>...; |}|NonReadOnlyFields)'", 78, 57);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'future<int>'", 78, 67);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'stream<boolean>'", 78, 84);
validateError(compileResult, index++,
"incompatible types: expected '((any|error) & readonly)', found 'future<int>'", 81, 39);
validateError(compileResult, index++,
"incompatible types: expected '((any|error) & readonly)', found 'stream<boolean>'", 81, 51);
validateError(compileResult, index++,
"incompatible mapping constructor expression for type '(map<(any|error)>|map<future<int>>)'",
82, 43);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'future<int>'", 82, 62);
validateError(compileResult, index++,
"incompatible types: expected 'readonly', found 'stream<boolean>'", 82, 74);
validateError(compileResult, index++, "incompatible types: expected 'record {| int i; anydata...; |}', found " +
"'record {| readonly (Details & readonly) d1; readonly (Details & readonly) d2; " +
"record {| string str; |} d3; readonly record {| string str; readonly int count; |} & readonly d4; " +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.

import testorg/foo;

type Rec record {
string name;
};

function testModuleConstantsInMappingConstructor() {
map<string> m1 = {[foo:MAPPING_A] : "A_VAL"};
assertEquals("A_VAL", m1["A"]);
map<string> m2 = {[foo:MAPPING_A] : foo:MAPPING_B};
assertEquals("B", m2["A"]);
map<string>? m3 = {[foo:MAPPING_A] : foo:MAPPING_B};
assertEquals("B", m3["A"]);
map<string>|Rec m4 = {[foo:MAPPING_A] : foo:MAPPING_B};
assertEquals("B", m4["A"]);
Rec r1 = {name: foo:MAPPING_A};
assertEquals("A", r1.name);
Rec? r2 = {name: foo:MAPPING_A};
assertEquals("A", (<Rec>r2).name);
}

function assertEquals(anydata expected, anydata actual) {
if expected != actual {
panic error(string `expected ${expected.toBalString()}, found ${actual.toBalString()}`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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.

import testorg/foo;

type Rec record {
string name;
};

public function testUnAccessibleModuleConstInMappingConstructor() {
map<string> _ = {[foo:MAPPING_NAME]: "Amy"};
map<string>? _ = {[foo:MAPPING_NAME]: "Amy"};
map<string>? _ = {[foo:MAPPING_NAME]: foo:MAPPING_C};
Rec? _ = {[foo:MAPPING_NAME]: foo:MAPPING_C};
map<string>|Rec _ = {[foo:MAPPING_NAME]: foo:MAPPING_C};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.

public const MAPPING_A = "A";
public const MAPPING_B = "B";
const MAPPING_C = "C";
const MAPPING_NAME = "name";
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,9 @@ function testMappingConstrWithIssuesInCET() {
function testAmbiguousTypeWithAny() {
any|map<any> _ = {a: 1};
}

function testMappingConstructorWithUndefinedVars() {
map<string>? _ = {[NAME] : "Amy"};
map<string>? _ = {[NAME] : NAME};
record {|string name;|}? _ = {[NAME] : NAME};
}

0 comments on commit ceb5155

Please sign in to comment.