Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,23 @@ public BLangNode transform(ImportDeclarationNode importDeclaration) {
List<BLangIdentifier> pkgNameComps = new ArrayList<>();
NodeList<IdentifierToken> names = importDeclaration.moduleName();
Location position = getPosition(importDeclaration);
names.forEach(name -> pkgNameComps.add(this.createIdentifier(getPosition(name), name.text())));
names.forEach(name -> {
// If the token is missing (e.g. a reserved keyword like 'client' was used
// without the '^' escape), the parser synthesises a MISSING identifier and
// places the original keyword as a leading invalid-token minutiae.
// Recover that keyword text so that error messages like
// "cannot resolve module 'ballerinax/client.config'" are correct.
if (name.isMissing()) {
List<Token> leadingInvalid = name.leadingInvalidTokens();
String originalValue = name.text(); // empty string for missing tokens
if (!leadingInvalid.isEmpty()) {
originalValue = leadingInvalid.get(0).text();
}
pkgNameComps.add(this.createIdentifier(getPosition(name), "", originalValue));
} else {
pkgNameComps.add(this.createIdentifier(getPosition(name), name.text()));
}
});

BLangImportPackage importDcl = (BLangImportPackage) TreeBuilder.createImportPackageNode();
importDcl.pos = position;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public String toString() {
public String getQualifiedPackageName() {
String orgName = this.orgName.toString();
String pkgName = String.join(".", pkgNameComps.stream()
.map(id -> id.value)
.map(id -> id.value.isEmpty() && id.originalValue != null ? id.originalValue : id.value)
.toList());

String versionStr = (this.version.value != null) ? this.version.value : "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,18 @@ public void testInvalidAutoImports() {
validateError(result, index++, "unknown type 'CallStackElement'", 51, 30);
assertEquals(result.getErrorCount(), index);
}

@Test(description = "Test diagnostic message for reserved keyword in module import path")
public void testReservedKeywordInModuleImportDiagnostic() {
CompileResult result = BCompileUtil.compile("test-src/imports/ReservedKeywordImportTestProject");
int index = 0;
// 'client' is a reserved keyword; the diagnostic must include it in the module path,
// i.e. "ballerinax/client.config", not "ballerinax/.config" (regression for #44509/#44519).
// Diagnostics are ordered by source position; the import declaration starts at col 1,
// so the module-resolution error precedes the parser errors at col 19.
validateError(result, index++, "cannot resolve module 'ballerinax/client.config as config'", 5, 1);
validateError(result, index++, "invalid token 'client'", 5, 19);
validateError(result, index++, "missing identifier", 5, 25);
assertEquals(result.getErrorCount(), index);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
org = "testorg"
name = "reservedkeywordimport"
version = "1.0.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Test: using a reserved keyword (client) in a module import path without '^' escape
// should produce a diagnostic that includes the keyword in the module path,
// i.e. "cannot resolve module 'ballerinax/client.config as config'"
// NOT "cannot resolve module 'ballerinax/.config as config'"
import ballerinax/client.config;