From ea5a4c6c8fa2b62a9817090aa981f1c9a8b05920 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 30 Jul 2024 10:28:32 -0700 Subject: [PATCH 1/2] Fix namespace in blockless namespace --- packages/compiler/src/core/binder.ts | 3 ++- packages/compiler/src/core/parser.ts | 2 +- packages/compiler/test/binder.test.ts | 38 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/compiler/src/core/binder.ts b/packages/compiler/src/core/binder.ts index 6f8b9087b53..eb5a1fc91f5 100644 --- a/packages/compiler/src/core/binder.ts +++ b/packages/compiler/src/core/binder.ts @@ -490,8 +490,9 @@ export function createBinder(program: Program): Binder { } function bindNamespaceStatement(statement: NamespaceStatementNode) { + const effectiveScope = fileNamespace ?? scope; // check if there's an existing symbol for this namespace - const existingBinding = (scope as NamespaceStatementNode).symbol.exports!.get(statement.id.sv); + const existingBinding = effectiveScope.symbol.exports!.get(statement.id.sv); if (existingBinding && existingBinding.flags & SymbolFlags.Namespace) { mutate(statement).symbol = existingBinding; // locals are never shared. diff --git a/packages/compiler/src/core/parser.ts b/packages/compiler/src/core/parser.ts index 29176357087..f9e834b03e4 100644 --- a/packages/compiler/src/core/parser.ts +++ b/packages/compiler/src/core/parser.ts @@ -582,7 +582,7 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa case Token.NamespaceKeyword: const ns = parseNamespaceStatement(pos, decorators, docs, directives); - if (!Array.isArray(ns.statements)) { + if (isBlocklessNamespace(ns)) { error({ code: "blockless-namespace-first", messageId: "topLevel", target: ns }); } item = ns; diff --git a/packages/compiler/test/binder.test.ts b/packages/compiler/test/binder.test.ts index afd59ede91f..1ff2fa6ffb7 100644 --- a/packages/compiler/test/binder.test.ts +++ b/packages/compiler/test/binder.test.ts @@ -60,6 +60,44 @@ describe("compiler: binder", () => { }, }); }); + + it("namespace inside blockless namespace with the same name", () => { + const code = ` + namespace A.B; + namespace A.B { + model D { } + } + `; + const script = bindTypeSpec(code); + strictEqual(script.namespaces.length, 4); + assertBindings("root", script.symbol.exports!, { + A: { + declarations: [SyntaxKind.NamespaceStatement], + flags: SymbolFlags.Namespace, + exports: { + B: { + flags: SymbolFlags.Namespace, + exports: { + A: { + declarations: [SyntaxKind.NamespaceStatement], + flags: SymbolFlags.Namespace, + exports: { + B: { + flags: SymbolFlags.Namespace, + exports: { + D: { + flags: SymbolFlags.Model, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + }); it("binds namespaces", () => { const code = ` namespace A { From a1d8ce706530a38c5b872a92d421dacbf5907434 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 30 Jul 2024 10:35:44 -0700 Subject: [PATCH 2/2] Add changelogs --- ...mespace-in-blockless-2024-6-30-10-29-59.md | 14 ++++++++ ...mespace-in-blockless-2024-6-30-10-31-17.md | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .chronus/changes/fix-namespace-in-blockless-2024-6-30-10-29-59.md create mode 100644 .chronus/changes/fix-namespace-in-blockless-2024-6-30-10-31-17.md diff --git a/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-29-59.md b/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-29-59.md new file mode 100644 index 00000000000..74ecf7faa5b --- /dev/null +++ b/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-29-59.md @@ -0,0 +1,14 @@ +--- +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Allow using compact namespace form `Foo.Bar` when inside another namespace + ```tsp + namespace MyOrg.MyProject { + namespace MyModule.MySubmodule { // <-- this used to emit an error + // ... + } + } + ``` diff --git a/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-31-17.md b/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-31-17.md new file mode 100644 index 00000000000..11ee6c6cb9d --- /dev/null +++ b/.chronus/changes/fix-namespace-in-blockless-2024-6-30-10-31-17.md @@ -0,0 +1,33 @@ +--- +changeKind: breaking +packages: + - "@typespec/compiler" +--- + +Fix issue where naming a namespace with the same name as the blockless namespace would merge with it instead of creating a subnamespace like any other name would. + + ```tsp + namespace MyOrg.MyProject; + + namespace MyOrg.MyProject.MyArea { + model A {} + } + + namespace MyArea2 { + model B {} + } + ``` + + Previously model `A` would end-up in namespace `MyOrg.MyProject.MyArea` and model `B` in `MyOrg.MyProject.MyArea2`. With this change model `A` will now be in `MyOrg.MyProject.MyOrg.MyProject.MyArea`. To achieve the previous behavior the above code should be written as: + + ```tsp + namespace MyOrg.MyProject; + + namespace MyArea { + model A {} + } + + namespace MyArea2 { + model B {} + } + ```