Add using statement (#391)
This commit is contained in:
Родитель
00fe2f5c4e
Коммит
72afcd17ba
|
@ -14,6 +14,7 @@ import {
|
|||
ScopeNode,
|
||||
IdentifierNode,
|
||||
ADLScriptNode,
|
||||
UsingStatementNode,
|
||||
} from "./types.js";
|
||||
import { reportDuplicateSymbols } from "./util.js";
|
||||
|
||||
|
@ -54,7 +55,7 @@ export function createBinder(): Binder {
|
|||
let currentFile: ADLScriptNode;
|
||||
let parentNode: Node;
|
||||
|
||||
let currentNamespace: NamespaceStatementNode;
|
||||
let currentNamespace: NamespaceStatementNode | ADLScriptNode;
|
||||
|
||||
// Node where locals go.
|
||||
let scope: ScopeNode;
|
||||
|
@ -64,7 +65,7 @@ export function createBinder(): Binder {
|
|||
|
||||
function bindSourceFile(program: Program, sourceFile: ADLScriptNode) {
|
||||
currentFile = sourceFile;
|
||||
currentNamespace = scope = program.globalNamespace;
|
||||
currentNamespace = scope = currentFile;
|
||||
currentFile.locals = new SymbolTable();
|
||||
currentFile.exports = program.globalNamespace.exports!;
|
||||
|
||||
|
@ -88,6 +89,10 @@ export function createBinder(): Binder {
|
|||
break;
|
||||
case SyntaxKind.TemplateParameterDeclaration:
|
||||
bindTemplateParameterDeclaration(node);
|
||||
break;
|
||||
case SyntaxKind.UsingStatement:
|
||||
bindUsingStatement(node);
|
||||
break;
|
||||
}
|
||||
|
||||
const prevParent = parentNode;
|
||||
|
@ -123,6 +128,7 @@ export function createBinder(): Binder {
|
|||
function getContainingSymbolTable() {
|
||||
switch (scope.kind) {
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
case SyntaxKind.ADLScript:
|
||||
return scope.exports!;
|
||||
default:
|
||||
return scope.locals!;
|
||||
|
@ -144,7 +150,8 @@ export function createBinder(): Binder {
|
|||
const existingBinding = (scope as NamespaceStatementNode).exports!.get(statement.name.sv);
|
||||
if (existingBinding && existingBinding.kind === "type") {
|
||||
statement.symbol = existingBinding;
|
||||
statement.locals = (existingBinding.node as NamespaceStatementNode).locals;
|
||||
// locals are never shared.
|
||||
statement.locals = new SymbolTable();
|
||||
statement.exports = (existingBinding.node as NamespaceStatementNode).exports;
|
||||
} else {
|
||||
declareSymbol(getContainingSymbolTable(), statement, statement.name.sv);
|
||||
|
@ -159,12 +166,14 @@ export function createBinder(): Binder {
|
|||
currentFile.namespaces.push(statement);
|
||||
|
||||
if (!statement.statements) {
|
||||
scope = currentNamespace = statement;
|
||||
currentFile.locals = statement.locals!;
|
||||
currentFile.exports = statement.exports!;
|
||||
}
|
||||
}
|
||||
|
||||
function bindUsingStatement(statement: UsingStatementNode) {
|
||||
currentFile.usings.push(statement);
|
||||
}
|
||||
|
||||
function bindOperationStatement(statement: OperationStatementNode) {
|
||||
declareSymbol(getContainingSymbolTable(), statement, statement.id.sv);
|
||||
}
|
||||
|
@ -192,6 +201,8 @@ function hasScope(node: Node): node is ScopeNode {
|
|||
return true;
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
return true;
|
||||
case SyntaxKind.ADLScript:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import {
|
|||
MemberExpressionNode,
|
||||
Sym,
|
||||
ADLScriptNode,
|
||||
IntrinsicType,
|
||||
} from "./types.js";
|
||||
import { reportDuplicateSymbols } from "./util.js";
|
||||
|
||||
|
@ -87,16 +88,33 @@ export function createChecker(program: Program) {
|
|||
let instantiatingTemplate: Node | undefined;
|
||||
let currentSymbolId = 0;
|
||||
const symbolLinks = new Map<number, SymbolLinks>();
|
||||
const errorType: IntrinsicType = { kind: "Intrinsic", name: "ErrorType" };
|
||||
|
||||
// This variable holds on to the model type that is currently
|
||||
// being instantiated in checkModelStatement so that it is
|
||||
// possible to have recursive type references in properties.
|
||||
let pendingModelType: PendingModelInfo | undefined = undefined;
|
||||
|
||||
// for our first act, issue errors for duplicate symbols
|
||||
for (const file of program.sourceFiles) {
|
||||
for (const using of file.usings) {
|
||||
const parentNs = using.parent! as NamespaceStatementNode | ADLScriptNode;
|
||||
const sym = resolveTypeReference(using.name);
|
||||
if (sym.kind === "decorator") throwDiagnostic("Can't use a decorator", using);
|
||||
if (sym.node.kind !== SyntaxKind.NamespaceStatement) {
|
||||
throwDiagnostic("Using must refer to a namespace", using);
|
||||
}
|
||||
|
||||
for (const [name, binding] of sym.node.exports!) {
|
||||
parentNs.locals!.set(name, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reportDuplicateSymbols(program.globalNamespace.exports!);
|
||||
for (const file of program.sourceFiles) {
|
||||
reportDuplicateSymbols(file.locals!);
|
||||
for (const ns of file.namespaces) {
|
||||
reportDuplicateSymbols(ns.locals!);
|
||||
reportDuplicateSymbols(ns.exports!);
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +159,7 @@ export function createChecker(program: Program) {
|
|||
return checkTemplateParameterDeclaration(node);
|
||||
}
|
||||
|
||||
throwDiagnostic("Cannot evaluate " + SyntaxKind[node.kind], node);
|
||||
return errorType;
|
||||
}
|
||||
|
||||
function getTypeName(type: Type): string {
|
||||
|
@ -463,13 +481,13 @@ export function createChecker(program: Program) {
|
|||
let containerSourceFile: ADLScriptNode;
|
||||
|
||||
while (scope) {
|
||||
if ("locals" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.locals!);
|
||||
if ("exports" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.exports!);
|
||||
if (binding) break;
|
||||
}
|
||||
|
||||
if ("exports" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.exports!);
|
||||
if ("locals" in scope) {
|
||||
binding = resolveIdentifierInTable(node, scope.locals!);
|
||||
if (binding) break;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,7 @@ export function createDiagnostic(
|
|||
location = getSourceLocation(target);
|
||||
} catch (err) {
|
||||
locationError = err;
|
||||
location = {
|
||||
file: createSourceFile("", "<unknown location>"),
|
||||
pos: 0,
|
||||
end: 0,
|
||||
};
|
||||
location = createDummySourceLocation();
|
||||
}
|
||||
|
||||
if (typeof message === "string") {
|
||||
|
@ -197,15 +193,22 @@ export function getSourceLocation(target: DiagnosticTarget): SourceLocation {
|
|||
}
|
||||
|
||||
if (target.kind === "decorator") {
|
||||
return {
|
||||
// We currently report all decorators at line 1 of defining .js path.
|
||||
file: createSourceFile("", target.path),
|
||||
pos: 0,
|
||||
end: 0,
|
||||
};
|
||||
return createDummySourceLocation(target.path);
|
||||
}
|
||||
|
||||
return getSourceLocationOfNode("node" in target ? target.node : target);
|
||||
const node = "node" in target ? target.node! : target;
|
||||
if (node.kind === "Intrinsic") {
|
||||
return createDummySourceLocation();
|
||||
}
|
||||
return getSourceLocationOfNode(node);
|
||||
}
|
||||
|
||||
function createDummySourceLocation(loc = "<unknown location>") {
|
||||
return {
|
||||
file: createSourceFile("", loc),
|
||||
pos: 0,
|
||||
end: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function getSourceLocationOfNode(node: Node): SourceLocation {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { checkServerIdentity } from "tls";
|
||||
import { DiagnosticError, throwDiagnostic } from "./diagnostics.js";
|
||||
import { throwDiagnostic } from "./diagnostics.js";
|
||||
import { createScanner, Token } from "./scanner.js";
|
||||
import * as Types from "./types.js";
|
||||
|
||||
|
@ -19,6 +18,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
interfaces: [],
|
||||
models: [],
|
||||
namespaces: [],
|
||||
usings: [],
|
||||
};
|
||||
|
||||
let seenBlocklessNs = false;
|
||||
|
@ -61,6 +61,11 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return parseNamespaceStatement(decorators);
|
||||
case Token.OpKeyword:
|
||||
return parseOperationStatement(decorators);
|
||||
case Token.UsingKeyword:
|
||||
if (decorators.length > 0) {
|
||||
error("Cannot decorate using statements");
|
||||
}
|
||||
return parseUsingStatement();
|
||||
case Token.Semicolon:
|
||||
if (decorators.length > 0) {
|
||||
error("Cannot decorate an empty statement");
|
||||
|
@ -92,6 +97,11 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return ns;
|
||||
case Token.OpKeyword:
|
||||
return parseOperationStatement(decorators);
|
||||
case Token.UsingKeyword:
|
||||
if (decorators.length > 0) {
|
||||
error("Cannot decorate using statements");
|
||||
}
|
||||
return parseUsingStatement();
|
||||
case Token.Semicolon:
|
||||
if (decorators.length > 0) {
|
||||
error("Cannot decorate an empty statement");
|
||||
|
@ -171,6 +181,21 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return outerNs;
|
||||
}
|
||||
|
||||
function parseUsingStatement(): Types.UsingStatementNode {
|
||||
const pos = tokenPos();
|
||||
parseExpected(Token.UsingKeyword);
|
||||
const name = parseIdentifierOrMemberExpression();
|
||||
parseExpected(Token.Semicolon);
|
||||
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.UsingStatement,
|
||||
name,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseOperationStatement(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.OperationStatementNode {
|
||||
|
@ -810,6 +835,8 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
Array.isArray(node.statements)
|
||||
? visitEach(cb, node.statements as Types.Statement[])
|
||||
: visitNode(cb, node.statements);
|
||||
case Types.SyntaxKind.UsingStatement:
|
||||
return visitNode(cb, node.name);
|
||||
case Types.SyntaxKind.IntersectionExpression:
|
||||
return visitEach(cb, node.options);
|
||||
case Types.SyntaxKind.MemberExpression:
|
||||
|
@ -842,7 +869,13 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
case Types.SyntaxKind.NumericLiteral:
|
||||
case Types.SyntaxKind.BooleanLiteral:
|
||||
case Types.SyntaxKind.Identifier:
|
||||
case Types.SyntaxKind.TemplateParameterDeclaration:
|
||||
return;
|
||||
default:
|
||||
// Dummy const to ensure we handle all node types.
|
||||
// If you get an error here, add a case for the new node type
|
||||
// you added..
|
||||
const assertNever: never = node;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ export enum Token {
|
|||
ImportKeyword,
|
||||
ModelKeyword,
|
||||
NamespaceKeyword,
|
||||
UsingKeyword,
|
||||
OpKeyword,
|
||||
ExtendsKeyword,
|
||||
TrueKeyword,
|
||||
|
@ -70,6 +71,7 @@ const keywords = new Map([
|
|||
["import", Token.ImportKeyword],
|
||||
["model", Token.ModelKeyword],
|
||||
["namespace", Token.NamespaceKeyword],
|
||||
["using", Token.UsingKeyword],
|
||||
["op", Token.OpKeyword],
|
||||
["extends", Token.ExtendsKeyword],
|
||||
["true", Token.TrueKeyword],
|
||||
|
|
|
@ -6,7 +6,7 @@ import { MultiKeyMap } from "./checker";
|
|||
*/
|
||||
export interface BaseType {
|
||||
kind: string;
|
||||
node: Node;
|
||||
node?: Node;
|
||||
instantiationParameters?: Array<Type>;
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,18 @@ export type Type =
|
|||
| BooleanLiteralType
|
||||
| ArrayType
|
||||
| TupleType
|
||||
| UnionType;
|
||||
| UnionType
|
||||
| IntrinsicType;
|
||||
|
||||
export interface IntrinsicType extends BaseType {
|
||||
kind: "Intrinsic";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ModelType extends BaseType {
|
||||
kind: "Model";
|
||||
name: string;
|
||||
node: ModelStatementNode | ModelExpressionNode | IntersectionExpressionNode;
|
||||
namespace?: NamespaceType;
|
||||
properties: Map<string, ModelTypeProperty>;
|
||||
baseModels: Array<ModelType>;
|
||||
|
@ -98,11 +105,13 @@ export interface TupleType extends BaseType {
|
|||
|
||||
export interface UnionType extends BaseType {
|
||||
kind: "Union";
|
||||
node: UnionExpressionNode;
|
||||
options: Array<Type>;
|
||||
}
|
||||
|
||||
export interface TemplateParameterType extends BaseType {
|
||||
kind: "TemplateParameter";
|
||||
node: TemplateParameterDeclarationNode;
|
||||
}
|
||||
|
||||
// trying to avoid masking built-in Symbol
|
||||
|
@ -142,6 +151,7 @@ export enum SyntaxKind {
|
|||
DecoratorExpression,
|
||||
MemberExpression,
|
||||
NamespaceStatement,
|
||||
UsingStatement,
|
||||
OperationStatement,
|
||||
ModelStatement,
|
||||
ModelExpression,
|
||||
|
@ -184,12 +194,14 @@ export interface ADLScriptNode extends BaseNode {
|
|||
locals?: SymbolTable;
|
||||
exports?: SymbolTable;
|
||||
namespaces: NamespaceStatementNode[]; // list of namespaces in this file (initialized during binding)
|
||||
usings: UsingStatementNode[];
|
||||
}
|
||||
|
||||
export type Statement =
|
||||
| ImportStatementNode
|
||||
| ModelStatementNode
|
||||
| NamespaceStatementNode
|
||||
| UsingStatementNode
|
||||
| OperationStatementNode;
|
||||
|
||||
export interface DeclarationNode {
|
||||
|
@ -203,7 +215,7 @@ export type Declaration =
|
|||
| OperationStatementNode
|
||||
| TemplateParameterDeclarationNode;
|
||||
|
||||
export type ScopeNode = NamespaceStatementNode | ModelStatementNode;
|
||||
export type ScopeNode = NamespaceStatementNode | ModelStatementNode | ADLScriptNode;
|
||||
|
||||
export interface ImportStatementNode extends BaseNode {
|
||||
kind: SyntaxKind.ImportStatement;
|
||||
|
@ -257,6 +269,11 @@ export interface NamespaceStatementNode extends BaseNode, DeclarationNode {
|
|||
decorators: Array<DecoratorExpressionNode>;
|
||||
}
|
||||
|
||||
export interface UsingStatementNode extends BaseNode {
|
||||
kind: SyntaxKind.UsingStatement;
|
||||
name: IdentifierNode | MemberExpressionNode;
|
||||
}
|
||||
|
||||
export interface OperationStatementNode extends BaseNode, DeclarationNode {
|
||||
kind: SyntaxKind.OperationStatement;
|
||||
id: IdentifierNode;
|
||||
|
|
|
@ -27,6 +27,7 @@ Keyword :
|
|||
`namespace`
|
||||
`op`
|
||||
`extends`
|
||||
`using`
|
||||
|
||||
Identifier :
|
||||
IdentifierName but not Keyword
|
||||
|
@ -188,11 +189,11 @@ Statement :
|
|||
ModelStatement
|
||||
NamespaceStatement
|
||||
OperationStatement
|
||||
UsingStatement
|
||||
`;`
|
||||
|
||||
NamedImports :
|
||||
Identifier
|
||||
NamedImports `,` Identifier
|
||||
UsingStatement :
|
||||
`using` IdentifierOrMemberExpression `;`
|
||||
|
||||
ModelStatement :
|
||||
DecoratorList? `model` Identifier TemplateParameters? ModelHeritage? `{` ModelBody? `}`
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
   <a name="Keyword-lpev-y-g"></a>`` namespace ``
|
||||
   <a name="Keyword-ytodrzn0"></a>`` op ``
|
||||
   <a name="Keyword-rmwjtwx9"></a>`` extends ``
|
||||
   <a name="Keyword-8d2nu2lj"></a>`` using ``
|
||||
|
||||
  <a name="Identifier"></a>*Identifier* **:**
|
||||
   <a name="Identifier-exwdmcdi"></a>*[IdentifierName](#IdentifierName)* **but not** *[Keyword](#Keyword)*
|
||||
|
@ -174,11 +175,11 @@
|
|||
   <a name="Statement-ngbc4m7o"></a>*[ModelStatement](#ModelStatement)*
|
||||
   <a name="Statement-_ljtj5og"></a>*[NamespaceStatement](#NamespaceStatement)*
|
||||
   <a name="Statement-qlyu8ssa"></a>*[OperationStatement](#OperationStatement)*
|
||||
   <a name="Statement-rmuscggd"></a>*[UsingStatement](#UsingStatement)*
|
||||
   <a name="Statement-sg2sawim"></a>`` ; ``
|
||||
|
||||
  <a name="NamedImports"></a>*NamedImports* **:**
|
||||
   <a name="NamedImports-bras6mo_"></a>*[Identifier](#Identifier)*
|
||||
   <a name="NamedImports-arox3l8x"></a>*[NamedImports](#NamedImports)* `` , `` *[Identifier](#Identifier)*
|
||||
  <a name="UsingStatement"></a>*UsingStatement* **:**
|
||||
   <a name="UsingStatement-nfxtjnnc"></a>`` using `` *[IdentifierOrMemberExpression](#IdentifierOrMemberExpression)* `` ; ``
|
||||
|
||||
  <a name="ModelStatement"></a>*ModelStatement* **:**
|
||||
   <a name="ModelStatement-w3a1y-ib"></a>*[DecoratorList](#DecoratorList)*<sub>opt</sub> `` model `` *[Identifier](#Identifier)* *[TemplateParameters](#TemplateParameters)*<sub>opt</sub> *[ModelHeritage](#ModelHeritage)*<sub>opt</sub> `` { `` *[ModelBody](#ModelBody)*<sub>opt</sub> `` } ``
|
||||
|
|
|
@ -157,4 +157,26 @@ describe("blockless namespaces", () => {
|
|||
);
|
||||
await testHost.compile("./");
|
||||
});
|
||||
|
||||
it("binds correctly", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N.M;
|
||||
model A { }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
model X { a: N.M.A }
|
||||
`
|
||||
);
|
||||
try {
|
||||
await testHost.compile("/");
|
||||
} catch (e) {
|
||||
console.log(e.diagnostics);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
import { ok, rejects, strictEqual } from "assert";
|
||||
import { ModelType } from "../../compiler/types";
|
||||
import { createTestHost, TestHost } from "../test-host.js";
|
||||
|
||||
describe("using statements", () => {
|
||||
let testHost: TestHost;
|
||||
|
||||
beforeEach(async () => {
|
||||
testHost = await createTestHost();
|
||||
});
|
||||
|
||||
it("works in global scope", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N;
|
||||
model X { x: int32 }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
using N;
|
||||
@test model Y { ... X }
|
||||
`
|
||||
);
|
||||
|
||||
const { Y } = (await testHost.compile("./")) as {
|
||||
Y: ModelType;
|
||||
};
|
||||
|
||||
strictEqual(Y.properties.size, 1);
|
||||
});
|
||||
|
||||
it("works in namespaces", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N;
|
||||
model X { x: int32 }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
namespace Z;
|
||||
using N;
|
||||
@test model Y { ... X }
|
||||
`
|
||||
);
|
||||
|
||||
const { Y } = (await testHost.compile("./")) as {
|
||||
Y: ModelType;
|
||||
};
|
||||
|
||||
strictEqual(Y.properties.size, 1);
|
||||
});
|
||||
|
||||
it("works with dotted namespaces", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N.M;
|
||||
model X { x: int32 }
|
||||
`
|
||||
);
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
using N.M;
|
||||
@test model Y { ... X }
|
||||
`
|
||||
);
|
||||
|
||||
const { Y } = (await testHost.compile("./")) as {
|
||||
Y: ModelType;
|
||||
};
|
||||
|
||||
strictEqual(Y.properties.size, 1);
|
||||
});
|
||||
|
||||
it("throws errors for duplicate imported usings", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N.M;
|
||||
model X { x: int32 }
|
||||
`
|
||||
);
|
||||
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
using N.M;
|
||||
using N.M;
|
||||
@test model Y { ... X }
|
||||
`
|
||||
);
|
||||
|
||||
await rejects(testHost.compile("./"));
|
||||
});
|
||||
|
||||
it("throws errors for different usings with the same bindings", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N {
|
||||
model A { }
|
||||
}
|
||||
|
||||
namespace M {
|
||||
model A { }
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
using N;
|
||||
using M;
|
||||
`
|
||||
);
|
||||
|
||||
await rejects(testHost.compile("./"));
|
||||
});
|
||||
|
||||
it("resolves 'local' decls over usings", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N;
|
||||
model A { a: string }
|
||||
`
|
||||
);
|
||||
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
using N;
|
||||
model A { a: int32 | string }
|
||||
@test model B { ... A }
|
||||
`
|
||||
);
|
||||
|
||||
const { B } = (await testHost.compile("./")) as {
|
||||
B: ModelType;
|
||||
};
|
||||
strictEqual(B.properties.size, 1);
|
||||
strictEqual(B.properties.get("a")!.type.kind, "Union");
|
||||
});
|
||||
|
||||
it("usings are local to a file", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace N;
|
||||
model A { a: string }
|
||||
`
|
||||
);
|
||||
|
||||
testHost.addAdlFile(
|
||||
"b.adl",
|
||||
`
|
||||
namespace M {
|
||||
using N;
|
||||
}
|
||||
|
||||
namespace M {
|
||||
model X = A;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
await rejects(testHost.compile("./"));
|
||||
});
|
||||
});
|
|
@ -157,6 +157,10 @@ describe("syntax", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
describe("using statements", () => {
|
||||
parseEach(["using A;", "using A.B;", "namespace Foo { using A; }"]);
|
||||
});
|
||||
|
||||
describe("multiple statements", () => {
|
||||
parseEach([
|
||||
`
|
||||
|
|
Загрузка…
Ссылка в новой задаче