diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java index e87e81305050..f6cf065c909e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java @@ -6380,7 +6380,7 @@ private void rewriteFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr) { (BVarSymbol) fieldAccessExpr.symbol, false, fieldAccessExpr.isStoreOnCreation); } } else if (types.isLaxFieldAccessAllowed(refType)) { - if (!(varRefTypeTag == TypeTags.XML || varRefTypeTag == TypeTags.XML_ELEMENT)) { + if (!types.isAssignable(refType, symTable.xmlType)) { if (varRefTypeTag == TypeTags.MAP && TypeTags.isXMLTypeTag(Types.getImpliedType(((BMapType) refType).constraint).tag)) { result = rewriteExpr(rewriteLaxMapAccess(fieldAccessExpr)); @@ -6391,6 +6391,7 @@ private void rewriteFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr) { fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.jsonType); targetVarRef = new BLangJSONAccessExpr(fieldAccessExpr.pos, fieldAccessExpr.expr, stringLit); } else { + fieldAccessExpr.expr = types.addConversionExprIfRequired(fieldAccessExpr.expr, symTable.xmlType); BLangInvocation xmlAccessInvocation = rewriteXMLAttributeOrElemNameAccess(fieldAccessExpr); xmlAccessInvocation.setBType(fieldAccessExpr.getBType()); result = xmlAccessInvocation; 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 b4e633503905..656fe22211ea 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 @@ -8666,6 +8666,9 @@ private BType getLaxFieldAccessType(BType exprType) { return symTable.jsonType; case TypeTags.XML: case TypeTags.XML_ELEMENT: + case TypeTags.XML_COMMENT: + case TypeTags.XML_PI: + case TypeTags.XML_TEXT: return symTable.stringType; case TypeTags.MAP: return ((BMapType) exprType).constraint; @@ -8760,7 +8763,7 @@ private boolean accessCouldResultInError(BType bType) { return false; } - if (type.tag == TypeTags.XML) { + if (types.isAssignable(bType, symTable.xmlType)) { return true; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java index b6f80c1f19cf..600e0ab8a79a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java @@ -313,7 +313,7 @@ public BType checkType(Location pos, public boolean isLaxFieldAccessAllowed(BType type) { type = Types.getImpliedType(type); Set visited = new HashSet<>(); - return isLaxType(type, visited) == 1 || type.tag == TypeTags.XML || type.tag == TypeTags.XML_ELEMENT; + return isLaxType(type, visited) == 1 || isAssignable(type, symTable.xmlType); } // TODO : clean diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLAttributeAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLAttributeAccessTest.java index 9a1986c3afd0..87973410c768 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLAttributeAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLAttributeAccessTest.java @@ -78,9 +78,69 @@ public void testGetAttrOfASequence() { @Test public void testXMLAttributeAccessNegative() { CompileResult negative = BCompileUtil.compile("test-src/types/xml/xml-attribute-access-syntax-neg.bal"); - Assert.assertEquals(negative.getErrorCount(), 2); - BAssertUtil.validateError(negative, 0, "invalid character ':' in field access expression", 7, 13); - BAssertUtil.validateError(negative, 1, "invalid character ':' in field access expression", 10, 13); + int index = 0; + BAssertUtil.validateError(negative, index++, "invalid character ':' in field access expression", 7, 13); + BAssertUtil.validateError(negative, index++, "invalid character ':' in field access expression", 10, 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)'", 17, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)?'", 18, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 19, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)?'", + 20, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)'", 23, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)?'", 24, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 25, 16); + BAssertUtil.validateError(negative, index++, + "incompatible types: expected '(string|error)', found '(string|error)?'", 26, 22); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml?', found '(string|error)'", 29, + 14); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)?'", 30, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string?', found '(string|error)'", + 31, 17); + BAssertUtil.validateError(negative, index++, + "incompatible types: expected '(string|error)', found '(string|error)?'", 32, 9); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml?', found '(string|error)'", 35, + 14); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)?'", 36, + 13); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 37, 16); + BAssertUtil.validateError(negative, index++, + "incompatible types: expected '(string|error)', found '(string|error)?'", 38, 9); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml', found '(string|error)'", 41, + 13); + BAssertUtil.validateError(negative, index++, + "invalid operation: type '(string|error)' does not support field access", 42, 13); + BAssertUtil.validateError(negative, index++, + "invalid operation: type '(string|error)' does not support optional field access", 43, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 49, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string?', found '(string|error)?'", + 50, 17); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 53, 16); + BAssertUtil.validateError(negative, index++, + "incompatible types: expected '(string|error)', found '(string|error)?'", 54, 22); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 57, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string?', found '(string|error)?'", + 58, 17); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string?', found '(string|error)'", + 61, 17); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string?', found '(string|error)?'", + 62, 17); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found '(string|error)'", + 65, 16); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'xml?', found '(string|error)?'", 66, + 14); + Assert.assertEquals(negative.getErrorCount(), index); + } @Test @@ -114,6 +174,16 @@ public void testErrorsOnXMLIndexedOptionalAttributeAccess() { BRunUtil.invoke(compileResult, "testErrorsOnXMLIndexedOptionalAttributeAccess"); } + @Test + public void testXmlAttributeAccessOnXmlUnionTypes() { + BRunUtil.invoke(compileResult, "testXmlAttributeAccessOnXmlUnionTypes"); + } + + @Test + public void testErrorsOnXmlAttributeAccessOnNonXmlElementValue() { + BRunUtil.invoke(compileResult, "testErrorsOnXmlAttributeAccessOnNonXmlElementValue"); + } + @AfterClass public void tearDown() { compileResult = null; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax-neg.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax-neg.bal index 787810cac3f7..3ad093d046f3 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax-neg.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax-neg.bal @@ -10,3 +10,58 @@ function testXMLAttributeAccessWithNamespaceSyntaxNeg() { var q = m.b:c; } +type XC xml:Comment; + +function testXmlAttributeAccessOnNonXmlElementValueNeg() { + xml:Comment x1 = xml ``; + xml _ = x1.attr; + xml _ = x1?.attr; + string _ = x1.attr; + string _ = x1?.attr; + + xml:ProcessingInstruction x2 = xml ``; + xml _ = x2.attr; + xml _ = x2?.attr; + string _ = x1.attr; + string|error r = x1?.attr; + + xml:Text x3 = xml `This is an xml text`; + xml? _ = x3.attr; + xml _ = x3?.attr; + string? _ = x3.attr; + r = x3?.attr; + + xml x4 = xml `text two`; + xml? _ = x4.attr; + xml _ = x4?.attr; + string _ = x4.attr; + r = x4?.attr; + + XC x5 = xml ``; + xml _ = x5.attr; + xml _ = x5.attr.attr2; + string _ = x5.attr?.attr3; +} + + +function testOptionalXmlAttributeAccessNeg() { + xml:Element x1 = xml ``; + string _ = x1.attr; + string? _ = x1?.attr; + + XC x2 = xml ``; + string _ = x2.attr; + string|error r = x1?.attr; + + xml:ProcessingInstruction x3 = xml ``; + string _ = x3.attr; + string? _ = x3?.attr; + + xml:Text x4 = xml `xml text`; + string? _ = x4.attr; + string? _ = x4?.attr; + + xml:Text|xml:Comment x5 = xml ``; + string _ = x5.attr; + xml? _ = x5?.attr; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax.bal index 8958a7b06ffe..c2fffbbab3df 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/xml/xml-attribute-access-syntax.bal @@ -194,6 +194,166 @@ function testErrorsOnXMLIndexedOptionalAttributeAccess() { assertError((x3/**//*)[0]?.id, errorMessage, expTextDetailMessage); } +type XC xml:Comment; +type XPI xml:ProcessingInstruction; +type XT xml:Text; +type XE xml:Element; + +function testXmlAttributeAccessOnXmlUnionTypes() { + xml x1 = xml `a`; + string|error result = x1.attr; + assertNonErrorValue(result, "aa"); + string|error? resultOptional = x1?.attr; + assertNonErrorValue(resultOptional, "aa"); + + xml x2 = xml `b`; + assertNonErrorValue(x2.attr, "ba"); + assertNonErrorValue(x2?.attr, "ba"); + + xml x3 = xml `c`; + assertNonErrorValue(x3.attr, "ca"); + assertNonErrorValue(x3?.attr, "ca"); + + xml x4 = xml `d`; + assertNonErrorValue(x4.attr, "da"); + assertNonErrorValue(x4?.attr, "da"); + + xml x5 = xml `e`; + result = x5.attr; + assertNonErrorValue(result, "ea"); + resultOptional = x5?.attr; + assertNonErrorValue(resultOptional, "ea"); + + xml x6 = xml `f`; + assertNonErrorValue(x6.attr, "fa"); + assertNonErrorValue(x6?.attr, "fa"); + + xml x7 = xml `g`; + result = x7.attr; + assertNonErrorValue(result, "ga"); + resultOptional = x7?.attr; + assertNonErrorValue(resultOptional, "ga"); + + xml:Element|xml:ProcessingInstruction x8 = xml `h`; + assertNonErrorValue(x8.attr, "ha"); + assertNonErrorValue(x8?.attr, "ha"); + + xml:Element|xml:Text x9 = xml `j`; + result = x9.attr; + assertNonErrorValue(result, "ja"); + resultOptional = x9?.attr; + assertNonErrorValue(resultOptional, "ja"); + + xml:Element|xml:Comment x10 = xml `k`; + assertNonErrorValue(x10.attr, "ka"); + assertNonErrorValue(x10?.attr, "ka"); + + XC|XE x11 = xml `l`; + result = x11.attr; + assertNonErrorValue(result, "la"); + resultOptional = x11?.attr; + assertNonErrorValue(resultOptional, "la"); +} + +function testErrorsOnXmlAttributeAccessOnNonXmlElementValue() { + string errorMessage = "{ballerina/lang.xml}XMLOperationError"; + + xml:Comment x1 = xml ``; + string|error result = x1.attr; + assertError(result, errorMessage, "invalid xml attribute access on xml comment"); + string|error? resultOptional = x1?.attr; + assertError(resultOptional, errorMessage, "invalid xml attribute access on xml comment"); + + xml:Text x2 = xml `text`; + assertError(x2.attr, errorMessage, "invalid xml attribute access on xml text"); + assertError(x2?.attr, errorMessage, "invalid xml attribute access on xml text"); + + xml:ProcessingInstruction x3 = xml ``; + assertError(x3.attr, errorMessage, "invalid xml attribute access on xml pi"); + assertError(x3?.attr, errorMessage, "invalid xml attribute access on xml pi"); + + xml x4 = xml `text`; + result = x4.attr; + assertError(result, errorMessage, "invalid xml attribute access on xml sequence"); + resultOptional = x4?.attr; + assertError(resultOptional, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x5 = xml ``; + assertError(x5.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x5?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x6 = xml `text`; + assertError(x6.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x6?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x7 = xml `text`; + result = x7.attr; + assertError(result, errorMessage, "invalid xml attribute access on xml sequence"); + resultOptional = x7?.attr; + assertError(resultOptional, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x8 = xml `text`; + assertError(x8.attr, errorMessage, "invalid xml attribute access on xml text"); + assertError(x8?.attr, errorMessage, "invalid xml attribute access on xml text"); + + xml x9 = xml ``; + assertError(x9.attr, errorMessage, "invalid xml attribute access on xml comment"); + assertError(x9?.attr, errorMessage, "invalid xml attribute access on xml comment"); + + xml x10 = xml ``; + assertError(x10.attr, errorMessage, "invalid xml attribute access on xml pi"); + assertError(x10?.attr, errorMessage, "invalid xml attribute access on xml pi"); + + xml x11 = xml `atext`; + assertError(x11.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x11?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x12 = xml `a`; + assertError(x12.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x12?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x13 = xml `a`; + assertError(x13.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x13?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + xml x14 = + xml `atext`; + assertError(x14.attr, errorMessage, "invalid xml attribute access on xml sequence"); + assertError(x14?.attr, errorMessage, "invalid xml attribute access on xml sequence"); + + XC x15 = xml ``; + assertError(x15.attr, errorMessage, "invalid xml attribute access on xml comment"); + assertError(x15?.attr, errorMessage, "invalid xml attribute access on xml comment"); + + XPI x16 = xml ``; + result = x16.attr; + assertError(result, errorMessage, "invalid xml attribute access on xml pi"); + resultOptional = x16?.attr; + assertError(resultOptional, errorMessage, "invalid xml attribute access on xml pi"); + + XT x17 = xml `text`; + assertError(x17.attr, errorMessage, "invalid xml attribute access on xml text"); + assertError(x17?.attr, errorMessage, "invalid xml attribute access on xml text"); + + xml:Comment|xml:Text x18 = xml ``; + assertError(x18.attr, errorMessage, "invalid xml attribute access on xml comment"); + assertError(x18?.attr, errorMessage, "invalid xml attribute access on xml comment"); + + xml:Text|xml:ProcessingInstruction x19 = xml `text`; + result = x19.attr; + assertError(result, errorMessage, "invalid xml attribute access on xml text"); + resultOptional = x19.attr; + assertError(resultOptional, errorMessage, "invalid xml attribute access on xml text"); + + xml:ProcessingInstruction|xml:Comment x20 = xml ``; + assertError(x20.attr, errorMessage, "invalid xml attribute access on xml pi"); + assertError(x20?.attr, errorMessage, "invalid xml attribute access on xml pi"); + + xml:ProcessingInstruction|xml:Element x21 = xml ``; + assertError(x21.attr, errorMessage, "invalid xml attribute access on xml pi"); + assertError(x21?.attr, errorMessage, "invalid xml attribute access on xml pi"); +} + function assertNonErrorValue(anydata|error actual, anydata expected) { if actual is error { panic error("expected [" + expected.toString() + "] " + ", but got error with message " + actual.message());