diff --git a/src/ast.ts b/src/ast.ts index 155b1603fc..1a922b7528 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -32,6 +32,12 @@ import { CharCode } from "./util"; +import { + DiagnosticEmitter, + DiagnosticMessage, + DiagnosticCode +} from "./diagnostics"; + /** Indicates the kind of a node. */ export enum NodeKind { @@ -1797,6 +1803,8 @@ export abstract class VariableLikeDeclarationStatement extends DeclarationStatem export class BlockStatement extends Statement { /** Contained statements. */ statements: Statement[]; + /** Scoped names. */ + _scope: string[]; } /** Represents a `break` statement. */ @@ -1908,6 +1916,8 @@ export class ForStatement extends Statement { incrementor: Expression | null; /** Statement being looped over. */ statement: Statement; + /** Scoped names. */ + _scope: string[]; } /** Represents a `for..of` statement. */ @@ -1918,6 +1928,8 @@ export class ForOfStatement extends Statement { iterable: Expression; /** Statement being looped over. */ statement: Statement; + /** Scoped names. */ + _scope: string[]; } /** Indicates the kind of an array function. */ @@ -1940,6 +1952,8 @@ export class FunctionDeclaration extends DeclarationStatement { body: Statement | null; /** Arrow function kind, if applicable. */ arrowKind: ArrowKind; + /** Scoped names. */ + _scope: string[]; get isGeneric(): bool { var typeParameters = this.typeParameters; @@ -1948,7 +1962,7 @@ export class FunctionDeclaration extends DeclarationStatement { /** Clones this function declaration. */ clone(): FunctionDeclaration { - return Node.createFunctionDeclaration( + var clonedDeclaration = Node.createFunctionDeclaration( this.name, this.typeParameters, this.signature, @@ -1958,6 +1972,8 @@ export class FunctionDeclaration extends DeclarationStatement { this.arrowKind, this.range ); + clonedDeclaration._scope = this._scope; + return clonedDeclaration; } } @@ -2001,6 +2017,8 @@ export class MethodDeclaration extends FunctionDeclaration { export class NamespaceDeclaration extends DeclarationStatement { /** Array of namespace members. */ members: Statement[]; + /** Scoped names. */ + _scope: string[]; } /** Represents a `return` statement. */ @@ -2023,6 +2041,8 @@ export class SwitchStatement extends Statement { condition: Expression; /** Contained cases. */ cases: SwitchCase[]; + /** Scoped names. */ + _scope: string[]; } /** Represents a `throw` statement. */ @@ -2041,6 +2061,10 @@ export class TryStatement extends Statement { catchStatements: Statement[] | null; /** Statements being executed afterwards, if a `finally` clause is present. */ finallyStatements: Statement[] | null; + /** Scoped names in catch clause. */ + _catchScope: string[]; + /** Scoped names in finally clause. */ + _finallyScope: string[]; } /** Represents a `type` declaration. */ @@ -2102,3 +2126,225 @@ export function isTypeOmitted(type: TypeNode): bool { } return false; } + +/** Analyzes function body scopes. */ +export class ScopeAnalyzer extends DiagnosticEmitter { + + /** Function being evaluated. */ + declaration: FunctionDeclaration; + /** Function scope. */ + functionScope: Map = new Map(); + /** Block scope stack. */ + blockScopes: Map[] = []; + + /** Evaluates the specified function. */ + static analyze(declaration: FunctionDeclaration, diagnostics: DiagnosticMessage[]): void { + var instance = new ScopeAnalyzer(declaration, diagnostics); + var body = declaration.body; + if (body) instance.visit(body); + declaration._scope = Map_keys(instance.functionScope); + assert(instance.blockScopes.length == 0); + } + + constructor(declaration: FunctionDeclaration, diagnostics: DiagnosticMessage[]) { + super(diagnostics); + this.declaration = declaration; + var parameters = declaration.signature.parameters; + var functionScope = this.functionScope; + for (let i = 0, k = parameters.length; i < k; ++i) { + let identifier = parameters[i].name; + functionScope.set(identifier.text, identifier); + } + } + + pushScope(scope: Map = new Map()): void { + this.blockScopes.push(scope); + } + + popScope(): string[] { + var names = new Set(); + var functionScope = this.functionScope; + // TODO: for(let key of functionScope.keys()) { + for (let _keys = Map_keys(functionScope), i = 0, k = _keys.length; i < k; ++i) { + names.add(_keys[i]); + } + var blockScopes = this.blockScopes; + var blockDepth = assert(blockScopes.length); + for (let i = 0; i < blockDepth; ++i) { + let blockScope = blockScopes[i]; + // TODO: for (let key of blockScope.keys()) { + for (let _keys = Map_keys(blockScope), j = 0, k = _keys.length; j < k; ++j) { + names.add(_keys[j]); + } + } + blockScopes.pop(); + return Set_values(names); + } + + visit(node: Node): void { + switch (node.kind) { + case NodeKind.BLOCK: { + let blockStatement = node; + this.pushScope(); + let statements = blockStatement.statements; + for (let i = 0, k = statements.length; i < k; ++i) { + this.visit(statements[i]); + } + blockStatement._scope = this.popScope(); + break; + } + case NodeKind.BREAK: break; + case NodeKind.CONTINUE: break; + case NodeKind.DO: { + this.visit((node).statement); + break; + } + case NodeKind.EMPTY: break; + case NodeKind.EXPRESSION: { + let expression = (node).expression; + if (expression.kind == NodeKind.FUNCTION) { + this.visit((expression).declaration); + } + break; + } + case NodeKind.FOR: { + let forStatement = node; + this.pushScope(); + let initializer = forStatement.initializer; + if (initializer) this.visit(initializer); + this.visit(forStatement.statement); + forStatement._scope = this.popScope(); + break; + } + case NodeKind.FOROF: { + let forOfStatement = node; + this.pushScope(); + let variable = forOfStatement.variable; + if (variable) this.visit(variable); + this.visit(forOfStatement.statement); + forOfStatement._scope = this.popScope(); + break; + } + case NodeKind.IF: { + let ifStatement = node; + this.visit(ifStatement.ifTrue); + let ifFalse = ifStatement.ifFalse; + if (ifFalse) this.visit(ifFalse); + break; + } + case NodeKind.RETURN: break; + case NodeKind.SWITCH: { + let switchStatement = node; + this.pushScope(); + let cases = switchStatement.cases; + for (let i = 0, k = cases.length; i < k; ++i) { + this.visit(cases[i]); + } + switchStatement._scope = this.popScope(); + break; + } + case NodeKind.THROW: break; + case NodeKind.TRY: { + let tryStatement = node; + let statements = tryStatement.statements; + for (let i = 0, k = statements.length; i < k; ++i) { + this.visit(statements[i]); + } + let catchStatements = tryStatement.catchStatements; + if (catchStatements) { + let scope = new Map(); + this.pushScope(scope); + let catchVariable = tryStatement.catchVariable; + if (catchVariable) { + scope.set(catchVariable.text, catchVariable); + } + for (let i = 0, k = catchStatements.length; i < k; ++i) { + this.visit(catchStatements[i]); + } + tryStatement._catchScope = this.popScope(); + } + let finallyStatements = tryStatement.finallyStatements; + if (finallyStatements) { + this.pushScope(); + for (let i = 0, k = finallyStatements.length; i < k; ++i) { + this.visit(finallyStatements[i]); + } + tryStatement._finallyScope = this.popScope(); + } + break; + } + case NodeKind.VARIABLE: { + let declarations = (node).declarations; + for (let i = 0, k = declarations.length; i < k; ++i) { + this.visit(declarations[i]); + } + break; + } + case NodeKind.VOID: break; + case NodeKind.WHILE: { + this.visit((node).statement); + break; + } + case NodeKind.FUNCTIONDECLARATION: { + let declaration = node; + // We are only interested in the name here, not the contents + let identifier = declaration.name; + if (identifier.text.length) this.check(identifier, true); + break; + } + case NodeKind.VARIABLEDECLARATION: { + let declaration = node; + this.check(declaration.name, !(declaration.flags & (CommonFlags.LET | CommonFlags.CONST))); + break; + } + case NodeKind.SWITCHCASE: { + let statements = (node).statements; + for (let i = 0, k = statements.length; i < k; ++i) { + this.visit(statements[i]); + } + break; + } + default: assert(false); + } + } + + check(identifier: IdentifierExpression, isVar: bool): void { + var name = identifier.text; + var blockScopes = this.blockScopes; + var blockDepth = assert(blockScopes.length); + var blockScope = blockScopes[blockDepth - 1]; + if (blockScope.has(name)) { + let existing = assert(blockScope.get(name)); + this.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + identifier.range, existing.range, name + ); + } else if (isVar || blockDepth == 1) { + let functionScope = this.functionScope; + if (functionScope.has(name)) { + let existing = assert(functionScope.get(name)); + this.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + identifier.range, existing.range, name + ); + } else { + if (isVar) { + let firstScope = blockScopes[0]; + if (firstScope.has(name)) { + let existing = assert(firstScope.get(name)); + this.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + identifier.range, existing.range, name + ); + } else { + functionScope.set(identifier.text, identifier); + } + } else { + blockScope.set(identifier.text, identifier); + } + } + } else { + blockScope.set(identifier.text, identifier); + } + } +} diff --git a/src/compiler.ts b/src/compiler.ts index 495f670822..0688116394 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -166,6 +166,7 @@ import { NamedTypeNode, + ScopeAnalyzer, findDecorator, isTypeOmitted } from "./ast"; @@ -1273,6 +1274,7 @@ export class Compiler extends DiagnosticEmitter { // concrete function if (bodyNode) { + ScopeAnalyzer.analyze(instance.declaration, this.diagnostics); // must not be ambient if (instance.is(CommonFlags.AMBIENT)) { diff --git a/src/extra/ast.ts b/src/extra/ast.ts index ad37ee5269..1a9a42cf80 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -86,7 +86,8 @@ import { SwitchCase, DeclarationStatement, - isTypeOmitted + isTypeOmitted, + ScopeAnalyzer } from "../ast"; import { @@ -102,12 +103,17 @@ import { CommonFlags } from "../common"; +import { + DiagnosticMessage, + DiagnosticEmitter +} from "../diagnostics"; + /** An AST builder. */ -export class ASTBuilder { +export class ASTBuilder extends DiagnosticEmitter { /** Rebuilds the textual source from the specified AST, as far as possible. */ - static build(node: Node): string { - var builder = new ASTBuilder(); + static build(node: Node, diagnostics: DiagnosticMessage[]): string { + var builder = new ASTBuilder(diagnostics); builder.visitNode(node); return builder.finish(); } @@ -115,6 +121,10 @@ export class ASTBuilder { private sb: string[] = []; private indentLevel: i32 = 0; + constructor(diagnostics: DiagnosticMessage[]) { + super(diagnostics); + } + visitNode(node: Node): void { switch (node.kind) { case NodeKind.SOURCE: { @@ -851,7 +861,9 @@ export class ASTBuilder { var statements = node.statements; var numStatements = statements.length; if (numStatements) { - sb.push("{\n"); + sb.push("{"); + this.serializeScope(node._scope); + sb.push("\n"); let indentLevel = ++this.indentLevel; for (let i = 0; i < numStatements; ++i) { indent(sb, indentLevel); @@ -860,7 +872,9 @@ export class ASTBuilder { indent(sb, --this.indentLevel); sb.push("}"); } else { - sb.push("{}"); + sb.push("{"); + this.serializeScope(node._scope); + sb.push("}"); } } @@ -1130,6 +1144,9 @@ export class ASTBuilder { sb.push(";"); } sb.push(") "); + if (this.serializeScope(node._scope)) { + sb.push(" "); + } this.visitNode(node.statement); } @@ -1140,6 +1157,9 @@ export class ASTBuilder { sb.push(" of "); this.visitNode(node.iterable); sb.push(") "); + if (this.serializeScope(node._scope)) { + sb.push(" "); + } this.visitNode(node.statement); } @@ -1206,6 +1226,7 @@ export class ASTBuilder { } } var body = node.body; + if (body != null) ScopeAnalyzer.analyze(node, this.diagnostics); var returnType = signature.returnType; if (node.arrowKind) { if (body) { @@ -1220,6 +1241,9 @@ export class ASTBuilder { } } sb.push(" => "); + if (this.serializeScope(node._scope)) { + sb.push(" "); + } this.visitNode(body); } else { assert(!isTypeOmitted(returnType)); @@ -1238,6 +1262,9 @@ export class ASTBuilder { } if (body) { sb.push(" "); + if (this.serializeScope(node._scope)) { + sb.push(" "); + } this.visitNode(body); } } @@ -1439,15 +1466,18 @@ export class ASTBuilder { var sb = this.sb; sb.push("switch ("); this.visitNode(node.condition); - sb.push(") {\n"); + sb.push(") "); + if (this.serializeScope(node._scope)) { + sb.push(" "); + } + sb.push("{\n"); var indentLevel = ++this.indentLevel; var cases = node.cases; for (let i = 0, k = cases.length; i < k; ++i) { indent(sb, indentLevel); this.visitSwitchCase(cases[i]); - sb.push("\n"); } - --this.indentLevel; + indent(sb, --this.indentLevel); sb.push("}"); } @@ -1488,7 +1518,7 @@ export class ASTBuilder { this.visitNodeAndTerminate(finallyStatements[i]); } } - indent(sb, indentLevel - 1); + indent(sb, --this.indentLevel); sb.push("}"); } @@ -1649,6 +1679,14 @@ export class ASTBuilder { } } + serializeScope(scope: string[] | null): bool { + if (scope != null && scope.length > 0) { + this.sb.push("/* {" + scope.join(",") + "} */"); + return true; + } + return false; + } + finish(): string { var ret = this.sb.join(""); this.sb = []; diff --git a/src/parser.ts b/src/parser.ts index 0c0ac7d9c0..c67261e4ba 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1439,7 +1439,7 @@ export class Parser extends DiagnosticEmitter { tn.range(signatureStart, tn.pos) ); - var body: Statement | null = null; + var body: BlockStatement | null = null; if (tn.skip(Token.OPENBRACE)) { if (flags & CommonFlags.AMBIENT) { this.error( @@ -1448,7 +1448,7 @@ export class Parser extends DiagnosticEmitter { ); // recoverable } - body = this.parseBlockStatement(tn, false); + body = this.parseBlockStatement(tn, /* topLevel */ false); if (!body) return null; } else if (!(flags & CommonFlags.AMBIENT)) { this.error( @@ -1554,7 +1554,7 @@ export class Parser extends DiagnosticEmitter { var body: Statement | null = null; if (arrowKind) { if (tn.skip(Token.OPENBRACE)) { - body = this.parseBlockStatement(tn, false); + body = this.parseBlockStatement(tn, /* topLevel */ false); } else { let bodyExpression = this.parseExpression(tn, Precedence.COMMA + 1); if (bodyExpression) body = Node.createExpressionStatement(bodyExpression); @@ -1567,7 +1567,7 @@ export class Parser extends DiagnosticEmitter { ); return null; } - body = this.parseBlockStatement(tn, false); + body = this.parseBlockStatement(tn, /* topLevel */ false); } if (!body) return null; diff --git a/tests/parser.js b/tests/parser.js index c17aa82862..4650b965e6 100644 --- a/tests/parser.js +++ b/tests/parser.js @@ -67,7 +67,7 @@ tests.forEach(filename => { var parser = program.parser; var sourceText = fs.readFileSync(basedir + "/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n"); parser.parseFile(sourceText, filename, true); - var serializedSourceText = ASTBuilder.build(program.sources[0]); + var serializedSourceText = ASTBuilder.build(program.sources[0], program.diagnostics); var actual = serializedSourceText + parser.diagnostics.map(diagnostic => "// " + diagnostic +"\n").join(""); var fixture = filename + ".fixture.ts"; diff --git a/tests/parser/arrow-functions.ts.fixture.ts b/tests/parser/arrow-functions.ts.fixture.ts index 3f049b2a6a..b1c565df03 100644 --- a/tests/parser/arrow-functions.ts.fixture.ts +++ b/tests/parser/arrow-functions.ts.fixture.ts @@ -1,8 +1,8 @@ -(x): i32 => x; -(x: i32) => x; -(x?) => x; -(x?, y?) => x; -(x?: i32) => x; -x => x; +(x): i32 => /* {x} */ x; +(x: i32) => /* {x} */ x; +(x?) => /* {x} */ x; +(x?, y?) => /* {x,y} */ x; +(x?: i32) => /* {x} */ x; +x => /* {x} */ x; (b ? x : y); (b ? f : g)(); diff --git a/tests/parser/class.ts.fixture.ts b/tests/parser/class.ts.fixture.ts index a76ddb7202..7817a5c9c0 100644 --- a/tests/parser/class.ts.fixture.ts +++ b/tests/parser/class.ts.fixture.ts @@ -3,7 +3,7 @@ export class Valid { instanceFunction(): void {} static staticFunction(): void {} get instanceGetter(): i32 {} - static set staticSetter(v: i32) {} + static set staticSetter(v: i32) /* {v} */ {/* {v} */} instanceField: i32; static staticField: i32; static void: i32; @@ -12,7 +12,7 @@ export class Valid { export class Invalid { constructor() {} instanceFunction() {} - get instanceGetter(a: i32) {} + get instanceGetter(a: i32) /* {a} */ {/* {a} */} set instanceSetter() {} } // ERROR 1092: "Type parameters cannot appear on a constructor declaration." in class.ts:15:14 diff --git a/tests/parser/constructor.ts.fixture.ts b/tests/parser/constructor.ts.fixture.ts index 8b80d9e70f..7b91a23db6 100644 --- a/tests/parser/constructor.ts.fixture.ts +++ b/tests/parser/constructor.ts.fixture.ts @@ -1,8 +1,8 @@ class MyClass { constructor() {} - constructor(a: i32) {} - constructor(a: i32, b: i32) {} + constructor(a: i32) /* {a} */ {/* {a} */} + constructor(a: i32, b: i32) /* {a,b} */ {/* {a,b} */} } class MyClassImplicit { - constructor(public a: i32, private readonly b?: i32 = 2, c?: i32 = 3) {} + constructor(public a: i32, private readonly b?: i32 = 2, c?: i32 = 3) /* {a,b,c} */ {/* {a,b,c} */} } diff --git a/tests/parser/definite-assignment-assertion.ts b/tests/parser/definite-assignment-assertion.ts index 05b3cd4f62..69e1298507 100644 --- a/tests/parser/definite-assignment-assertion.ts +++ b/tests/parser/definite-assignment-assertion.ts @@ -1,9 +1,9 @@ class C { x!: i32; - x!: i32 = 0; // invalid - static x!: i32; // invlaid + y!: i32 = 0; // invalid + static z!: i32; // invlaid } function f(): void { let x!: i32; - let x!: i32 = 0; // invalid + let y!: i32 = 0; // invalid } diff --git a/tests/parser/definite-assignment-assertion.ts.fixture.ts b/tests/parser/definite-assignment-assertion.ts.fixture.ts index 996e11f598..d99d32c52e 100644 --- a/tests/parser/definite-assignment-assertion.ts.fixture.ts +++ b/tests/parser/definite-assignment-assertion.ts.fixture.ts @@ -1,11 +1,11 @@ class C { x!: i32; - x!: i32 = 0; - static x!: i32; + y!: i32 = 0; + static z!: i32; } -function f(): void { +function f(): void {/* {x,y} */ let x!: i32; - let x!: i32 = 0; + let y!: i32 = 0; } // ERROR 1255: "A definite assignment assertion '!' is not permitted in this context." in definite-assignment-assertion.ts:3:3 // ERROR 1255: "A definite assignment assertion '!' is not permitted in this context." in definite-assignment-assertion.ts:4:3 diff --git a/tests/parser/for.ts b/tests/parser/for.ts index 3e10da956f..b1a33b60b1 100644 --- a/tests/parser/for.ts +++ b/tests/parser/for.ts @@ -1,6 +1,9 @@ for (var i: i32 = 0; i < 10; ++i) { ; } +for (let i = 0; i < 10; ++i) { + ; +} for (i = 0; i < 10; ++i) { ; } diff --git a/tests/parser/for.ts.fixture.ts b/tests/parser/for.ts.fixture.ts index 3e10da956f..b1a33b60b1 100644 --- a/tests/parser/for.ts.fixture.ts +++ b/tests/parser/for.ts.fixture.ts @@ -1,6 +1,9 @@ for (var i: i32 = 0; i < 10; ++i) { ; } +for (let i = 0; i < 10; ++i) { + ; +} for (i = 0; i < 10; ++i) { ; } diff --git a/tests/parser/function-expression.ts.fixture.ts b/tests/parser/function-expression.ts.fixture.ts index bbd37e9918..6efdbf32d3 100644 --- a/tests/parser/function-expression.ts.fixture.ts +++ b/tests/parser/function-expression.ts.fixture.ts @@ -4,13 +4,13 @@ var a = function(): void { var b = function someName(): void { ; }; -var c = function(a: i32, b: i32): i32 { +var c = function(a: i32, b: i32): i32 /* {a,b} */ {/* {a,b} */ ; }; var d = (): void => { ; }; -var e = (a: i32, b: i32): i32 => { +var e = (a: i32, b: i32): i32 => /* {a,b} */ {/* {a,b} */ ; }; -var f = (a: i32): i32 => a; +var f = (a: i32): i32 => /* {a} */ a; diff --git a/tests/parser/function.ts.fixture.ts b/tests/parser/function.ts.fixture.ts index 277b48a93f..f09bc0b75f 100644 --- a/tests/parser/function.ts.fixture.ts +++ b/tests/parser/function.ts.fixture.ts @@ -1,10 +1,10 @@ function simple(): void {} -function typeparams(a?: V | null = null): void {} +function typeparams(a?: V | null = null): void /* {a} */ {/* {a} */} @decorator() function withdecorator(): void {} function withthis(this: i32): i32 { return this; } -function withthisp(this: i32, a: f32, b: f64): i32 { +function withthisp(this: i32, a: f32, b: f64): i32 /* {a,b} */ {/* {a,b} */ return this; } diff --git a/tests/parser/parameter-order.ts.fixture.ts b/tests/parser/parameter-order.ts.fixture.ts index 587b2069da..352441d753 100644 --- a/tests/parser/parameter-order.ts.fixture.ts +++ b/tests/parser/parameter-order.ts.fixture.ts @@ -1,8 +1,8 @@ -function restValid(a: i32, ...b: Array): void {} -function optionalValid(a: i32, b?: i32): void {} -function restParameterMustBeLast(...a: Array, b: i32): void {} -function optionalCannotPrecedeRequired(a?: i32, b: i32): void {} -function optionalWithInitializerCannotPrecedeRequired(a?: i32 = 1, b: i32): void {} +function restValid(a: i32, ...b: Array): void /* {a,b} */ {/* {a,b} */} +function optionalValid(a: i32, b?: i32): void /* {a,b} */ {/* {a,b} */} +function restParameterMustBeLast(...a: Array, b: i32): void /* {a,b} */ {/* {a,b} */} +function optionalCannotPrecedeRequired(a?: i32, b: i32): void /* {a,b} */ {/* {a,b} */} +function optionalWithInitializerCannotPrecedeRequired(a?: i32 = 1, b: i32): void /* {a,b} */ {/* {a,b} */} // ERROR 1014: "A rest parameter must be last in a parameter list." in parameter-order.ts:5:37 // ERROR 1016: "A required parameter cannot follow an optional parameter." in parameter-order.ts:8:49 // ERROR 1016: "A required parameter cannot follow an optional parameter." in parameter-order.ts:11:67 diff --git a/tests/parser/reserved-keywords.ts.fixture.ts b/tests/parser/reserved-keywords.ts.fixture.ts index 0193884db8..f77e63874d 100644 --- a/tests/parser/reserved-keywords.ts.fixture.ts +++ b/tests/parser/reserved-keywords.ts.fixture.ts @@ -1,7 +1,7 @@ -function alsoIdentifier(readonly: i32): void {} +function alsoIdentifier(readonly: i32): void /* {readonly} */ {/* {readonly} */} class AClass { - constructor(readonly: i32) {} - constructor(readonly readonly: i32) {} + constructor(readonly: i32) /* {readonly} */ {/* {readonly} */} + constructor(readonly readonly: i32) /* {readonly} */ {/* {readonly} */} } type type = i32; var type: i32; diff --git a/tests/parser/scope.ts b/tests/parser/scope.ts new file mode 100644 index 0000000000..1ace8eeb87 --- /dev/null +++ b/tests/parser/scope.ts @@ -0,0 +1,58 @@ +function foo(a: i32): void { + var b = 0; + let c = 0; + { + var d = 0; + let e = 0; + } + for (var f = 0; f < 10; ++f) { + var g = 0; + let h = 0; + } + for (let i = 0; i < 10; ++i) { + var j = 0; + let k = 0; + } + for (var l of something) { + var m = 0; + let n = 0; + } + for (let o of something) { + var p = 0; + let q = 0; + } + switch (something) { + case 0: var r = 0; + case 1: let s = 0; + case 2: { + var t = 0; + let u = 0; + } + } + function v(): void { + var w = 0; + let x = 0; + } + + // Errors + var a = 1; + let a = 1; + { + var a = 1; + } + var b = 1; + let b = 1; + { + var b = 1; + } + var c = 1; + let c = 1; + { + var c = 1; + } + var v = 1; + let v = 1; + { + var v = 1; + } +} diff --git a/tests/parser/scope.ts.fixture.ts b/tests/parser/scope.ts.fixture.ts new file mode 100644 index 0000000000..3b4608abe6 --- /dev/null +++ b/tests/parser/scope.ts.fixture.ts @@ -0,0 +1,71 @@ +function foo(a: i32): void /* {a,b,d,f,g,j,l,m,p,r,t,v} */ {/* {a,b,d,f,g,j,l,m,p,r,t,v,c} */ + var b = 0; + let c = 0; + {/* {a,b,d,c,e} */ + var d = 0; + let e = 0; + } + for (var f = 0; f < 10; ++f) /* {a,b,d,f,g,c} */ {/* {a,b,d,f,g,c,h} */ + var g = 0; + let h = 0; + } + for (let i = 0; i < 10; ++i) /* {a,b,d,f,g,j,c,i} */ {/* {a,b,d,f,g,j,c,i,k} */ + var j = 0; + let k = 0; + } + for (var l of something) /* {a,b,d,f,g,j,l,m,c} */ {/* {a,b,d,f,g,j,l,m,c,n} */ + var m = 0; + let n = 0; + } + for (let o of something) /* {a,b,d,f,g,j,l,m,p,c,o} */ {/* {a,b,d,f,g,j,l,m,p,c,o,q} */ + var p = 0; + let q = 0; + } + switch (something) /* {a,b,d,f,g,j,l,m,p,r,t,c,s} */ { + case 0: + var r = 0; + case 1: + let s = 0; + case 2: + {/* {a,b,d,f,g,j,l,m,p,r,t,c,s,u} */ + var t = 0; + let u = 0; + } + } + function v(): void /* {w} */ {/* {w,x} */ + var w = 0; + let x = 0; + }; + var a = 1; + let a = 1; + {/* {a,b,d,f,g,j,l,m,p,r,t,v,c} */ + var a = 1; + } + var b = 1; + let b = 1; + {/* {a,b,d,f,g,j,l,m,p,r,t,v,c} */ + var b = 1; + } + var c = 1; + let c = 1; + {/* {a,b,d,f,g,j,l,m,p,r,t,v,c} */ + var c = 1; + } + var v = 1; + let v = 1; + {/* {a,b,d,f,g,j,l,m,p,r,t,v,c} */ + var v = 1; + } +} +// ERROR 2300: "Duplicate identifier 'a'." in scope.ts:38:7 +// ERROR 2300: "Duplicate identifier 'a'." in scope.ts:39:7 +// ERROR 2300: "Duplicate identifier 'a'." in scope.ts:41:9 +// ERROR 2300: "Duplicate identifier 'b'." in scope.ts:43:7 +// ERROR 2300: "Duplicate identifier 'b'." in scope.ts:44:7 +// ERROR 2300: "Duplicate identifier 'b'." in scope.ts:46:9 +// ERROR 2300: "Duplicate identifier 'c'." in scope.ts:48:7 +// ERROR 2300: "Duplicate identifier 'c'." in scope.ts:49:7 +// ERROR 2300: "Duplicate identifier 'c'." in scope.ts:51:9 +// ERROR 2300: "Duplicate identifier 'v'." in scope.ts:53:7 +// ERROR 2300: "Duplicate identifier 'v'." in scope.ts:54:7 +// ERROR 2300: "Duplicate identifier 'v'." in scope.ts:56:9 diff --git a/tests/parser/string-binding.ts.fixture.ts b/tests/parser/string-binding.ts.fixture.ts index 92f9e21a5c..30994d7b0d 100644 --- a/tests/parser/string-binding.ts.fixture.ts +++ b/tests/parser/string-binding.ts.fixture.ts @@ -1,48 +1,48 @@ @binding(BindingCall.NEW, [BindingType.STRING], BindingType.OBJECT_HANDLE) export class ExternalString { @binding(BindingCall.FUNCTION, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE) - static fromCharCode(char: u16, schar?: u16 = -1): String { + static fromCharCode(char: u16, schar?: u16 = -1): String /* {char,schar} */ {/* {char,schar} */ return unreachable(); } @binding(BindingCall.FUNCTION, [BindingType.U32], BindingType.OBJECT_HANDLE) - static fromCodePoint(codepoint: u32): String { + static fromCodePoint(codepoint: u32): String /* {codepoint} */ {/* {codepoint} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.U32], BindingType.OBJECT_HANDLE) - charAt(index: u32): String { + charAt(index: u32): String /* {index} */ {/* {index} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.U32], BindingType.PASS_THRU) - charCodeAt(index: u32): u16 { + charCodeAt(index: u32): u16 /* {index} */ {/* {index} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.U32], BindingType.PASS_THRU) - codePointAt(index: u32): u32 { + codePointAt(index: u32): u32 /* {index} */ {/* {index} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.OBJECT_HANDLE) @operator("+") - concat(other: String): String { + concat(other: String): String /* {other} */ {/* {other} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU) - endsWith(other: String): bool { + endsWith(other: String): bool /* {other} */ {/* {other} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU) - indexOf(other: String): i32 { + indexOf(other: String): i32 /* {other} */ {/* {other} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU) - startsWith(other: String): bool { + startsWith(other: String): bool /* {other} */ {/* {other} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE) - substr(start: i32, length: i32): String { + substr(start: i32, length: i32): String /* {start,length} */ {/* {start,length} */ return unreachable(); } @binding(BindingCall.THIS, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE) - substring(start: i32, end: i32): String { + substring(start: i32, end: i32): String /* {start,end} */ {/* {start,end} */ return unreachable(); } @binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE) @@ -59,7 +59,7 @@ export class ExternalString { } @binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU) @operator("==") - equals(other: String): bool { + equals(other: String): bool /* {other} */ {/* {other} */ return unreachable(); } } diff --git a/tests/parser/trailing-commas.ts.fixture.ts b/tests/parser/trailing-commas.ts.fixture.ts index 3433f6e1c4..f511e8ff92 100644 --- a/tests/parser/trailing-commas.ts.fixture.ts +++ b/tests/parser/trailing-commas.ts.fixture.ts @@ -6,11 +6,11 @@ enum Foo { A, B } -function add(x: i32, y: i32): i32 { +function add(x: i32, y: i32): i32 /* {x,y} */ {/* {x,y} */ return x + y; } -function parameterized(a: A, b: B): void {} -export function compute(): i32 { +function parameterized(a: A, b: B): void /* {a,b} */ {/* {a,b} */} +export function compute(): i32 {/* {arr} */ const arr: Array = [1, 2]; parameterized(0, 0); return add(1, 2);