Fix referencing symbols found in parent ns of top blockless ns
This commit is contained in:
Родитель
a2a5a91a1f
Коммит
230c66b357
|
@ -1,6 +1,6 @@
|
|||
import { createDiagnostic, Diagnostic, DiagnosticError, formatDiagnostic } from "./diagnostics.js";
|
||||
import { visitChildren } from "./parser.js";
|
||||
import { Program } from "./program.js";
|
||||
import { createProgram, Program } from "./program.js";
|
||||
import {
|
||||
NamespaceStatementNode,
|
||||
ModelStatementNode,
|
||||
|
@ -54,8 +54,9 @@ export interface Binder {
|
|||
export function createBinder(): Binder {
|
||||
let currentFile: ADLScriptNode;
|
||||
let parentNode: Node;
|
||||
|
||||
let currentNamespace: NamespaceStatementNode | ADLScriptNode;
|
||||
let globalNamespace: NamespaceStatementNode;
|
||||
let fileNamespace: NamespaceStatementNode;
|
||||
let currentNamespace: NamespaceStatementNode;
|
||||
|
||||
// Node where locals go.
|
||||
let scope: ScopeNode;
|
||||
|
@ -64,12 +65,12 @@ export function createBinder(): Binder {
|
|||
};
|
||||
|
||||
function bindSourceFile(program: Program, sourceFile: ADLScriptNode) {
|
||||
globalNamespace = program.globalNamespace;
|
||||
fileNamespace = globalNamespace;
|
||||
currentFile = sourceFile;
|
||||
currentNamespace = scope = currentFile;
|
||||
currentFile.locals = new SymbolTable();
|
||||
currentFile.exports = program.globalNamespace.exports!;
|
||||
|
||||
currentNamespace = scope = globalNamespace;
|
||||
bindNode(sourceFile);
|
||||
currentFile.inScopeNamespaces.push(globalNamespace);
|
||||
}
|
||||
|
||||
function bindNode(node: Node) {
|
||||
|
@ -110,9 +111,7 @@ export function createBinder(): Binder {
|
|||
visitChildren(node, bindNode);
|
||||
|
||||
if (node.kind !== SyntaxKind.NamespaceStatement) {
|
||||
// we've finished binding all the children, so make sure
|
||||
// there are no duplicates.
|
||||
reportDuplicateSymbols(node.locals!);
|
||||
reportDuplicateSymbols(node.locals!)
|
||||
}
|
||||
|
||||
scope = prevScope;
|
||||
|
@ -128,8 +127,9 @@ export function createBinder(): Binder {
|
|||
function getContainingSymbolTable() {
|
||||
switch (scope.kind) {
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
case SyntaxKind.ADLScript:
|
||||
return scope.exports!;
|
||||
case SyntaxKind.ADLScript:
|
||||
return fileNamespace.exports!;
|
||||
default:
|
||||
return scope.locals!;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ export function createBinder(): Binder {
|
|||
|
||||
function bindNamespaceStatement(statement: NamespaceStatementNode) {
|
||||
// check if there's an existing symbol for this namespace
|
||||
const existingBinding = (scope as NamespaceStatementNode).exports!.get(statement.name.sv);
|
||||
const existingBinding = currentNamespace.exports!.get(statement.name.sv);
|
||||
if (existingBinding && existingBinding.kind === "type") {
|
||||
statement.symbol = existingBinding;
|
||||
// locals are never shared.
|
||||
|
@ -166,10 +166,13 @@ export function createBinder(): Binder {
|
|||
currentFile.namespaces.push(statement);
|
||||
|
||||
if (statement.statements === undefined) {
|
||||
currentFile.exports = statement.exports!;
|
||||
scope = currentNamespace = statement;
|
||||
} else if (!Array.isArray(statement.statements)) {
|
||||
scope = currentNamespace = statement;
|
||||
fileNamespace = statement;
|
||||
let current: ADLScriptNode | NamespaceStatementNode = statement;
|
||||
while (current.kind !== SyntaxKind.ADLScript) {
|
||||
currentFile.inScopeNamespaces.push(current);
|
||||
current = current.parent as ADLScriptNode | NamespaceStatementNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,6 +195,11 @@ export function createBinder(): Binder {
|
|||
}
|
||||
|
||||
node.namespaceSymbol = scope.symbol;
|
||||
} else if (scope.kind === SyntaxKind.ADLScript) {
|
||||
if (node.kind === SyntaxKind.TemplateParameterDeclaration) {
|
||||
throw new Error("Attempted to declare template parameter in global scope");
|
||||
}
|
||||
node.namespaceSymbol = fileNamespace.symbol;
|
||||
}
|
||||
|
||||
table.set(name, symbol);
|
||||
|
@ -203,7 +211,7 @@ function hasScope(node: Node): node is ScopeNode {
|
|||
case SyntaxKind.ModelStatement:
|
||||
return true;
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
return Array.isArray(node.statements);
|
||||
return node.statements !== undefined;
|
||||
case SyntaxKind.ADLScript:
|
||||
return true;
|
||||
default:
|
||||
|
|
|
@ -478,31 +478,36 @@ export function createChecker(program: Program) {
|
|||
function resolveIdentifier(node: IdentifierNode) {
|
||||
let scope: Node | undefined = node.parent;
|
||||
let binding;
|
||||
let containerSourceFile: ADLScriptNode;
|
||||
|
||||
while (scope) {
|
||||
while (scope && scope.kind !== SyntaxKind.ADLScript) {
|
||||
if ("exports" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.exports!);
|
||||
if (binding) break;
|
||||
if (binding) return binding;
|
||||
}
|
||||
|
||||
if ("locals" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.locals!);
|
||||
if (binding) break;
|
||||
if (binding) return binding;
|
||||
}
|
||||
|
||||
scope = scope.parent;
|
||||
}
|
||||
|
||||
if (!binding) {
|
||||
binding = resolveIdentifierInTable(node, program.globalNamespace.exports!);
|
||||
if (!binding && scope && scope.kind === SyntaxKind.ADLScript) {
|
||||
// check any blockless namespace decls and global scope
|
||||
for (const ns of scope.inScopeNamespaces) {
|
||||
binding = resolveIdentifierInTable(node, ns.exports!);
|
||||
if (binding) return binding;
|
||||
}
|
||||
|
||||
// check "global scope" usings
|
||||
binding = resolveIdentifierInTable(node, scope.locals);
|
||||
if (binding) return binding;
|
||||
}
|
||||
|
||||
if (!binding) {
|
||||
throwDiagnostic("Unknown identifier " + node.sv, node);
|
||||
}
|
||||
|
||||
throwDiagnostic("Unknown identifier " + node.sv, node);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
function resolveTypeReference(node: ReferenceExpression): DecoratorSymbol | TypeSymbol {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { SymbolTable } from "./binder.js";
|
||||
import { throwDiagnostic } from "./diagnostics.js";
|
||||
import { createScanner, Token } from "./scanner.js";
|
||||
import * as Types from "./types.js";
|
||||
|
@ -19,6 +20,8 @@ export function parse(code: string | Types.SourceFile) {
|
|||
models: [],
|
||||
namespaces: [],
|
||||
usings: [],
|
||||
locals: new SymbolTable(),
|
||||
inScopeNamespaces: []
|
||||
};
|
||||
|
||||
let seenBlocklessNs = false;
|
||||
|
|
|
@ -191,9 +191,9 @@ export interface ADLScriptNode extends BaseNode {
|
|||
models: Array<ModelType>;
|
||||
file: SourceFile;
|
||||
interfaces: Array<NamespaceType>;
|
||||
locals?: SymbolTable;
|
||||
exports?: SymbolTable;
|
||||
inScopeNamespaces: NamespaceStatementNode[]; // namespaces that declarations in this file belong to
|
||||
namespaces: NamespaceStatementNode[]; // list of namespaces in this file (initialized during binding)
|
||||
locals: SymbolTable;
|
||||
usings: UsingStatementNode[];
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,42 @@ describe("namespaces with blocks", () => {
|
|||
};
|
||||
strictEqual(Z.properties.size, 2, "has two properties");
|
||||
});
|
||||
|
||||
it("can see things in outer scope same file", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
model A { }
|
||||
namespace N { model B extends A { } }
|
||||
`
|
||||
);
|
||||
await testHost.compile("./");
|
||||
});
|
||||
|
||||
it("can see things in outer scope cross file", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
model A { }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
model B extends A { }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"c.adl",
|
||||
`
|
||||
model C { }
|
||||
namespace foo {
|
||||
op foo(a: A, b: B): C;
|
||||
}
|
||||
`
|
||||
);
|
||||
await testHost.compile("./");
|
||||
})
|
||||
});
|
||||
|
||||
describe("blockless namespaces", () => {
|
||||
|
@ -176,6 +212,36 @@ describe("blockless namespaces", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("does lookup correctly with nested namespaces", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace Repro;
|
||||
model Yo {
|
||||
}
|
||||
model Hey {
|
||||
wat: Yo;
|
||||
}
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
namespace Repro.Uhoh;
|
||||
model SayYo {
|
||||
yo: Hey;
|
||||
wat: Yo;
|
||||
}
|
||||
`
|
||||
);
|
||||
try {
|
||||
await testHost.compile("./");
|
||||
} catch (e) {
|
||||
console.log(e.diagnostics);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it("binds correctly", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
|
@ -251,6 +317,7 @@ describe("blockless namespaces", () => {
|
|||
};
|
||||
|
||||
ok(M.namespace);
|
||||
ok(O.namespace);
|
||||
strictEqual(O.namespace, M);
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче