diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java index e1287f447eeb..c443de1d123f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java @@ -655,16 +655,21 @@ public BLangNode transform(ModulePartNode modulePart) { compilationUnit.name = currentCompUnitName; compilationUnit.setPackageID(packageID); Location pos = getPosition(modulePart); + BLangIdentifier compUnit = this.createIdentifier(pos, compilationUnit.getName()); // Generate import declarations for (ImportDeclarationNode importDecl : modulePart.imports()) { BLangImportPackage bLangImport = (BLangImportPackage) importDecl.apply(this); - bLangImport.compUnit = this.createIdentifier(pos, compilationUnit.getName()); + bLangImport.compUnit = compUnit; compilationUnit.addTopLevelNode(bLangImport); } // Generate other module-level declarations for (ModuleMemberDeclarationNode member : modulePart.members()) { - compilationUnit.addTopLevelNode((TopLevelNode) member.apply(this)); + TopLevelNode node = (TopLevelNode) member.apply(this); + if (member.kind() == SyntaxKind.MODULE_XML_NAMESPACE_DECLARATION) { + ((BLangXMLNS) node).compUnit = compUnit; + } + compilationUnit.addTopLevelNode(node); } Location newLocation = new BLangDiagnosticLocation(pos.lineRange().fileName(), 0, 0, 0, 0, 0, 0); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java index ca13f4df3bb4..a9c2ef0811a2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java @@ -450,6 +450,7 @@ public void visit(BLangXMLNS source) { source.cloneRef = clone; clone.namespaceURI = clone(source.namespaceURI); clone.prefix = source.prefix; + clone.compUnit = source.compUnit; } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index 2c7c9b2b13a1..eb50ff7fdb0b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -79,7 +79,6 @@ import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.compiler.tree.SimpleBLangNodeAnalyzer; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangGroupExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; @@ -315,7 +314,8 @@ public void visit(BLangSimpleVarRef varRefExpr, AnalyzerData data) { dlog.error(varRefExpr.pos, DiagnosticErrorCode.UNDEFINED_MODULE, varRefExpr.pkgAlias); } else { BSymbol symbol = - getSymbolOfVarRef(varRefExpr.pos, data.env, names.fromIdNode(varRefExpr.pkgAlias), varName, data); + typeResolver.getSymbolOfVarRef(varRefExpr.pos, data.env, names.fromIdNode(varRefExpr.pkgAlias), + varName); if (symbol == symTable.notFoundSymbol) { data.resultType = symTable.semanticError; @@ -376,7 +376,8 @@ public void visit(BLangRecordLiteral.BLangRecordVarNameField varRefExpr, Analyze if (varRefExpr.pkgSymbol != symTable.notFoundSymbol) { BSymbol symbol = - getSymbolOfVarRef(varRefExpr.pos, data.env, names.fromIdNode(varRefExpr.pkgAlias), varName, data); + typeResolver.getSymbolOfVarRef(varRefExpr.pos, data.env, names.fromIdNode(varRefExpr.pkgAlias), + varName); if (symbol == symTable.notFoundSymbol) { data.resultType = symTable.semanticError; @@ -2017,24 +2018,6 @@ private BUnionType createFiniteType(BConstantSymbol constantSymbol, Object value return BUnionType.create(null, memberTypes); } - private BSymbol getSymbolOfVarRef(Location pos, SymbolEnv env, Name pkgAlias, Name varName, AnalyzerData data) { - if (pkgAlias == Names.EMPTY && data.modTable.containsKey(varName.value)) { - // modTable contains the available constants in current module. - BLangNode node = data.modTable.get(varName.value); - if (node.getKind() == NodeKind.CONSTANT) { - if (!typeResolver.resolvedConstants.contains((BLangConstant) node)) { - typeResolver.resolveConstant(data.env, data.modTable, (BLangConstant) node); - } - } else { - dlog.error(pos, DiagnosticErrorCode.EXPRESSION_IS_NOT_A_CONSTANT_EXPRESSION); - return symTable.notFoundSymbol; - } - } - - // Search and get the referenced variable from different module. - return symResolver.lookupMainSpaceSymbolInPackage(pos, env, pkgAlias, varName); - } - private boolean addFields(LinkedHashMap fields, BType keyValueType, String key, Location pos, BRecordTypeSymbol recordSymbol) { Name fieldName = Names.fromString(key); 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 46e73ce6cf36..7cb3e4f838d4 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 @@ -418,9 +418,8 @@ public void visit(BLangXMLNS xmlnsNode, AnalyzerData data) { typeChecker.checkExpr(xmlnsNode.namespaceURI, currentEnv, symTable.stringType, data.prevEnvs, data.commonAnalyzerData); - // Namespace node already having the symbol means we are inside an init-function, - // and the symbol has already been declared by the original statement. - if (xmlnsNode.symbol == null) { + // For module-level XML namespace declarations the symbol is already defined at symbol enter. + if (currentEnv.node.getKind() != NodeKind.PACKAGE) { symbolEnter.defineNode(xmlnsNode, currentEnv); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java index 8f29a69f07a1..2346136889ce 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java @@ -93,6 +93,7 @@ import org.wso2.ballerinalang.compiler.tree.BLangAnnotationAttachment; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangCompilationUnit; +import org.wso2.ballerinalang.compiler.tree.BLangConstantValue; import org.wso2.ballerinalang.compiler.tree.BLangErrorVariable; import org.wso2.ballerinalang.compiler.tree.BLangFunction; import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; @@ -417,17 +418,17 @@ private void defineConstructs(BLangPackage pkgNode, SymbolEnv pkgEnv) { // Define type definitions. this.typePrecedence = 0; - // Treat constants and type definitions in the same manner, since constants can be used as - // types. Also, there can be references between constant and type definitions in both ways. - // Thus visit them according to the precedence. - List typeAndClassDefs = new ArrayList<>(); - pkgNode.constants.forEach(constant -> typeAndClassDefs.add(constant)); - pkgNode.typeDefinitions.forEach(typDef -> typeAndClassDefs.add(typDef)); - List classDefinitions = getClassDefinitions(pkgNode.topLevelNodes); - classDefinitions.forEach(classDefn -> typeAndClassDefs.add(classDefn)); + // Treat constants, type definitions and xmlns declarations in the same manner, since constants can be used + // as types and can be referred to in XMLNS declarations. Also, there can be references between constant, + // type definitions and xmlns declarations in both ways. Thus visit them according to the precedence. + List moduleDefs = new ArrayList<>(); + moduleDefs.addAll(pkgNode.constants); + moduleDefs.addAll(pkgNode.typeDefinitions); + moduleDefs.addAll(pkgNode.xmlnsList); + moduleDefs.addAll(getClassDefinitions(pkgNode.topLevelNodes)); this.env = pkgEnv; - typeResolver.defineBTypes(typeAndClassDefs, pkgEnv); + typeResolver.defineBTypes(moduleDefs, pkgEnv); // Enabled logging errors after type def visit. // TODO: Do this in a cleaner way @@ -435,19 +436,19 @@ private void defineConstructs(BLangPackage pkgNode, SymbolEnv pkgEnv) { // Sort type definitions with precedence, before defining their members. pkgNode.typeDefinitions.sort(getTypePrecedenceComparator()); - typeAndClassDefs.sort(getTypePrecedenceComparator()); + moduleDefs.sort(getTypePrecedenceComparator()); // Define error details. defineErrorDetails(pkgNode.typeDefinitions, pkgEnv); // Define type def members (if any) - defineFunctions(typeAndClassDefs, pkgEnv); + defineFunctions(moduleDefs, pkgEnv); // Intersection type nodes need to look at the member fields of a structure too. // Once all the fields and members of other types are set revisit intersection type definitions to validate // them and set the fields and members of the relevant immutable type. validateIntersectionTypeDefinitions(pkgNode.typeDefinitions, pkgNode.packageID); - defineUndefinedReadOnlyTypes(pkgNode.typeDefinitions, typeAndClassDefs, pkgEnv); + defineUndefinedReadOnlyTypes(pkgNode.typeDefinitions, moduleDefs, pkgEnv); // Define service and resource nodes. pkgNode.services.forEach(service -> defineNode(service, pkgEnv)); @@ -1166,12 +1167,19 @@ public void initPredeclaredModules(Map predeclaredModules, @Override public void visit(BLangXMLNS xmlnsNode) { + defineXMLNS(env, xmlnsNode); + } + + public void defineXMLNS(SymbolEnv symEnv, BLangXMLNS xmlnsNode) { String nsURI = ""; if (xmlnsNode.namespaceURI.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { BLangSimpleVarRef varRef = (BLangSimpleVarRef) xmlnsNode.namespaceURI; if (Symbols.isFlagOn(varRef.symbol.flags, Flags.CONSTANT)) { - nsURI = ((BConstantSymbol) varRef.symbol).value.toString(); - checkInvalidNameSpaceDeclaration(xmlnsNode.pos, xmlnsNode.prefix, nsURI); + BLangConstantValue constantValue = ((BConstantSymbol) varRef.symbol).value; + if (constantValue != null) { + nsURI = constantValue.toString(); + checkInvalidNameSpaceDeclaration(xmlnsNode.pos, xmlnsNode.prefix, nsURI); + } } } else { nsURI = (String) ((BLangLiteral) xmlnsNode.namespaceURI).value; @@ -1185,14 +1193,16 @@ public void visit(BLangXMLNS xmlnsNode) { Name prefix = names.fromIdNode(xmlnsNode.prefix); Location nsSymbolPos = prefix.value.isEmpty() ? xmlnsNode.pos : xmlnsNode.prefix.pos; - BXMLNSSymbol xmlnsSymbol = Symbols.createXMLNSSymbol(prefix, nsURI, env.enclPkg.symbol.pkgID, env.scope.owner, - nsSymbolPos, getOrigin(prefix)); + BLangIdentifier compUnit = xmlnsNode.compUnit; + BXMLNSSymbol xmlnsSymbol = + Symbols.createXMLNSSymbol(prefix, nsURI, symEnv.enclPkg.symbol.pkgID, symEnv.scope.owner, nsSymbolPos, + getOrigin(prefix), compUnit != null ? names.fromIdNode(compUnit) : null); xmlnsNode.symbol = xmlnsSymbol; // First check for package-imports with the same alias. // Here we do not check for owner equality, since package import is always at the package // level, but the namespace declaration can be at any level. - BSymbol foundSym = symResolver.lookupSymbolInPrefixSpace(env, xmlnsSymbol.name); + BSymbol foundSym = symResolver.lookupSymbolInPrefixSpace(symEnv, xmlnsSymbol.name); if ((foundSym.tag & SymTag.PACKAGE) != SymTag.PACKAGE) { foundSym = symTable.notFoundSymbol; } @@ -1546,7 +1556,7 @@ private void checkErrorsOfUserDefinedType(SymbolEnv env, BLangNode unresolvedTyp public String getTypeOrClassName(BLangNode node) { if (node.getKind() == NodeKind.TYPE_DEFINITION || node.getKind() == NodeKind.CONSTANT) { return ((TypeDefinition) node).getName().getValue(); - } else { + } else { return ((BLangClassDefinition) node).getName().getValue(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java index b146c0c98512..1d13bc7016fc 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java @@ -225,6 +225,10 @@ public boolean checkForUniqueSymbol(Location pos, SymbolEnv env, BSymbol symbol) return true; } + if (symbol.tag == SymTag.XMLNS && isDistinctXMLNSSymbol((BXMLNSSymbol) symbol, (BXMLNSSymbol) foundSym)) { + return true; + } + // if a symbol is found, then check whether it is unique if (!isDistinctSymbol(pos, symbol, foundSym)) { return false; @@ -471,11 +475,11 @@ public BSymbol resolvePrefixSymbol(SymbolEnv env, Name pkgAlias, Name compUnit) BSymbol symbol = entry.symbol; long tag = symbol.tag; - if ((tag & SymTag.XMLNS) == SymTag.XMLNS) { + if (isDistinctXMLNSSymbol(symbol, compUnit)) { return symbol; } - if ((tag & SymTag.IMPORT) == SymTag.IMPORT && + if (!((tag & SymTag.XMLNS) == SymTag.XMLNS) && (tag & SymTag.IMPORT) == SymTag.IMPORT && ((BPackageSymbol) symbol).compUnit.equals(compUnit)) { ((BPackageSymbol) symbol).isUsed = true; return symbol; @@ -491,6 +495,14 @@ public BSymbol resolvePrefixSymbol(SymbolEnv env, Name pkgAlias, Name compUnit) return symTable.notFoundSymbol; } + private boolean isDistinctXMLNSSymbol(BSymbol symbol, Name compUnit) { + if (symbol instanceof BXMLNSSymbol bxmlnsSymbol) { + Name xmlnsCompUnit = bxmlnsSymbol.compUnit; + return xmlnsCompUnit == null || xmlnsCompUnit.equals(compUnit); + } + return false; + } + public BSymbol resolveAnnotation(Location pos, SymbolEnv env, Name pkgAlias, Name annotationName) { return this.lookupAnnotationSpaceSymbolInPackage(pos, env, pkgAlias, annotationName); } @@ -2646,6 +2658,19 @@ public boolean isReAtomNode(NodeKind kind) { } } + private boolean isDistinctXMLNSSymbol(BXMLNSSymbol symbol, BXMLNSSymbol foundSym) { + Name foundSymCompUnit = foundSym.compUnit; + Name symbolCompUnit = symbol.compUnit; + boolean isFoundSymModuleXmlns = foundSymCompUnit != null; + boolean isSymModuleXmlns = symbolCompUnit != null; + if (isFoundSymModuleXmlns && isSymModuleXmlns) { + return !foundSymCompUnit.value.equals(symbolCompUnit.value); + } + // If only one of the symbols have a compUnit then it is a module level xmlns and the symbols are distinct. + // If they both don't have a compUnit then it is a redeclared prefix. + return isFoundSymModuleXmlns || isSymModuleXmlns; + } + /** * @since 2.0.0 */ diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java index fb5c582c1c64..2688961b6165 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java @@ -77,6 +77,7 @@ import org.wso2.ballerinalang.compiler.tree.BLangTableKeySpecifier; import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; import org.wso2.ballerinalang.compiler.tree.BLangVariable; +import org.wso2.ballerinalang.compiler.tree.BLangXMLNS; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; @@ -213,31 +214,37 @@ public static TypeResolver getInstance(CompilerContext context) { public void defineBTypes(List moduleDefs, SymbolEnv pkgEnv) { this.pkgEnv = pkgEnv; typePrecedence = 0; - for (BLangNode typeAndClassDef : moduleDefs) { - String typeOrClassName = symEnter.getTypeOrClassName(typeAndClassDef); + for (BLangNode moduleDef : moduleDefs) { + if (moduleDef.getKind() == NodeKind.XMLNS) { + continue; + } + String typeOrClassName = symEnter.getTypeOrClassName(moduleDef); if (!modTable.containsKey(typeOrClassName)) { - modTable.put(typeOrClassName, typeAndClassDef); + modTable.put(typeOrClassName, moduleDef); } } for (BLangNode def : moduleDefs) { resolvingTypes = new Stack<>(); resolvingModuleDefs = new Stack<>(); - if (def.getKind() == NodeKind.CLASS_DEFN) { - intersectionTypeList = new HashMap<>(); - extracted(pkgEnv, (BLangClassDefinition) def, 0); - updateEffectiveTypeOfCyclicIntersectionTypes(pkgEnv); - } else if (def.getKind() == NodeKind.CONSTANT) { - resolveConstant(pkgEnv, modTable, (BLangConstant) def); - } else { - BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; - intersectionTypeList = new HashMap<>(); - resolveTypeDefinition(pkgEnv, modTable, typeDefinition, 0); - BType type = typeDefinition.typeNode.getBType(); - if (typeDefinition.hasCyclicReference) { - updateIsCyclicFlag(type); + switch (def.getKind()) { + case CLASS_DEFN -> { + intersectionTypeList = new HashMap<>(); + extracted(pkgEnv, (BLangClassDefinition) def, 0); + updateEffectiveTypeOfCyclicIntersectionTypes(pkgEnv); + } + case CONSTANT -> resolveConstant(pkgEnv, modTable, (BLangConstant) def); + case XMLNS -> resolveXMLNS(pkgEnv, (BLangXMLNS) def); + default -> { + BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; + intersectionTypeList = new HashMap<>(); + resolveTypeDefinition(pkgEnv, modTable, typeDefinition, 0); + BType type = typeDefinition.typeNode.getBType(); + if (typeDefinition.hasCyclicReference) { + updateIsCyclicFlag(type); + } + updateEffectiveTypeOfCyclicIntersectionTypes(pkgEnv); } - updateEffectiveTypeOfCyclicIntersectionTypes(pkgEnv); } resolvingTypes.clear(); resolvingModuleDefs.clear(); @@ -1979,6 +1986,33 @@ public void resolveConstant(SymbolEnv symEnv, Map modTable, B checkUniqueness(constant); } + public void resolveXMLNS(SymbolEnv symEnv, BLangXMLNS xmlnsNode) { + if (xmlnsNode.namespaceURI.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { + BLangSimpleVarRef varRef = (BLangSimpleVarRef) xmlnsNode.namespaceURI; + varRef.symbol = getSymbolOfVarRef(varRef.pos, symEnv, names.fromIdNode(varRef.pkgAlias), + names.fromIdNode(varRef.variableName)); + } + symEnter.defineXMLNS(symEnv, xmlnsNode); + } + + public BSymbol getSymbolOfVarRef(Location pos, SymbolEnv env, Name pkgAlias, Name varName) { + if (pkgAlias == Names.EMPTY && modTable.containsKey(varName.value)) { + // modTable contains the available constants in current module. + BLangNode node = modTable.get(varName.value); + if (node.getKind() == NodeKind.CONSTANT) { + if (!resolvedConstants.contains((BLangConstant) node)) { + resolveConstant(env, modTable, (BLangConstant) node); + } + } else { + dlog.error(pos, DiagnosticErrorCode.EXPRESSION_IS_NOT_A_CONSTANT_EXPRESSION); + return symTable.notFoundSymbol; + } + } + + // Search and get the referenced variable from different module. + return symResolver.lookupMainSpaceSymbolInPackage(pos, env, pkgAlias, varName); + } + private void checkUniqueness(BLangConstant constant) { if (constant.symbol.kind != SymbolKind.CONSTANT) { return; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BXMLNSSymbol.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BXMLNSSymbol.java index e3add1ad6abb..f12c6d67019a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BXMLNSSymbol.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BXMLNSSymbol.java @@ -35,6 +35,8 @@ public class BXMLNSSymbol extends BSymbol implements VariableSymbol { * Holds the namespace URI for lookup during semantic validations. */ public String namespaceURI; + // Applicable to only module level XMLNS declarations + public Name compUnit; public BXMLNSSymbol(Name prefix, String namespaceURI, PackageID pkgID, BSymbol owner, Location pos, SymbolOrigin origin) { @@ -43,6 +45,14 @@ public BXMLNSSymbol(Name prefix, String namespaceURI, PackageID pkgID, BSymbol o this.kind = SymbolKind.XMLNS; } + public BXMLNSSymbol(Name prefix, String namespaceURI, PackageID pkgID, BSymbol owner, Location pos, + SymbolOrigin origin, Name compUnit) { + super(SymTag.XMLNS, 0, prefix, pkgID, new BNoType(TypeTags.XMLNS), owner, pos, origin); + this.namespaceURI = namespaceURI; + this.kind = SymbolKind.XMLNS; + this.compUnit = compUnit; + } + @Override public Object getConstValue() { return null; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/Symbols.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/Symbols.java index e99dbbf31c01..4df3f8a51a75 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/Symbols.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/Symbols.java @@ -218,8 +218,8 @@ public static BXMLNSSymbol createXMLNSSymbol(Name name, PackageID pkgID, BSymbol owner, Location pos, - SymbolOrigin origin) { - return new BXMLNSSymbol(name, nsURI, pkgID, owner, pos, origin); + SymbolOrigin origin, Name compUnit) { + return new BXMLNSSymbol(name, nsURI, pkgID, owner, pos, origin, compUnit); } public static BResourcePathSegmentSymbol createResourcePathSegmentSymbol(Name name, diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangXMLNS.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangXMLNS.java index e4bd93aa09fa..ef70fa4a3d47 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangXMLNS.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangXMLNS.java @@ -32,7 +32,9 @@ public class BLangXMLNS extends BLangNode implements XMLNSDeclarationNode { // BLangNodes public BLangExpression namespaceURI; public BLangIdentifier prefix; - + // Applicable to only module level XMLNS declarations + public BLangIdentifier compUnit; + // Semantic Data public BSymbol symbol; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/declarations/XMLNSTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/declarations/XMLNSTest.java index a676cbf65fa6..036ddc50c421 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/declarations/XMLNSTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/declarations/XMLNSTest.java @@ -27,6 +27,8 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import static org.ballerinalang.test.BAssertUtil.validateError; + /** * Test class for XMLNS Definitions. * @@ -35,10 +37,13 @@ public class XMLNSTest { private CompileResult result; + private CompileResult prefixCompileResult; + @BeforeClass public void setup() { result = BCompileUtil.compile("test-src/declarations/xmlns.bal"); + prefixCompileResult = BCompileUtil.compile("test-src/declarations/xmlnsPrefixProject"); } @Test (dataProvider = "xmlnsDeclFunctions") @@ -65,11 +70,38 @@ public void testXMLNSDefinitionNegative() { BAssertUtil.validateError(negativeResult, i++, "cannot bind prefix 'ns3' to the empty namespace name", 25, 1); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'string', found 'int'", 28, 11); BAssertUtil.validateError(negativeResult, i++, "undefined symbol 'F'", 29, 11); + BAssertUtil.validateError(negativeResult, i++, "expression is not a constant expression", 36, 11); + BAssertUtil.validateError(negativeResult, i++, "expression is not a constant expression", 39, 11); + BAssertUtil.validateError(negativeResult, i++, "undefined symbol 'K'", 39, 11); Assert.assertEquals(negativeResult.getErrorCount(), i); } + @Test (dataProvider = "xmlnsPrefixUsageFunctions") + public void testXMLNSPrefixUsage(String functionName) { + BRunUtil.invoke(prefixCompileResult, functionName); + } + + @DataProvider(name = "xmlnsPrefixUsageFunctions") + private Object[] xmlnsPrefixUsageFunctions() { + return new String[]{ + "testXMLNSUsage", + "testXMLNSUsageInAnotherFile" + }; + } + + @Test + public void testXMLNSPrefixUsageNegative() { + CompileResult compileResult = BCompileUtil.compile("test-src/declarations/xmlnsPrefixNegativeProject"); + int i = 0; + validateError(compileResult, i++, "undefined module 'ns0'", 18, 9); + validateError(compileResult, i++, "undefined module 'ns1'", 19, 9); + validateError(compileResult, i++, "undefined module 'ns2'", 20, 9); + Assert.assertEquals(compileResult.getErrorCount(), i); + } + @AfterClass public void tearDown() { result = null; + prefixCompileResult = null; } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns.bal index bf6e379f86f1..278cd4c27e3f 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns.bal @@ -20,6 +20,7 @@ const C = "http://ballerina.com/c"; const D = C; const E = "http://ballerina.com/e"; const string|int F = "http://ballerina.com/f"; +const I = "http://ballerina.com/i"; xmlns A as ns0; xmlns xml:XMLNS_NAMESPACE_URI as ns1; @@ -28,19 +29,35 @@ xmlns D as ns3; final string f1 = ns1:lmn; final string f2 = ns2:ijk; +final string f3 = ns7:ghi; +final string f4 = ns8:jkl; string s = ns3:pqr; +string s2 = ns7:sty; +string s3 = ns8:xyz; + +type Rec record {| + string a = ns1:baz; +|}; function testXMLNSDeclUsingConstant() { assert(ns0:foo, "{http://ballerina.com/a}foo"); assert(ns1:foo, "{http://www.w3.org/2000/xmlns/}foo"); assert(ns2:bar, "{http://ballerina.com/b}bar"); assert(ns3:bar, "{http://ballerina.com/c}bar"); + Rec rec = {}; + assert(rec.a, "{http://www.w3.org/2000/xmlns/}baz"); + assert(ns7:abc, "{http://ballerina.com/g}abc"); + assert(ns8:def, "{http://ballerina.com/i}def"); } function testXMLNSUsageInModuleVar() { assert(f1, "{http://www.w3.org/2000/xmlns/}lmn"); assert(f2, "{http://ballerina.com/b}ijk"); + assert(f3, "{http://ballerina.com/g}ghi"); + assert(f4, "{http://ballerina.com/i}jkl"); assert(s, "{http://ballerina.com/c}pqr"); + assert(s2, "{http://ballerina.com/g}sty"); + assert(s3, "{http://ballerina.com/i}xyz"); } function testXMLNSDeclStmtUsingConstant() { @@ -54,6 +71,9 @@ function testXMLNSDeclStmtUsingConstant() { assert(ns6:abc, "{http://ballerina.com/f}abc"); } +xmlns "http://ballerina.com/g" as ns7; +xmlns I as ns8; + function assert(anydata actual, anydata expected) { if expected != actual { panic error("expected `" + expected.toString() + "`, but found `" + actual.toString() + "`"); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/Ballerina.toml new file mode 100644 index 000000000000..f8770d8923de --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "testorg" +name = "xmlnsPrefixNegativeProject" +version = "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/a.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/a.bal new file mode 100644 index 000000000000..742b5df46b87 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/a.bal @@ -0,0 +1,27 @@ +// 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. + +xmlns "http://example1.com" as ns0; + +function testXMLNSUsage() { + xmlns "http://example2.com" as ns1; + string _ = ns0:doc; + string _ = ns1:doc; + { + xmlns "http://example3.com" as ns2; + string _ = ns2:foo; + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/b.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/b.bal new file mode 100644 index 000000000000..4be14adf8dc6 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixNegativeProject/b.bal @@ -0,0 +1,21 @@ +// 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 testXMLNSUsageInAnotherFileNegative() { + _ = ns0:doc; + _ = ns1:doc; + _ = ns2:doc; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/Ballerina.toml new file mode 100644 index 000000000000..a8cade67221d --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "testorg" +name = "xmlnsPrefixProject" +version = "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/a.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/a.bal new file mode 100644 index 000000000000..adbbb790033f --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/a.bal @@ -0,0 +1,35 @@ +// 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. + +xmlns "http://exampleA1.com" as ns1; +final string AA = ns1:doc; + +function testXMLNSUsage() { + assert(AA, "{http://exampleA1.com}doc"); + assert(ns1:foo, "{http://exampleA1.com}foo"); + assert(ns3:foz, "{http://exampleA3.com}foz"); + + xmlns "http://exampleA2.com" as ns2; + assert(ns2:foo, "{http://exampleA2.com}foo"); +} + +xmlns "http://exampleA3.com" as ns3; + +function assert(anydata actual, anydata expected) { + if expected != actual { + panic error(string `expected ${expected.toString()}, but found ${actual.toString()};`); + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/b.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/b.bal new file mode 100644 index 000000000000..33a8a2d87998 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlnsPrefixProject/b.bal @@ -0,0 +1,30 @@ +// 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. + +xmlns "http://exampleB1.com" as ns1; + +final string BA = ns1:doc; + +function testXMLNSUsageInAnotherFile() { + assert(BA, "{http://exampleB1.com}doc"); + assert(ns1:foo, "{http://exampleB1.com}foo"); + assert(ns3:foz, "{http://exampleB3.com}foz"); + + xmlns "http://exampleB2.com" as ns2; + assert(ns2:foo, "{http://exampleB2.com}foo"); +} + +xmlns "http://exampleB3.com" as ns3; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns_negative.bal index 769726d67d55..5c6b5d0f5397 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/declarations/xmlns_negative.bal @@ -28,3 +28,13 @@ function foo() { xmlns C as ns4; xmlns F as ns5; } + +type G record {| + int a; +|}; + +const X = G; +xmlns X as ns; + +const Z = K; +xmlns Z as ns6; diff --git a/tests/jballerina-unit-test/src/test/resources/testng.xml b/tests/jballerina-unit-test/src/test/resources/testng.xml index 76c27ca456ae..45455c0569c8 100644 --- a/tests/jballerina-unit-test/src/test/resources/testng.xml +++ b/tests/jballerina-unit-test/src/test/resources/testng.xml @@ -33,6 +33,7 @@ +