Skip to content

Commit

Permalink
Fix xml invalid subtyping
Browse files Browse the repository at this point in the history
  • Loading branch information
poorna2152 committed Jul 7, 2024
1 parent b8fd113 commit bb425a4
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4480,7 +4480,7 @@ private void handleForeachDefinitionVariables(VariableDefinitionNode variableDef
// If the type node is available, we get the type from it.
BType typeNodeType = symResolver.resolveTypeNode(variableNode.typeNode, blockEnv);
// Checking whether the RHS type is assignable to LHS type.
if (types.isAssignable(varType, typeNodeType)) {
if (types.constituentTypesAssignable(varType, typeNodeType)) {
if (onFail && varType == symTable.neverType) {
varType = typeNodeType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1456,7 +1456,7 @@ private boolean isFunctionTypeAssignable(BInvokableType source, BInvokableType t
boolean isTypeParam = TypeParamAnalyzer.isTypeParam(targetParam);

if (isTypeParam) {
if (!isAssignable(sourceParam, targetParam)) {
if (!constituentTypesAssignable(sourceParam, targetParam)) {
return false;
}
} else {
Expand All @@ -1481,6 +1481,17 @@ private boolean isFunctionTypeAssignable(BInvokableType source, BInvokableType t
return checkFunctionTypeEquality(source, target, unresolvedTypes, (s, t, ut) -> isAssignable(t, s, ut));
}

public boolean constituentTypesAssignable(BType varType, BType typeNodeType) {
if (varType.tag == TypeTags.XML) {
typeNodeType = getReferredType(typeNodeType);
if (typeNodeType.tag == TypeTags.UNION) {
return isAssignable(symTable.xmlItemType, typeNodeType);
}
return typeNodeType.tag == TypeTags.XML;
}
return isAssignable(varType, typeNodeType);
}

public boolean isInherentlyImmutableType(BType type) {
type = getImpliedType(type);
if (isValueType(type)) {
Expand Down Expand Up @@ -3681,11 +3692,6 @@ private boolean isAssignableToUnionType(BType source, BType target, Set<TypePair
sourceIterator.remove();
continue;
}
if (sMember.tag == TypeTags.XML &&
isAssignableToUnionType(expandedXMLBuiltinSubtypes, target, unresolvedTypes)) {
sourceIterator.remove();
continue;
}

if (!isValueType(sMember)) {
if (!targetIsAUnion) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,10 @@ public class SymbolTable {
public final BRegexpType regExpType = new BRegexpType(TypeTags.REGEXP, Names.REGEXP_TYPE);
public final BType xmlNeverType = new BXMLType(neverType, null);
public final BType xmlElementSeqType = new BXMLType(xmlElementType, null);
public final BType xmlItemType = BUnionType.create(null, xmlElementType, xmlCommentType,
xmlPIType, xmlTextType);

public final BType xmlType = new BXMLType(BUnionType.create(null, xmlElementType, xmlCommentType,
xmlPIType, xmlTextType), null);
public final BType xmlType = new BXMLType(xmlItemType, null);

public BAnydataType anydataType;
public BArrayType arrayAnydataType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,17 @@ public void testXMLNegativeSemantics() {
"'(xml<xml<xml:Text>>|xml<xml<xml:Comment>>)', found 'xml'", 150, 54);
BAssertUtil.validateError(negativeResult, index++, "missing gt token", 154, 22);
BAssertUtil.validateError(negativeResult, index++, "missing gt token", 155, 28);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 166, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 169, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 172, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 175, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 178, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 181, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 184, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 187, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 190, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 193, 12);
BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'UX', found 'X'", 196, 12);
Assert.assertEquals(index, negativeResult.getErrorCount());
}

Expand Down Expand Up @@ -456,6 +467,11 @@ public void testXMLLiteralWithConditionExpr() {
BRunUtil.invoke(result, "testXMLLiteralWithConditionExpr");
}

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

@AfterClass
public void tearDown() {
result = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,44 @@ function testMissingClosingGTToken() {
xml x1 = xml `<a/a>`;
xml x2 = xml `<a n="a"/a>`;
}

type X xml;
type XE xml<xml:Element>;
type XP xml<xml:ProcessingInstruction>;
type XC xml<xml:Comment>;
type UX XE|XP|XC|xml:Text;

function testXMLInvalidSubtype() {
X x1 = xml `<a></a><b></b>`;
UX _ = x1;

X x2 = xml `<?target instructions?><?a data?>`;
UX _ = x2;

X x3 = xml `<!--comment one--><!--comment two-->`;
UX _ = x3;

X x4 = xml `random sequence of text`;
UX _ = x4;

X x5 = xml `<a></a><b></b><?target instructions?><?c data?>`;
UX _ = x5;

X x6 = xml `<a></a><b></b><!--comment one--><!--comment two-->`;
UX _ = x6;

X x7 = xml `<a></a><b></b>random text`;
UX _ = x7;

X x8 = xml `<?target instructions?><?a data?>text`;
UX _ = x8;

X x9 = xml `<?target instructions?><?a data?><!--comment one--><!--comment two-->`;
UX _ = x9;

X x10 = xml `<!--comment one--><!--comment two-->text`;
UX _ = x10;

X x11 = xml `<a></a><b></b><?d data?><?c data?>?><!--comment--><!--comment-->text`;
UX _ = x11;
}
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,75 @@ function testXMLLiteralWithConditionExpr() {
xml w3 = (s ?: (s ?: (v1 is xml:Element ? e2 : xml `<user>Foo</user>`))) + xml `<element>B</element>`;
test:assertEquals(w3.toString(), "<user>Foo</user><element>B</element>");
}

type X xml;
type XE xml<xml:Element>;
type XP xml<xml:ProcessingInstruction>;
type XC xml<xml:Comment>;
type UX XE|XP|XC|xml:Text;

function testXMLSubtype() {
X x1 = xml `<a></a><b></b>`;
UX _ = <UX>x1;

X x2 = xml `<?target instructions?><?a data?>`;
UX _ = <UX>x2;

X x3 = xml `<!--comment one--><!--comment two-->`;
UX _ = <UX>x3;

X x4 = xml `random sequence of text`;
UX _ = <UX>x4;

UX|error ux;
X x5 = xml `<a></a><b></b><?target instructions?><?c data?>`;
ux = trap <UX>x5;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x6 = xml `<a></a><b></b><!--comment one--><!--comment two-->`;
ux = trap <UX>x6;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x7 = xml `<a></a><b></b>random text`;
ux = trap <UX>x7;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x8 = xml `<?target instructions?><?a data?>text`;
ux = trap <UX>x8;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x9 = xml `<?target instructions?><?a data?><!--comment one--><!--comment two-->`;
ux = trap <UX>x9;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x10 = xml `<!--comment one--><!--comment two-->text`;
ux = trap <UX>x10;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");

X x11 = xml `<a></a><b></b><?d data?><?c data?>?><!--comment--><!--comment-->text`;
ux = trap <UX>x11;
assertError(ux, "{ballerina}TypeCastError",
"incompatible types: 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' cannot be cast to 'UX'");
}

type Error error<record {string message;}>;

function assertError(any|error value, string errorMessage, string expDetailMessage) {
if value is Error {
if value.message() != errorMessage {
panic error("Expected error message: " + errorMessage + " found: " + value.message());
}

if value.detail().message == expDetailMessage {
return;
}
panic error("Expected error detail message: " + expDetailMessage + " found: " + value.detail().message);
}
panic error("Expected: Error, found: " + (typeof value).toString());
}

0 comments on commit bb425a4

Please sign in to comment.