From f40421279333dbe4ca0f7154f140ea2613677e33 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:01:41 -0800 Subject: [PATCH 01/21] Make fewer direct calls to 'error' and 'errorAtPos' in the parser. --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 2b8acf0feb1..ffd32b6094c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1234,7 +1234,7 @@ module ts { } } else { - error(Diagnostics._0_expected, ";"); + parseExpected(SyntaxKind.SemicolonToken); } } @@ -1536,7 +1536,7 @@ module ts { if (isListTerminator(kind)) { break; } - error(Diagnostics._0_expected, ","); + parseExpected(SyntaxKind.CommaToken); } else if (isListTerminator(kind)) { break; From 7eb4e742d12341d9bd1ed85f1af38eb0bcfe1838 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:16:55 -0800 Subject: [PATCH 02/21] Don't directly produce errors while parsing 'try' statements. --- src/compiler/diagnosticInformationMap.generated.ts | 1 - src/compiler/diagnosticMessages.json | 4 ---- src/compiler/parser.ts | 9 +++++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index c17cf94e8c0..f593348e2a7 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -106,7 +106,6 @@ module ts { Type_argument_expected: { code: 1140, category: DiagnosticCategory.Error, key: "Type argument expected." }, String_literal_expected: { code: 1141, category: DiagnosticCategory.Error, key: "String literal expected." }, Line_break_not_permitted_here: { code: 1142, category: DiagnosticCategory.Error, key: "Line break not permitted here." }, - catch_or_finally_expected: { code: 1143, category: DiagnosticCategory.Error, key: "'catch' or 'finally' expected." }, Block_or_expected: { code: 1144, category: DiagnosticCategory.Error, key: "Block or ';' expected." }, Modifiers_not_permitted_on_index_signature_members: { code: 1145, category: DiagnosticCategory.Error, key: "Modifiers not permitted on index signature members." }, Declaration_expected: { code: 1146, category: DiagnosticCategory.Error, key: "Declaration expected." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3d5be6d566a..ef45a93b298 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -415,10 +415,6 @@ "category": "Error", "code": 1142 }, - "'catch' or 'finally' expected.": { - "category": "Error", - "code": 1143 - }, "Block or ';' expected.": { "category": "Error", "code": 1144 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ffd32b6094c..5b4805cd74d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3397,12 +3397,13 @@ module ts { if (token === SyntaxKind.CatchKeyword) { node.catchBlock = parseCatchBlock(); } - if (token === SyntaxKind.FinallyKeyword) { + + // If we don't have a catch clause, then we must have a finally clause. Try to parse + // one out no matter what. + if (!node.catchBlock || token === SyntaxKind.FinallyKeyword) { node.finallyBlock = parseTokenAndBlock(SyntaxKind.FinallyKeyword, SyntaxKind.FinallyBlock); } - if (!(node.catchBlock || node.finallyBlock)) { - error(Diagnostics.catch_or_finally_expected); - } + return finishNode(node); } From f91996a80c5d03565e3df156db3608150280ddab Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:31:31 -0800 Subject: [PATCH 03/21] Don't report an error directly when parsing a function body. --- src/compiler/diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- src/compiler/parser.ts | 12 ++++-------- .../baselines/reference/dottedModuleName.errors.txt | 4 ++-- .../objectTypesWithOptionalProperties2.errors.txt | 8 ++++---- ...rErrantEqualsGreaterThanAfterFunction1.errors.txt | 4 ++-- ...rErrantEqualsGreaterThanAfterFunction2.errors.txt | 4 ++-- 7 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index f593348e2a7..74341b96651 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -106,7 +106,7 @@ module ts { Type_argument_expected: { code: 1140, category: DiagnosticCategory.Error, key: "Type argument expected." }, String_literal_expected: { code: 1141, category: DiagnosticCategory.Error, key: "String literal expected." }, Line_break_not_permitted_here: { code: 1142, category: DiagnosticCategory.Error, key: "Line break not permitted here." }, - Block_or_expected: { code: 1144, category: DiagnosticCategory.Error, key: "Block or ';' expected." }, + or_expected: { code: 1144, category: DiagnosticCategory.Error, key: "'{' or ';' expected." }, Modifiers_not_permitted_on_index_signature_members: { code: 1145, category: DiagnosticCategory.Error, key: "Modifiers not permitted on index signature members." }, Declaration_expected: { code: 1146, category: DiagnosticCategory.Error, key: "Declaration expected." }, Import_declarations_in_an_internal_module_cannot_reference_an_external_module: { code: 1147, category: DiagnosticCategory.Error, key: "Import declarations in an internal module cannot reference an external module." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ef45a93b298..0e06f4fcfb0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -415,7 +415,7 @@ "category": "Error", "code": 1142 }, - "Block or ';' expected.": { + "'{' or ';' expected.": { "category": "Error", "code": 1144 }, diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5b4805cd74d..934cec2c0bc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1226,7 +1226,7 @@ module ts { return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak(); } - function parseSemicolon(): void { + function parseSemicolon(diagnosticMessage?: DiagnosticMessage): void { if (canParseSemicolon()) { if (token === SyntaxKind.SemicolonToken) { // consume the semicolon if it was explicitly provided. @@ -1234,7 +1234,7 @@ module ts { } } else { - parseExpected(SyntaxKind.SemicolonToken); + parseExpected(SyntaxKind.SemicolonToken, diagnosticMessage); } } @@ -3572,12 +3572,8 @@ module ts { return parseFunctionBlock(isGenerator, /* ignoreMissingOpenBrace */ false); } - if (canParseSemicolon()) { - parseSemicolon(); - return undefined; - } - - error(Diagnostics.Block_or_expected); // block or ';' expected + parseSemicolon(Diagnostics.or_expected); + return undefined; } // DECLARATIONS diff --git a/tests/baselines/reference/dottedModuleName.errors.txt b/tests/baselines/reference/dottedModuleName.errors.txt index 0b8c3a5c422..9d9de6ee230 100644 --- a/tests/baselines/reference/dottedModuleName.errors.txt +++ b/tests/baselines/reference/dottedModuleName.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/dottedModuleName.ts(3,29): error TS1144: Block or ';' expected. +tests/cases/compiler/dottedModuleName.ts(3,29): error TS1144: '{' or ';' expected. tests/cases/compiler/dottedModuleName.ts(3,18): error TS2391: Function implementation is missing or not immediately following the declaration. tests/cases/compiler/dottedModuleName.ts(3,33): error TS2304: Cannot find name 'x'. @@ -8,7 +8,7 @@ tests/cases/compiler/dottedModuleName.ts(3,33): error TS2304: Cannot find name ' export module N { export function f(x:number)=>2*x; ~~ -!!! error TS1144: Block or ';' expected. +!!! error TS1144: '{' or ';' expected. ~ !!! error TS2391: Function implementation is missing or not immediately following the declaration. ~ diff --git a/tests/baselines/reference/objectTypesWithOptionalProperties2.errors.txt b/tests/baselines/reference/objectTypesWithOptionalProperties2.errors.txt index a427681b22e..533834baea7 100644 --- a/tests/baselines/reference/objectTypesWithOptionalProperties2.errors.txt +++ b/tests/baselines/reference/objectTypesWithOptionalProperties2.errors.txt @@ -2,11 +2,11 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(4,9): error TS1131: Property or signature expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(8,8): error TS1005: ';' expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(8,9): error TS1131: Property or signature expected. -tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(12,8): error TS1144: Block or ';' expected. +tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(12,8): error TS1144: '{' or ';' expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(12,9): error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(16,8): error TS1005: ';' expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(16,9): error TS1131: Property or signature expected. -tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(20,8): error TS1144: Block or ';' expected. +tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(20,8): error TS1144: '{' or ';' expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(20,9): error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(25,8): error TS1005: '{' expected. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts(25,9): error TS1136: Property assignment expected. @@ -37,7 +37,7 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith class C { x()?: number; // error ~ -!!! error TS1144: Block or ';' expected. +!!! error TS1144: '{' or ';' expected. ~ !!! error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. ~ @@ -55,7 +55,7 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith class C2 { x()?: T; // error ~ -!!! error TS1144: Block or ';' expected. +!!! error TS1144: '{' or ';' expected. ~ !!! error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. ~ diff --git a/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction1.errors.txt b/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction1.errors.txt index 70d6a8481ee..7c32d4433e4 100644 --- a/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction1.errors.txt +++ b/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction1.errors.txt @@ -1,10 +1,10 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction1.ts(1,14): error TS1144: Block or ';' expected. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction1.ts(1,14): error TS1144: '{' or ';' expected. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction1.ts(1,10): error TS2391: Function implementation is missing or not immediately following the declaration. ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction1.ts (2 errors) ==== function f() => 4; ~~ -!!! error TS1144: Block or ';' expected. +!!! error TS1144: '{' or ';' expected. ~ !!! error TS2391: Function implementation is missing or not immediately following the declaration. \ No newline at end of file diff --git a/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction2.errors.txt b/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction2.errors.txt index a830b84ff6e..8df3b0bbff6 100644 --- a/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction2.errors.txt +++ b/tests/baselines/reference/parserErrantEqualsGreaterThanAfterFunction2.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts(1,18): error TS1144: Block or ';' expected. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts(1,18): error TS1144: '{' or ';' expected. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts(1,10): error TS2391: Function implementation is missing or not immediately following the declaration. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts(1,15): error TS2304: Cannot find name 'A'. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts(1,21): error TS2304: Cannot find name 'p'. @@ -7,7 +7,7 @@ tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreat ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserErrantEqualsGreaterThanAfterFunction2.ts (4 errors) ==== function f(p: A) => p; ~~ -!!! error TS1144: Block or ';' expected. +!!! error TS1144: '{' or ';' expected. ~ !!! error TS2391: Function implementation is missing or not immediately following the declaration. ~ From 2f634b7e5429ec8926b3c42ce5ba08da2ae4c73a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:45:18 -0800 Subject: [PATCH 04/21] Don't report errors directly when parsing throw statements or try/finally statements. --- src/compiler/parser.ts | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 934cec2c0bc..1f060945740 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3380,12 +3380,17 @@ module ts { } function parseThrowStatement(): ThrowStatement { + // ThrowStatement[Yield] : + // throw [no LineTerminator here]Expression[In, ?Yield]; + + // Because of automatic semicolon insertion, we need to report error if this + // throw could be terminated with a semicolon. Note: we can't call 'parseExpression' + // directly as that might consume an expression on the following line. + // We just return 'undefined' in that case. The actual error will be reported in the + // grammar walker. var node = createNode(SyntaxKind.ThrowStatement); parseExpected(SyntaxKind.ThrowKeyword); - if (scanner.hasPrecedingLineBreak()) { - error(Diagnostics.Line_break_not_permitted_here); - } - node.expression = allowInAnd(parseExpression); + node.expression = scanner.hasPrecedingLineBreak() ? undefined : allowInAnd(parseExpression); parseSemicolon(); return finishNode(node); } @@ -3393,25 +3398,20 @@ module ts { // TODO: Review for error recovery function parseTryStatement(): TryStatement { var node = createNode(SyntaxKind.TryStatement); - node.tryBlock = parseTokenAndBlock(SyntaxKind.TryKeyword, SyntaxKind.TryBlock); - if (token === SyntaxKind.CatchKeyword) { - node.catchBlock = parseCatchBlock(); - } + node.tryBlock = parseTokenAndBlock(SyntaxKind.TryKeyword); + node.catchBlock = token === SyntaxKind.CatchKeyword ? parseCatchBlock() : undefined; // If we don't have a catch clause, then we must have a finally clause. Try to parse // one out no matter what. - if (!node.catchBlock || token === SyntaxKind.FinallyKeyword) { - node.finallyBlock = parseTokenAndBlock(SyntaxKind.FinallyKeyword, SyntaxKind.FinallyBlock); - } - + node.finallyBlock = !node.catchBlock || token === SyntaxKind.FinallyKeyword ? parseTokenAndBlock(SyntaxKind.FinallyKeyword) : undefined; return finishNode(node); } - function parseTokenAndBlock(token: SyntaxKind, kind: SyntaxKind): Block { + function parseTokenAndBlock(token: SyntaxKind): Block { var pos = getNodePos(); parseExpected(token); var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); - result.kind = kind; + result.kind = token === SyntaxKind.TryKeyword ? SyntaxKind.TryBlock : SyntaxKind.FinallyBlock; result.pos = pos; return result; } @@ -4321,6 +4321,7 @@ module ts { case SyntaxKind.ShorthandPropertyAssignment: return checkShorthandPropertyAssignment(node); case SyntaxKind.SwitchStatement: return checkSwitchStatement(node); case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); + case SyntaxKind.ThrowStatement: return checkThrowStatement(node); case SyntaxKind.TupleType: return checkTupleType(node); case SyntaxKind.TypeParameter: return checkTypeParameter(node); case SyntaxKind.TypeReference: return checkTypeReference(node); @@ -4331,12 +4332,22 @@ module ts { } } - function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { - var start = skipTrivia(sourceText, node.pos); + function scanToken(pos: number) { + var start = skipTrivia(sourceText, pos); scanner.setTextPos(start); scanner.scan(); - var end = scanner.getTextPos(); - grammarDiagnostics.push(createFileDiagnostic(file, start, end - start, message, arg0, arg1, arg2)); + return start; + } + + function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { + var start = scanToken(node.pos); + grammarDiagnostics.push(createFileDiagnostic(file, start, scanner.getTextPos() - start, message, arg0, arg1, arg2)); + return true; + } + + function grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { + scanToken(node.pos); + grammarDiagnostics.push(createFileDiagnostic(file, scanner.getTextPos(), 0, message, arg0, arg1, arg2)); return true; } @@ -5348,6 +5359,12 @@ module ts { } } + function checkThrowStatement(node: ThrowStatement) { + if (node.expression === undefined) { + return grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here); + } + } + function checkTupleType(node: TupleTypeNode) { return checkForDisallowedTrailingComma(node.elementTypes) || checkForAtLeastOneType(node); From 94febee13874e2309be130e232ffe057ee1b025b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:52:52 -0800 Subject: [PATCH 05/21] Don't create errors directly when parsing template spans. --- src/compiler/diagnosticInformationMap.generated.ts | 1 - src/compiler/diagnosticMessages.json | 4 ---- src/compiler/parser.ts | 3 +-- tests/baselines/reference/TemplateExpression1.errors.txt | 4 ++-- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 74341b96651..6b545ddfd0d 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -119,7 +119,6 @@ module ts { const_declarations_must_be_initialized: { code: 1155, category: DiagnosticCategory.Error, key: "'const' declarations must be initialized" }, const_declarations_can_only_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "'const' declarations can only be declared inside a block." }, let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." }, - Invalid_template_literal_expected: { code: 1158, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" }, Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1159, category: DiagnosticCategory.Error, key: "Tagged templates are only available when targeting ECMAScript 6 and higher." }, Unterminated_template_literal: { code: 1160, category: DiagnosticCategory.Error, key: "Unterminated template literal." }, Unterminated_regular_expression_literal: { code: 1161, category: DiagnosticCategory.Error, key: "Unterminated regular expression literal." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0e06f4fcfb0..8d9dbe94097 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -467,10 +467,6 @@ "category": "Error", "code": 1157 }, - "Invalid template literal; expected '}'": { - "category": "Error", - "code": 1158 - }, "Tagged templates are only available when targeting ECMAScript 6 and higher.": { "category": "Error", "code": 1159 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1f060945740..9bfde417a2f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1676,13 +1676,12 @@ module ts { literal = parseLiteralNode(); } else { - error(Diagnostics.Invalid_template_literal_expected); + parseExpected(SyntaxKind.CloseBraceToken); literal = createMissingNode(); literal.text = ""; } span.literal = literal; - return finishNode(span); } diff --git a/tests/baselines/reference/TemplateExpression1.errors.txt b/tests/baselines/reference/TemplateExpression1.errors.txt index bf57f5ef24e..c13b0fabd54 100644 --- a/tests/baselines/reference/TemplateExpression1.errors.txt +++ b/tests/baselines/reference/TemplateExpression1.errors.txt @@ -1,10 +1,10 @@ -tests/cases/conformance/es6/templates/TemplateExpression1.ts(1,19): error TS1158: Invalid template literal; expected '}' +tests/cases/conformance/es6/templates/TemplateExpression1.ts(1,19): error TS1005: '}' expected. tests/cases/conformance/es6/templates/TemplateExpression1.ts(1,17): error TS2304: Cannot find name 'a'. ==== tests/cases/conformance/es6/templates/TemplateExpression1.ts (2 errors) ==== var v = `foo ${ a -!!! error TS1158: Invalid template literal; expected '}' +!!! error TS1005: '}' expected. ~ !!! error TS2304: Cannot find name 'a'. \ No newline at end of file From 08d3e4a90ea694422a1d6f88cdb5356ef1964f76 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 00:57:48 -0800 Subject: [PATCH 06/21] Don't directly create errors when parsing expressions. --- src/compiler/parser.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9bfde417a2f..77d6d69ba10 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1272,7 +1272,7 @@ module ts { // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues // with magic property names like '__proto__'. The 'identifiers' object is used to share a single string instance for // each identifier in order to reduce memory consumption. - function createIdentifier(isIdentifier: boolean): Identifier { + function createIdentifier(isIdentifier: boolean, diagnosticMessage?: DiagnosticMessage): Identifier { identifierCount++; if (isIdentifier) { var node = createNode(SyntaxKind.Identifier); @@ -1280,14 +1280,18 @@ module ts { nextToken(); return finishNode(node); } - error(Diagnostics.Identifier_expected); + error(diagnosticMessage || Diagnostics.Identifier_expected); + return createMissingIdentifier(); + } + + function createMissingIdentifier() { var node = createMissingNode(); node.text = ""; return node; } - function parseIdentifier(): Identifier { - return createIdentifier(isIdentifier()); + function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier { + return createIdentifier(isIdentifier(), diagnosticMessage); } function parseIdentifierName(): Identifier { @@ -1677,8 +1681,7 @@ module ts { } else { parseExpected(SyntaxKind.CloseBraceToken); - literal = createMissingNode(); - literal.text = ""; + literal = createMissingIdentifier(); } span.literal = literal; @@ -3032,14 +3035,9 @@ module ts { break; case SyntaxKind.TemplateHead: return parseTemplateExpression(); - - default: - if (isIdentifier()) { - return parseIdentifier(); - } } - error(Diagnostics.Expression_expected); - return createMissingNode(); + + return parseIdentifier(Diagnostics.Expression_expected); } function parseParenthesizedExpression(): ParenthesizedExpression { From 4db6d3136cf19e82217cb50254ce296a7a4554fc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 01:10:21 -0800 Subject: [PATCH 07/21] Extract out common code from the list parsers. --- src/compiler/parser.ts | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 77d6d69ba10..45345237958 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1280,6 +1280,7 @@ module ts { nextToken(); return finishNode(node); } + error(diagnosticMessage || Diagnostics.Identifier_expected); return createMissingIdentifier(); } @@ -1490,12 +1491,14 @@ module ts { var result = >[]; result.pos = getNodePos(); var savedStrictModeContext = inStrictModeContext(); + while (!isListTerminator(kind)) { if (isListElement(kind, /* inErrorRecovery */ false)) { var element = parseElement(); result.push(element); + // test elements only if we are not already in strict mode - if (!inStrictModeContext() && checkForStrictMode) { + if (checkForStrictMode && !inStrictModeContext()) { if (isPrologueDirective(element)) { if (isUseStrictPrologueDirective(element)) { setStrictModeContext(true); @@ -1506,21 +1509,32 @@ module ts { checkForStrictMode = false; } } + + continue; } - else { - error(parsingContextErrors(kind)); - if (isInSomeParsingContext()) { - break; - } - nextToken(); + + if (abortParsingListOrMoveToNextToken(kind)) { + break; } } + setStrictModeContext(savedStrictModeContext); result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } + // Returns true if we should abort parsing. + function abortParsingListOrMoveToNextToken(kind: ParsingContext) { + error(parsingContextErrors(kind)); + if (isInSomeParsingContext()) { + return true; + } + + nextToken(); + return false; + } + // Parses a comma-delimited list of elements function parseDelimitedList(kind: ParsingContext, parseElement: () => T): NodeArray { var saveParsingContext = parsingContext; @@ -1541,16 +1555,15 @@ module ts { break; } parseExpected(SyntaxKind.CommaToken); + continue; } - else if (isListTerminator(kind)) { + + if (isListTerminator(kind)) { break; } - else { - error(parsingContextErrors(kind)); - if (isInSomeParsingContext()) { - break; - } - nextToken(); + + if (abortParsingListOrMoveToNextToken(kind)) { + break; } } From ada6cebef37ff59ba22d647bfdd51bf9cccf8c9c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 03:00:27 -0800 Subject: [PATCH 08/21] Don't parse nodes, only to not include them in the tree. This will break incremental parsing scenarios. Properly store the data for an external module reference in the AST. --- src/compiler/checker.ts | 30 +++++--- src/compiler/emitter.ts | 13 ++-- src/compiler/parser.ts | 70 +++++++++++++------ src/compiler/types.ts | 8 ++- src/services/services.ts | 4 +- .../importNonStringLiteral.errors.txt | 5 +- 6 files changed, 85 insertions(+), 45 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c43d1074dfa..7a1bfafcb47 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -467,9 +467,9 @@ module ts { if (!links.target) { links.target = resolvingSymbol; var node = getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration); - var target = node.externalModuleName ? - resolveExternalModuleName(node, node.externalModuleName) : - getSymbolOfPartOfRightHandSideOfImport(node.entityName, node); + var target = node.externalModuleName + ? resolveExternalModuleName(node, node.externalModuleName.expression) + : getSymbolOfPartOfRightHandSideOfImport(node.entityName, node); if (links.target === resolvingSymbol) { links.target = target || unknownSymbol; } @@ -546,7 +546,12 @@ module ts { return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\"; } - function resolveExternalModuleName(location: Node, moduleLiteral: LiteralExpression): Symbol { + function resolveExternalModuleName(location: Node, moduleExpression: Expression): Symbol { + if (moduleExpression.kind !== SyntaxKind.StringLiteral) { + return; + } + + var moduleLiteral = moduleExpression; var searchPath = getDirectoryPath(getSourceFile(location).filename); var moduleName = moduleLiteral.text; if (!moduleName) return; @@ -8368,12 +8373,17 @@ module ts { // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference // other external modules only through top - level external module names. // Relative external module names are not permitted. - if (isExternalModuleNameRelative(node.externalModuleName.text)) { - error(node, Diagnostics.Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name); - target = unknownSymbol; + if (node.externalModuleName.expression.kind === SyntaxKind.StringLiteral) { + if (isExternalModuleNameRelative((node.externalModuleName.expression).text)) { + error(node, Diagnostics.Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name); + target = unknownSymbol; + } + else { + target = resolveImport(symbol); + } } else { - target = resolveImport(symbol); + target = unknownSymbol; } } else { @@ -8948,8 +8958,8 @@ module ts { case SyntaxKind.StringLiteral: // External module name in an import declaration - if (node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).externalModuleName === node) { - var importSymbol = getSymbolOfNode(node.parent); + if (node.parent.parent.kind === SyntaxKind.ImportDeclaration && (node.parent.parent).externalModuleName.expression === node) { + var importSymbol = getSymbolOfNode(node.parent.parent); var moduleType = getTypeOfSymbol(importSymbol); return moduleType ? moduleType.symbol : undefined; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index df99de098f3..f5e5c056def 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -671,7 +671,7 @@ module ts { } else { write("require("); - writeTextOfNode(currentSourceFile, node.externalModuleName); + writeTextOfNode(currentSourceFile, node.externalModuleName.expression); write(");"); } writer.writeLine(); @@ -3312,11 +3312,12 @@ module ts { emit(node.entityName); } else { + var literal = node.externalModuleName.expression; write("require("); - emitStart(node.externalModuleName); - emitLiteral(node.externalModuleName); - emitEnd(node.externalModuleName); - emitToken(SyntaxKind.CloseParenToken, node.externalModuleName.end); + emitStart(literal); + emitLiteral(literal); + emitEnd(literal); + emitToken(SyntaxKind.CloseParenToken, literal.end); } write(";"); emitEnd(node); @@ -3356,7 +3357,7 @@ module ts { write("[\"require\", \"exports\""); forEach(imports, imp => { write(", "); - emitLiteral(imp.externalModuleName); + emitLiteral(imp.externalModuleName.expression); }); forEach(node.amdDependencies, amdDependency => { var text = "\"" + amdDependency + "\""; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 45345237958..9e8424030ab 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -394,6 +394,8 @@ module ts { return child((node).expression); case SyntaxKind.HeritageClause: return children((node).types); + case SyntaxKind.ExternalModuleReference: + return child((node).expression); } } @@ -1726,14 +1728,6 @@ module ts { return node; } - function parseStringLiteral(): LiteralExpression { - if (token === SyntaxKind.StringLiteral) { - return parseLiteralNode(/*internName:*/ true); - } - error(Diagnostics.String_literal_expected); - return createMissingNode(); - } - // TYPES function parseTypeReference(): TypeReferenceNode { @@ -1786,9 +1780,7 @@ module ts { function parseParameterType(): TypeNode { return parseOptional(SyntaxKind.ColonToken) - ? token === SyntaxKind.StringLiteral - ? parseStringLiteral() - : parseType() + ? token === SyntaxKind.StringLiteral ? parseLiteralNode(/*internName:*/ true) : parseType() : undefined; } @@ -3940,7 +3932,7 @@ module ts { function parseAmbientExternalModuleDeclaration(fullStart: number, flags: NodeFlags): ModuleDeclaration { var node = createNode(SyntaxKind.ModuleDeclaration, fullStart); node.flags = flags; - node.name = parseStringLiteral(); + node.name = parseLiteralNode(/*internName:*/ true); node.body = parseModuleBlock(); return finishNode(node); } @@ -3952,24 +3944,45 @@ module ts { : parseInternalModuleTail(fullStart, flags); } + function isExternalModuleReference() { + if (token === SyntaxKind.RequireKeyword) { + return lookAhead(() => { + nextToken(); + return token === SyntaxKind.OpenParenToken; + }); + } + + return false; + } + function parseImportDeclaration(fullStart: number, modifiers: ModifiersArray): ImportDeclaration { var node = createNode(SyntaxKind.ImportDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.ImportKeyword); node.name = parseIdentifier(); parseExpected(SyntaxKind.EqualsToken); - var entityName = parseEntityName(/*allowReservedWords*/ false); - if (entityName.kind === SyntaxKind.Identifier && (entityName).text === "require" && parseOptional(SyntaxKind.OpenParenToken)) { - node.externalModuleName = parseStringLiteral(); - parseExpected(SyntaxKind.CloseParenToken); + if (isExternalModuleReference()) { + node.externalModuleName = parseExternalModuleReference(); } else { - node.entityName = entityName; + node.entityName = parseEntityName(/*allowReservedWords*/ false); } parseSemicolon(); return finishNode(node); } + function parseExternalModuleReference() { + var node = createNode(SyntaxKind.ExternalModuleReference); + parseExpected(SyntaxKind.RequireKeyword); + parseExpected(SyntaxKind.OpenParenToken); + node.expression = parseExpression(); + if (node.expression.kind === SyntaxKind.StringLiteral) { + internIdentifier((node.expression).text); + } + parseExpected(SyntaxKind.CloseParenToken); + return finishNode(node); + } + function parseExportAssignmentTail(fullStart: number, modifiers: ModifiersArray): ExportAssignment { var node = createNode(SyntaxKind.ExportAssignment, fullStart); setModifiers(node, modifiers); @@ -4307,6 +4320,7 @@ module ts { case SyntaxKind.DeleteExpression: return checkDeleteExpression( node); case SyntaxKind.ElementAccessExpression: return checkElementAccessExpression(node); case SyntaxKind.ExportAssignment: return checkExportAssignment(node); + case SyntaxKind.ExternalModuleReference: return checkExternalModuleReference(node); case SyntaxKind.ForInStatement: return checkForInStatement(node); case SyntaxKind.ForStatement: return checkForStatement(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); @@ -4704,6 +4718,12 @@ module ts { } } + function checkExternalModuleReference(node: ExternalModuleReference) { + if (node.expression.kind !== SyntaxKind.StringLiteral) { + return grammarErrorOnNode(node.expression, Diagnostics.String_literal_expected); + } + } + function checkForInStatement(node: ForInStatement) { return checkVariableDeclarations(node.declarations) || checkForMoreThanOneDeclaration(node.declarations); @@ -4905,7 +4925,7 @@ module ts { return grammarErrorOnNode(statement, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); } else if (statement.kind === SyntaxKind.ImportDeclaration && (statement).externalModuleName) { - return grammarErrorOnNode((statement).externalModuleName, Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); + return grammarErrorOnNode((statement).externalModuleName.expression, Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); } } } @@ -5627,8 +5647,11 @@ module ts { function processImportedModules(file: SourceFile, basePath: string) { forEach(file.statements, node => { - if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) { - var nameLiteral = (node).externalModuleName; + if (node.kind === SyntaxKind.ImportDeclaration && + (node).externalModuleName && + (node).externalModuleName.expression.kind === SyntaxKind.StringLiteral) { + + var nameLiteral = (node).externalModuleName.expression; var moduleName = nameLiteral.text; if (moduleName) { var searchPath = basePath; @@ -5653,8 +5676,11 @@ module ts { // The StringLiteral must specify a top - level external module name. // Relative external module names are not permitted forEachChild((node).body, node => { - if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) { - var nameLiteral = (node).externalModuleName; + if (node.kind === SyntaxKind.ImportDeclaration && + (node).externalModuleName && + (node).externalModuleName.expression.kind === SyntaxKind.StringLiteral) { + + var nameLiteral = (node).externalModuleName.expression; var moduleName = nameLiteral.text; if (moduleName) { // TypeScript 1.0 spec (April 2014): 12.1.6 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e97aaeb2edb..0a636b94b06 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -221,6 +221,8 @@ module ts { ModuleBlock, ImportDeclaration, ExportAssignment, + // Module references + ExternalModuleReference, // Clauses CaseClause, DefaultClause, @@ -730,7 +732,11 @@ module ts { export interface ImportDeclaration extends Declaration, ModuleElement { name: Identifier; entityName?: EntityName; - externalModuleName?: LiteralExpression; + externalModuleName?: ExternalModuleReference; + } + + export interface ExternalModuleReference extends Node { + expression?: Expression; } export interface ExportAssignment extends Statement, ModuleElement { diff --git a/src/services/services.ts b/src/services/services.ts index 0ef6ab02b72..a4569d88791 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1982,7 +1982,7 @@ module ts { function isNameOfExternalModuleImportOrDeclaration(node: Node): boolean { return node.kind === SyntaxKind.StringLiteral && (isNameOfModuleDeclaration(node) || - (node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).externalModuleName === node)); + (node.parent.parent.kind === SyntaxKind.ImportDeclaration && (node.parent.parent).externalModuleName.expression === node)); } /** Returns true if the position is within a comment */ @@ -3086,7 +3086,7 @@ module ts { displayParts.push(spacePart()); displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - displayParts.push(displayPart(getTextOfNode(importDeclaration.externalModuleName), SymbolDisplayPartKind.stringLiteral)); + displayParts.push(displayPart(getTextOfNode(importDeclaration.externalModuleName.expression), SymbolDisplayPartKind.stringLiteral)); displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } else { diff --git a/tests/baselines/reference/importNonStringLiteral.errors.txt b/tests/baselines/reference/importNonStringLiteral.errors.txt index ce3dbb3e1ec..7138680d709 100644 --- a/tests/baselines/reference/importNonStringLiteral.errors.txt +++ b/tests/baselines/reference/importNonStringLiteral.errors.txt @@ -1,12 +1,9 @@ tests/cases/conformance/externalModules/importNonStringLiteral.ts(2,22): error TS1141: String literal expected. -tests/cases/conformance/externalModules/importNonStringLiteral.ts(2,23): error TS1005: ';' expected. -==== tests/cases/conformance/externalModules/importNonStringLiteral.ts (2 errors) ==== +==== tests/cases/conformance/externalModules/importNonStringLiteral.ts (1 errors) ==== var x = "filename"; import foo = require(x); // invalid ~ !!! error TS1141: String literal expected. - ~ -!!! error TS1005: ';' expected. \ No newline at end of file From 1600b38760119457fe34c2e515e3920a43591faa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 03:15:51 -0800 Subject: [PATCH 09/21] Don't explicitly call 'error' when parsing a type. --- src/compiler/parser.ts | 20 +++++++------------ .../parservoidInQualifiedName1.errors.txt | 9 +++------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9e8424030ab..db1f7b18ee3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1603,8 +1603,8 @@ module ts { } // The allowReservedWords parameter controls whether reserved words are permitted after the first dot - function parseEntityName(allowReservedWords: boolean): EntityName { - var entity: EntityName = parseIdentifier(); + function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { + var entity: EntityName = parseIdentifier(diagnosticMessage); while (parseOptional(SyntaxKind.DotToken)) { var node = createNode(SyntaxKind.QualifiedName, entity.pos); node.left = entity; @@ -1732,7 +1732,7 @@ module ts { function parseTypeReference(): TypeReferenceNode { var node = createNode(SyntaxKind.TypeReference); - node.typeName = parseEntityName(/*allowReservedWords*/ false); + node.typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { node.typeArguments = parseTypeArguments(); } @@ -2102,9 +2102,11 @@ module ts { case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: - case SyntaxKind.VoidKeyword: + // If these are followed by a dot, then parse these out as a dotted type reference instead. var node = tryParse(parseKeywordAndNoDot); return node || parseTypeReference(); + case SyntaxKind.VoidKeyword: + return parseTokenNode(); case SyntaxKind.TypeOfKeyword: return parseTypeQuery(); case SyntaxKind.OpenBraceToken: @@ -2114,12 +2116,8 @@ module ts { case SyntaxKind.OpenParenToken: return parseParenType(); default: - if (isIdentifier()) { - return parseTypeReference(); - } + return parseTypeReference(); } - error(Diagnostics.Type_expected); - return createMissingNode(); } function isStartOfType(): boolean { @@ -3852,10 +3850,6 @@ module ts { return parseList(ParsingContext.ClassMembers, /*checkForStrictMode*/ false, parseClassElement); } - function parseClassBaseType(): TypeReferenceNode { - return parseOptional(SyntaxKind.ExtendsKeyword) ? parseTypeReference() : undefined; - } - function parseInterfaceDeclaration(fullStart: number, modifiers: ModifiersArray): InterfaceDeclaration { var node = createNode(SyntaxKind.InterfaceDeclaration, fullStart); setModifiers(node, modifiers); diff --git a/tests/baselines/reference/parservoidInQualifiedName1.errors.txt b/tests/baselines/reference/parservoidInQualifiedName1.errors.txt index a8a77c9a220..d5433fb9078 100644 --- a/tests/baselines/reference/parservoidInQualifiedName1.errors.txt +++ b/tests/baselines/reference/parservoidInQualifiedName1.errors.txt @@ -1,10 +1,7 @@ -tests/cases/conformance/parser/ecmascript5/parservoidInQualifiedName1.ts(1,9): error TS1003: Identifier expected. -tests/cases/conformance/parser/ecmascript5/parservoidInQualifiedName1.ts(1,13): error TS1109: Expression expected. +tests/cases/conformance/parser/ecmascript5/parservoidInQualifiedName1.ts(1,13): error TS1005: ',' expected. -==== tests/cases/conformance/parser/ecmascript5/parservoidInQualifiedName1.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/parservoidInQualifiedName1.ts (1 errors) ==== var v : void.x; - ~~~~ -!!! error TS1003: Identifier expected. ~ -!!! error TS1109: Expression expected. \ No newline at end of file +!!! error TS1005: ',' expected. \ No newline at end of file From 64b1f232b5ae666cf2be800814cf7d0e2aaacf5a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 03:24:12 -0800 Subject: [PATCH 10/21] Simplify the code for parseDeclaration. This is to help get to a point where we can remove the direct call to 'error' within it. --- src/compiler/parser.ts | 60 +++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db1f7b18ee3..6db6d2a30a7 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3883,9 +3883,11 @@ module ts { return finishNode(node); } - function parseAndCheckEnumDeclaration(fullStart: number, flags: NodeFlags): EnumDeclaration { + function parseAndCheckEnumDeclaration(fullStart: number, modifiers: ModifiersArray, flags: NodeFlags): EnumDeclaration { var node = createNode(SyntaxKind.EnumDeclaration, fullStart); - node.flags = flags; + setModifiers(node, modifiers); + + node.flags |= flags; if (flags & NodeFlags.Const) { parseExpected(SyntaxKind.ConstKeyword); } @@ -3913,29 +3915,31 @@ module ts { return finishNode(node); } - function parseInternalModuleTail(fullStart: number, flags: NodeFlags): ModuleDeclaration { + function parseInternalModuleTail(fullStart: number, modifiers: ModifiersArray, flags: NodeFlags): ModuleDeclaration { var node = createNode(SyntaxKind.ModuleDeclaration, fullStart); - node.flags = flags; + setModifiers(node, modifiers); + node.flags |= flags; node.name = parseIdentifier(); node.body = parseOptional(SyntaxKind.DotToken) - ? parseInternalModuleTail(getNodePos(), NodeFlags.Export) + ? parseInternalModuleTail(getNodePos(), /*modifiers:*/undefined, NodeFlags.Export) : parseModuleBlock(); return finishNode(node); } - function parseAmbientExternalModuleDeclaration(fullStart: number, flags: NodeFlags): ModuleDeclaration { + function parseAmbientExternalModuleDeclaration(fullStart: number, modifiers: ModifiersArray, flags: NodeFlags): ModuleDeclaration { var node = createNode(SyntaxKind.ModuleDeclaration, fullStart); - node.flags = flags; + setModifiers(node, modifiers); + node.flags |= flags; node.name = parseLiteralNode(/*internName:*/ true); node.body = parseModuleBlock(); return finishNode(node); } - function parseModuleDeclaration(fullStart: number, flags: NodeFlags): ModuleDeclaration { + function parseModuleDeclaration(fullStart: number, modifiers: ModifiersArray, flags: NodeFlags): ModuleDeclaration { parseExpected(SyntaxKind.ModuleKeyword); return token === SyntaxKind.StringLiteral - ? parseAmbientExternalModuleDeclaration(fullStart, flags) - : parseInternalModuleTail(fullStart, flags); + ? parseAmbientExternalModuleDeclaration(fullStart, modifiers, flags) + : parseInternalModuleTail(fullStart, modifiers, flags); } function isExternalModuleReference() { @@ -4026,51 +4030,35 @@ module ts { } var flags = modifiers ? modifiers.flags : 0; - var result: ModuleElement; switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: - result = parseVariableStatement(fullStart, modifiers); - break; + return parseVariableStatement(fullStart, modifiers); case SyntaxKind.ConstKeyword: var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); if (isConstEnum) { - result = parseAndCheckEnumDeclaration(fullStart, flags | NodeFlags.Const); + return parseAndCheckEnumDeclaration(fullStart, modifiers, flags | NodeFlags.Const); } else { - result = parseVariableStatement(fullStart, modifiers); + return parseVariableStatement(fullStart, modifiers); } - break; case SyntaxKind.FunctionKeyword: - result = parseFunctionDeclaration(fullStart, modifiers); - break; + return parseFunctionDeclaration(fullStart, modifiers); case SyntaxKind.ClassKeyword: - result = parseClassDeclaration(fullStart, modifiers); - break; + return parseClassDeclaration(fullStart, modifiers); case SyntaxKind.InterfaceKeyword: - result = parseInterfaceDeclaration(fullStart, modifiers); - break; + return parseInterfaceDeclaration(fullStart, modifiers); case SyntaxKind.TypeKeyword: - result = parseTypeAliasDeclaration(fullStart, modifiers); - break; + return parseTypeAliasDeclaration(fullStart, modifiers); case SyntaxKind.EnumKeyword: - result = parseAndCheckEnumDeclaration(fullStart, flags); - break; + return parseAndCheckEnumDeclaration(fullStart, modifiers, flags); case SyntaxKind.ModuleKeyword: - result = parseModuleDeclaration(fullStart, flags); - break; + return parseModuleDeclaration(fullStart, modifiers, flags); case SyntaxKind.ImportKeyword: - result = parseImportDeclaration(fullStart, modifiers); - break; + return parseImportDeclaration(fullStart, modifiers); default: error(Diagnostics.Declaration_expected); } - - if (modifiers) { - result.modifiers = modifiers; - } - - return result; } function isSourceElement(inErrorRecovery: boolean): boolean { From cc1da37e7267e3a03528a3ac3b86828121510822 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 13:24:33 -0800 Subject: [PATCH 11/21] Use a single member to represent a module reference. --- src/compiler/checker.ts | 27 +++++++++--------- src/compiler/emitter.ts | 25 +++++++--------- src/compiler/parser.ts | 57 ++++++++++++++++++++++++------------- src/compiler/types.ts | 6 ++-- src/services/breakpoints.ts | 2 +- src/services/services.ts | 17 ++++++----- 6 files changed, 77 insertions(+), 57 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a1bfafcb47..31cf28946fc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -467,9 +467,9 @@ module ts { if (!links.target) { links.target = resolvingSymbol; var node = getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration); - var target = node.externalModuleName - ? resolveExternalModuleName(node, node.externalModuleName.expression) - : getSymbolOfPartOfRightHandSideOfImport(node.entityName, node); + var target = node.moduleReference.kind === SyntaxKind.ExternalModuleReference + ? resolveExternalModuleName(node, getExternalModuleImportDeclarationExpression(node)) + : getSymbolOfPartOfRightHandSideOfImport(node.moduleReference, node); if (links.target === resolvingSymbol) { links.target = target || unknownSymbol; } @@ -829,8 +829,8 @@ module ts { if (symbolFromSymbolTable.flags & SymbolFlags.Import) { if (!useOnlyExternalAliasing || // We can use any type of alias to get the name // Is this external alias, then use it to name - ts.forEach(symbolFromSymbolTable.declarations, declaration => - declaration.kind === SyntaxKind.ImportDeclaration && (declaration).externalModuleName)) { + ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportDeclaration)) { + var resolvedImportedSymbol = resolveImport(symbolFromSymbolTable); if (isAccessible(symbolFromSymbolTable, resolveImport(symbolFromSymbolTable))) { return [symbolFromSymbolTable]; @@ -8343,16 +8343,16 @@ module ts { var symbol = getSymbolOfNode(node); var target: Symbol; - if (node.entityName) { + if (isInternalModuleImportDeclaration(node)) { target = resolveImport(symbol); // Import declaration for an internal module if (target !== unknownSymbol) { if (target.flags & SymbolFlags.Value) { // Target is a value symbol, check that it is not hidden by a local declaration with the same name and // ensure it can be evaluated as an expression - var moduleName = getFirstIdentifier(node.entityName); + var moduleName = getFirstIdentifier(node.moduleReference); if (resolveEntityName(node, moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace) { - checkExpression(node.entityName); + checkExpression(node.moduleReference); } else { error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); @@ -8373,8 +8373,8 @@ module ts { // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference // other external modules only through top - level external module names. // Relative external module names are not permitted. - if (node.externalModuleName.expression.kind === SyntaxKind.StringLiteral) { - if (isExternalModuleNameRelative((node.externalModuleName.expression).text)) { + if (getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { + if (isExternalModuleNameRelative((getExternalModuleImportDeclarationExpression(node)).text)) { error(node, Diagnostics.Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name); target = unknownSymbol; } @@ -8847,7 +8847,7 @@ module ts { } if (node.parent.kind === SyntaxKind.ImportDeclaration) { - return (node.parent).entityName === node; + return (node.parent).moduleReference === node; } if (node.parent.kind === SyntaxKind.ExportAssignment) { return (node.parent).exportName === node; @@ -8958,7 +8958,8 @@ module ts { case SyntaxKind.StringLiteral: // External module name in an import declaration - if (node.parent.parent.kind === SyntaxKind.ImportDeclaration && (node.parent.parent).externalModuleName.expression === node) { + if (isExternalModuleImportDeclaration(node.parent.parent) && + getExternalModuleImportDeclarationExpression(node.parent.parent) === node) { var importSymbol = getSymbolOfNode(node.parent.parent); var moduleType = getTypeOfSymbol(importSymbol); return moduleType ? moduleType.symbol : undefined; @@ -9145,7 +9146,7 @@ module ts { } function isTopLevelValueImportWithEntityName(node: ImportDeclaration): boolean { - if (node.parent.kind !== SyntaxKind.SourceFile || !node.entityName) { + if (node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportDeclaration(node)) { // parent is not source file or it is not reference to internal module return false; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f5e5c056def..19a3355ff0a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -665,13 +665,13 @@ module ts { write("import "); writeTextOfNode(currentSourceFile, node.name); write(" = "); - if (node.entityName) { - emitTypeWithNewGetSymbolAccessibilityDiangostic(node.entityName, getImportEntityNameVisibilityError); + if (isInternalModuleImportDeclaration(node)) { + emitTypeWithNewGetSymbolAccessibilityDiangostic(node.moduleReference, getImportEntityNameVisibilityError); write(";"); } else { write("require("); - writeTextOfNode(currentSourceFile, node.externalModuleName.expression); + writeTextOfNode(currentSourceFile, getExternalModuleImportDeclarationExpression(node)); write(");"); } writer.writeLine(); @@ -3288,7 +3288,7 @@ module ts { } if (emitImportDeclaration) { - if (node.externalModuleName && node.parent.kind === SyntaxKind.SourceFile && compilerOptions.module === ModuleKind.AMD) { + if (isExternalModuleImportDeclaration(node) && node.parent.kind === SyntaxKind.SourceFile && compilerOptions.module === ModuleKind.AMD) { if (node.flags & NodeFlags.Export) { writeLine(); emitLeadingComments(node); @@ -3308,11 +3308,11 @@ module ts { if (!(node.flags & NodeFlags.Export)) write("var "); emitModuleMemberName(node); write(" = "); - if (node.entityName) { - emit(node.entityName); + if (isInternalModuleImportDeclaration(node)) { + emit(node.moduleReference); } else { - var literal = node.externalModuleName.expression; + var literal = getExternalModuleImportDeclarationExpression(node); write("require("); emitStart(literal); emitLiteral(literal); @@ -3328,12 +3328,9 @@ module ts { function getExternalImportDeclarations(node: SourceFile): ImportDeclaration[] { var result: ImportDeclaration[] = []; - forEach(node.statements, stat => { - if (stat.kind === SyntaxKind.ImportDeclaration - && (stat).externalModuleName - && resolver.isReferencedImportDeclaration(stat)) { - - result.push(stat); + forEach(node.statements, statement => { + if (isExternalModuleImportDeclaration(statement) && resolver.isReferencedImportDeclaration(statement)) { + result.push(statement); } }); return result; @@ -3357,7 +3354,7 @@ module ts { write("[\"require\", \"exports\""); forEach(imports, imp => { write(", "); - emitLiteral(imp.externalModuleName.expression); + emitLiteral(getExternalModuleImportDeclarationExpression(imp)); }); forEach(node.amdDependencies, amdDependency => { var text = "\"" + amdDependency + "\""; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6db6d2a30a7..6b130799ce7 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -382,8 +382,7 @@ module ts { child((node).body); case SyntaxKind.ImportDeclaration: return child((node).name) || - child((node).entityName) || - child((node).externalModuleName); + child((node).moduleReference); case SyntaxKind.ExportAssignment: return child((node).exportName); case SyntaxKind.TemplateExpression: @@ -591,6 +590,19 @@ module ts { return false; } + export function isExternalModuleImportDeclaration(node: Node) { + return node.kind === SyntaxKind.ImportDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; + } + + export function getExternalModuleImportDeclarationExpression(node: Node) { + Debug.assert(isExternalModuleImportDeclaration(node)); + return ((node).moduleReference).expression; + } + + export function isInternalModuleImportDeclaration(node: Node) { + return node.kind === SyntaxKind.ImportDeclaration && (node).moduleReference.kind !== SyntaxKind.ExternalModuleReference; + } + export function hasRestParameters(s: SignatureDeclaration): boolean { return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0; } @@ -3403,7 +3415,9 @@ module ts { // If we don't have a catch clause, then we must have a finally clause. Try to parse // one out no matter what. - node.finallyBlock = !node.catchBlock || token === SyntaxKind.FinallyKeyword ? parseTokenAndBlock(SyntaxKind.FinallyKeyword) : undefined; + node.finallyBlock = !node.catchBlock || token === SyntaxKind.FinallyKeyword + ? parseTokenAndBlock(SyntaxKind.FinallyKeyword) + : undefined; return finishNode(node); } @@ -3959,20 +3973,25 @@ module ts { parseExpected(SyntaxKind.ImportKeyword); node.name = parseIdentifier(); parseExpected(SyntaxKind.EqualsToken); - if (isExternalModuleReference()) { - node.externalModuleName = parseExternalModuleReference(); - } - else { - node.entityName = parseEntityName(/*allowReservedWords*/ false); - } + node.moduleReference = parseModuleReference(); parseSemicolon(); return finishNode(node); } + function parseModuleReference() { + return isExternalModuleReference() + ? parseExternalModuleReference() + : parseEntityName(/*allowReservedWords*/ false); + } + function parseExternalModuleReference() { var node = createNode(SyntaxKind.ExternalModuleReference); parseExpected(SyntaxKind.RequireKeyword); parseExpected(SyntaxKind.OpenParenToken); + + // We allow arbitrary expressions here, even though the grammar only allows string + // literals. We check to ensure that it is only a string literal later in the grammar + // walker. node.expression = parseExpression(); if (node.expression.kind === SyntaxKind.StringLiteral) { internIdentifier((node.expression).text); @@ -4129,7 +4148,7 @@ module ts { function getExternalModuleIndicator() { return forEach(file.statements, node => node.flags & NodeFlags.Export - || node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName + || node.kind === SyntaxKind.ImportDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference || node.kind === SyntaxKind.ExportAssignment ? node : undefined); @@ -4906,8 +4925,8 @@ module ts { // Export assignments are not allowed in an internal module return grammarErrorOnNode(statement, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); } - else if (statement.kind === SyntaxKind.ImportDeclaration && (statement).externalModuleName) { - return grammarErrorOnNode((statement).externalModuleName.expression, Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); + else if (isExternalModuleImportDeclaration(statement)) { + return grammarErrorOnNode(getExternalModuleImportDeclarationExpression(statement), Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); } } } @@ -5629,11 +5648,10 @@ module ts { function processImportedModules(file: SourceFile, basePath: string) { forEach(file.statements, node => { - if (node.kind === SyntaxKind.ImportDeclaration && - (node).externalModuleName && - (node).externalModuleName.expression.kind === SyntaxKind.StringLiteral) { + if (isExternalModuleImportDeclaration(node) && + getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { - var nameLiteral = (node).externalModuleName.expression; + var nameLiteral = getExternalModuleImportDeclarationExpression(node); var moduleName = nameLiteral.text; if (moduleName) { var searchPath = basePath; @@ -5658,11 +5676,10 @@ module ts { // The StringLiteral must specify a top - level external module name. // Relative external module names are not permitted forEachChild((node).body, node => { - if (node.kind === SyntaxKind.ImportDeclaration && - (node).externalModuleName && - (node).externalModuleName.expression.kind === SyntaxKind.StringLiteral) { + if (isExternalModuleImportDeclaration(node) && + getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { - var nameLiteral = (node).externalModuleName.expression; + var nameLiteral = getExternalModuleImportDeclarationExpression(node); var moduleName = nameLiteral.text; if (moduleName) { // TypeScript 1.0 spec (April 2014): 12.1.6 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0a636b94b06..19bc8154b7b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -731,8 +731,10 @@ module ts { export interface ImportDeclaration extends Declaration, ModuleElement { name: Identifier; - entityName?: EntityName; - externalModuleName?: ExternalModuleReference; + + // 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external + // module reference. + moduleReference: EntityName | ExternalModuleReference; } export interface ExternalModuleReference extends Node { diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index d4aa5e40f39..6cf775487fa 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -174,7 +174,7 @@ module ts.BreakpointResolver { case SyntaxKind.ImportDeclaration: // import statement without including semicolon - return textSpan(node, (node).entityName || (node).externalModuleName); + return textSpan(node,(node).moduleReference); case SyntaxKind.ModuleDeclaration: // span on complete module if it is instantiated diff --git a/src/services/services.ts b/src/services/services.ts index a4569d88791..e3012f42846 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1980,9 +1980,12 @@ module ts { } function isNameOfExternalModuleImportOrDeclaration(node: Node): boolean { - return node.kind === SyntaxKind.StringLiteral && - (isNameOfModuleDeclaration(node) || - (node.parent.parent.kind === SyntaxKind.ImportDeclaration && (node.parent.parent).externalModuleName.expression === node)); + if (node.kind === SyntaxKind.StringLiteral) { + return isNameOfModuleDeclaration(node) || + (isExternalModuleImportDeclaration(node.parent.parent) && getExternalModuleImportDeclarationExpression(node.parent.parent) === node); + } + + return false; } /** Returns true if the position is within a comment */ @@ -3080,17 +3083,17 @@ module ts { ts.forEach(symbol.declarations, declaration => { if (declaration.kind === SyntaxKind.ImportDeclaration) { var importDeclaration = declaration; - if (importDeclaration.externalModuleName) { + if (isExternalModuleImportDeclaration(importDeclaration)) { displayParts.push(spacePart()); displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); displayParts.push(spacePart()); displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - displayParts.push(displayPart(getTextOfNode(importDeclaration.externalModuleName.expression), SymbolDisplayPartKind.stringLiteral)); + displayParts.push(displayPart(getTextOfNode(getExternalModuleImportDeclarationExpression(importDeclaration)), SymbolDisplayPartKind.stringLiteral)); displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } else { - var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.entityName); + var internalAliasSymbol = typeResolver.getSymbolInfo(importDeclaration.moduleReference); if (internalAliasSymbol) { displayParts.push(spacePart()); displayParts.push(punctuationPart(SyntaxKind.EqualsToken)); @@ -4814,7 +4817,7 @@ module ts { while (node.parent.kind === SyntaxKind.QualifiedName) { node = node.parent; } - return node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).entityName === node; + return isInternalModuleImportDeclaration(node.parent) && (node.parent).moduleReference === node; } function getMeaningFromRightHandSideOfImport(node: Node) { From 1a39d09939f2fc8d42c19a538690ab8d99f1814f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 13:42:27 -0800 Subject: [PATCH 12/21] Treat 'const' as a modifier if followed by 'enum'. --- src/compiler/parser.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6b130799ce7..ce5287482b1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -896,6 +896,7 @@ module ts { case SyntaxKind.StaticKeyword: case SyntaxKind.ExportKeyword: case SyntaxKind.DeclareKeyword: + case SyntaxKind.ConstKeyword: return true; } return false; @@ -909,6 +910,7 @@ module ts { case SyntaxKind.PrivateKeyword: return NodeFlags.Private; case SyntaxKind.ExportKeyword: return NodeFlags.Export; case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; + case SyntaxKind.ConstKeyword: return NodeFlags.Const; } return 0; } @@ -1366,6 +1368,11 @@ module ts { function parseAnyContextualModifier(): boolean { return isModifier(token) && tryParse(() => { + if (token === SyntaxKind.ConstKeyword) { + // 'const' is only a modifier if followed by 'enum'. + return nextToken() === SyntaxKind.EnumKeyword; + } + nextToken(); return canFollowModifier(); }); @@ -3900,11 +3907,6 @@ module ts { function parseAndCheckEnumDeclaration(fullStart: number, modifiers: ModifiersArray, flags: NodeFlags): EnumDeclaration { var node = createNode(SyntaxKind.EnumDeclaration, fullStart); setModifiers(node, modifiers); - - node.flags |= flags; - if (flags & NodeFlags.Const) { - parseExpected(SyntaxKind.ConstKeyword); - } parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -4054,13 +4056,7 @@ module ts { case SyntaxKind.LetKeyword: return parseVariableStatement(fullStart, modifiers); case SyntaxKind.ConstKeyword: - var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); - if (isConstEnum) { - return parseAndCheckEnumDeclaration(fullStart, modifiers, flags | NodeFlags.Const); - } - else { - return parseVariableStatement(fullStart, modifiers); - } + return parseVariableStatement(fullStart, modifiers); case SyntaxKind.FunctionKeyword: return parseFunctionDeclaration(fullStart, modifiers); case SyntaxKind.ClassKeyword: From 3a6dba58789468255e47d6df61653d6d72f1d756 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 13:46:44 -0800 Subject: [PATCH 13/21] Replace unnecessary case in switch with assert. --- src/compiler/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ce5287482b1..ed98cdfdf40 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4072,7 +4072,7 @@ module ts { case SyntaxKind.ImportKeyword: return parseImportDeclaration(fullStart, modifiers); default: - error(Diagnostics.Declaration_expected); + Debug.fail("Mismatch between isDeclarationStart and parseDeclaration"); } } From eee6491521083927c55176c2df3a2261eefefdd7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 14:14:39 -0800 Subject: [PATCH 14/21] Don't create a missing node for "foo[]". We want to remove 'missing' nodes, and we also want to improve increment reuse (which missing nodes interfere with). --- src/compiler/checker.ts | 28 +++++++++++++++++----------- src/compiler/parser.ts | 5 +---- src/compiler/types.ts | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 31cf28946fc..431740af125 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5246,11 +5246,13 @@ module ts { function checkIndexedAccess(node: ElementAccessExpression): Type { // Obtain base constraint such that we can bail out if the constraint is an unknown type var objectType = getApparentType(checkExpression(node.expression)); - var indexType = checkExpression(node.argumentExpression); + var indexType = node.argumentExpression ? checkExpression(node.argumentExpression) : unknownType; - if (objectType === unknownType) return unknownType; + if (objectType === unknownType) { + return unknownType; + } - if (isConstEnumObjectType(objectType) && node.argumentExpression.kind !== SyntaxKind.StringLiteral) { + if (isConstEnumObjectType(objectType) && node.argumentExpression && node.argumentExpression.kind !== SyntaxKind.StringLiteral) { error(node.argumentExpression, Diagnostics.Index_expression_arguments_in_const_enums_must_be_of_type_string); } @@ -5264,12 +5266,14 @@ module ts { // - Otherwise, if IndexExpr is of type Any, the String or Number primitive type, or an enum type, the property access is of type Any. // See if we can index as a property. - if (node.argumentExpression.kind === SyntaxKind.StringLiteral || node.argumentExpression.kind === SyntaxKind.NumericLiteral) { - var name = (node.argumentExpression).text; - var prop = getPropertyOfType(objectType, name); - if (prop) { - getNodeLinks(node).resolvedSymbol = prop; - return getTypeOfSymbol(prop); + if (node.argumentExpression) { + if (node.argumentExpression.kind === SyntaxKind.StringLiteral || node.argumentExpression.kind === SyntaxKind.NumericLiteral) { + var name = (node.argumentExpression).text; + var prop = getPropertyOfType(objectType, name); + if (prop) { + getNodeLinks(node).resolvedSymbol = prop; + return getTypeOfSymbol(prop); + } } } @@ -6225,7 +6229,8 @@ module ts { case SyntaxKind.ElementAccessExpression: var index = (n).argumentExpression; var symbol = findSymbol((n).expression); - if (symbol && index.kind === SyntaxKind.StringLiteral) { + + if (symbol && index && index.kind === SyntaxKind.StringLiteral) { var name = (index).text; var prop = getPropertyOfType(getTypeOfSymbol(symbol), name); return prop && (prop.flags & SymbolFlags.Variable) !== 0 && (getDeclarationFlagsFromSymbol(prop) & NodeFlags.Const) !== 0; @@ -8196,7 +8201,8 @@ module ts { } else { if (e.kind === SyntaxKind.ElementAccessExpression) { - if ((e).argumentExpression.kind !== SyntaxKind.StringLiteral) { + if ((e).argumentExpression === undefined || + (e).argumentExpression.kind !== SyntaxKind.StringLiteral) { return undefined; } var enumType = getTypeOfNode((e).expression); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ed98cdfdf40..499b2e44f0a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2943,9 +2943,6 @@ module ts { literal.text = internIdentifier(literal.text); } } - else { - indexedAccess.argumentExpression = createMissingNode(); - } parseExpected(SyntaxKind.CloseBracketToken); expression = finishNode(indexedAccess); @@ -4769,7 +4766,7 @@ module ts { } function checkElementAccessExpression(node: ElementAccessExpression) { - if (node.argumentExpression.kind === SyntaxKind.Missing) { + if (!node.argumentExpression) { if (node.parent.kind === SyntaxKind.NewExpression && (node.parent).expression === node) { var start = skipTrivia(sourceText, node.expression.end); var end = node.end; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 19bc8154b7b..bc7ca28e515 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -563,7 +563,7 @@ module ts { export interface ElementAccessExpression extends MemberExpression { expression: LeftHandSideExpression; - argumentExpression: Expression; + argumentExpression?: Expression; } export interface CallExpression extends LeftHandSideExpression { From 284e136f2adea8ba976943e948e864782456aee5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 14:23:08 -0800 Subject: [PATCH 15/21] Remove unused function. --- src/compiler/parser.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 499b2e44f0a..97f81fbd95f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1674,15 +1674,6 @@ module ts { return finishNode(node); } - function parseExpectedTokenNode(kind: SyntaxKind): Node { - if (token === kind) { - return parseTokenNode(); - } - - parseExpected(kind); - return createMissingNode(); - } - function parseTemplateExpression(): TemplateExpression { var template = createNode(SyntaxKind.TemplateExpression); From af7b8d624f5ebd91f24426b5a7b62bef6438c66c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 14:44:58 -0800 Subject: [PATCH 16/21] Don't explicitly create a missing node when parsing arrow expressions. --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 97f81fbd95f..d0954a7f0d8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2475,7 +2475,7 @@ module ts { if (triState === Tristate.True) { // Arrow function are never generators. - var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, /*yieldAndGeneratorParameterContext:*/ false); + var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /*returnTokenRequired:*/ false, /*yieldAndGeneratorParameterContext:*/ false); // If we have an arrow, then try to parse the body. // Even if not, try to parse if we have an opening brace, just in case we're in an error state. @@ -2484,7 +2484,7 @@ module ts { } else { // If not, we're probably better off bailing out and returning a bogus function expression. - return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /*asteriskToken:*/ undefined, /*name:*/ undefined, sig, createMissingNode()); + return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /*asteriskToken:*/ undefined, /*name:*/ undefined, sig, parseIdentifier(Diagnostics.Expression_expected)); } } From d730e5ca5594ac2d9c8c1ff71965093ba91e2f67 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 16:17:04 -0800 Subject: [PATCH 17/21] Remove 'missing' syntax kind. --- src/compiler/checker.ts | 33 +++++----- src/compiler/parser.ts | 61 ++++++++++++------- src/compiler/types.ts | 7 ++- src/services/formatting.ts | 4 +- src/services/services.ts | 20 +++--- src/services/signatureHelp.ts | 2 +- src/services/smartIndenter.ts | 6 +- ...umConflictsWithGlobalIdentifier.errors.txt | 6 +- .../reference/enumMemberResolution.errors.txt | 6 +- ...parserPostfixPostfixExpression1.errors.txt | 7 +-- .../parserPostfixUnaryExpression1.errors.txt | 7 +-- 11 files changed, 91 insertions(+), 68 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 431740af125..dad791fe8ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -338,7 +338,6 @@ module ts { // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with // the given name can be found. function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { - var result: Symbol; var lastLocation: Node; var propertyWithInvalidInitializer: Node; @@ -516,26 +515,27 @@ module ts { // Resolves a qualified name and any involved import aliases function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol { + if (getFullWidth(name) === 0) { + return undefined; + } + if (name.kind === SyntaxKind.Identifier) { - var symbol = resolveName(location, (name).text, meaning, Diagnostics.Cannot_find_name_0, name); + var symbol = resolveName(location,(name).text, meaning, Diagnostics.Cannot_find_name_0, name); if (!symbol) { return; } } else if (name.kind === SyntaxKind.QualifiedName) { - var namespace = resolveEntityName(location, (name).left, SymbolFlags.Namespace); - if (!namespace || namespace === unknownSymbol || (name).right.kind === SyntaxKind.Missing) return; - var symbol = getSymbol(namespace.exports, (name).right.text, meaning); + var namespace = resolveEntityName(location,(name).left, SymbolFlags.Namespace); + if (!namespace || namespace === unknownSymbol || getFullWidth((name).right) === 0) return; + var symbol = getSymbol(namespace.exports,(name).right.text, meaning); if (!symbol) { error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString((name).right)); return; } } - else { - // Missing identifier - return; - } + Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); return symbol.flags & meaning ? symbol : resolveImport(symbol); } @@ -4256,7 +4256,7 @@ module ts { function getResolvedSymbol(node: Identifier): Symbol { var links = getNodeLinks(node); if (!links.resolvedSymbol) { - links.resolvedSymbol = resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node) || unknownSymbol; + links.resolvedSymbol = (getFullWidth(node) > 0 && resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node)) || unknownSymbol; } return links.resolvedSymbol; } @@ -5344,7 +5344,7 @@ module ts { var templateExpression = tagExpression.template; var lastSpan = lastOrUndefined(templateExpression.templateSpans); Debug.assert(lastSpan !== undefined); // we should always have at least one span. - callIsIncomplete = lastSpan.literal.kind === SyntaxKind.Missing || isUnterminatedTemplateEnd(lastSpan.literal); + callIsIncomplete = getFullWidth(lastSpan.literal) === 0 || isUnterminatedTemplateEnd(lastSpan.literal); } else { // If the template didn't end in a backtick, or its beginning occurred right prior to EOF, @@ -7051,7 +7051,7 @@ module ts { var isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0; function reportImplementationExpectedError(node: FunctionLikeDeclaration): void { - if (node.name && node.name.kind === SyntaxKind.Missing) { + if (node.name && getFullWidth(node.name) === 0) { return; } @@ -8889,6 +8889,11 @@ module ts { } if (isExpression(entityName)) { + if (getFullWidth(entityName) === 0) { + // Missing entity name. + return undefined; + } + if (entityName.kind === SyntaxKind.Identifier) { // Include Import in the meaning, this ensures that we do not follow aliases to where they point and instead // return the alias symbol. @@ -8909,10 +8914,6 @@ module ts { } return getNodeLinks(entityName).resolvedSymbol; } - else { - // Missing identifier - return; - } } else if (isTypeReferenceIdentifier(entityName)) { var meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d0954a7f0d8..e6f57c6e7f3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5,6 +5,10 @@ module ts { var nodeConstructors = new Array Node>(SyntaxKind.Count); + export function getFullWidth(node: Node) { + return node.end - node.pos; + } + export function getNodeConstructor(kind: SyntaxKind): new () => Node { return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)); } @@ -75,13 +79,14 @@ module ts { // Computed property names will just be emitted as "[]", where is the source // text of the expression in the computed property. export function declarationNameToString(name: DeclarationName) { - return name.kind === SyntaxKind.Missing ? "(Missing)" : getTextOfNode(name); + return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): Diagnostic { node = getErrorSpanForNode(node); var file = getSourceFileOfNode(node); - var start = node.kind === SyntaxKind.Missing ? node.pos : skipTrivia(file.text, node.pos); + + var start = getFullWidth(node) === 0 ? node.pos : skipTrivia(file.text, node.pos); var length = node.end - start; return createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); @@ -1200,14 +1205,14 @@ module ts { return inStrictModeContext() ? token > SyntaxKind.LastFutureReservedWord : token > SyntaxKind.LastReservedWord; } - function parseExpected(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage): boolean { + function parseExpected(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): boolean { if (token === t) { nextToken(); return true; } if (diagnosticMessage) { - error(diagnosticMessage); + error(diagnosticMessage, arg0); } else { error(Diagnostics._0_expected, tokenToString(t)); @@ -1276,8 +1281,24 @@ module ts { return node; } - function createMissingNode(pos?: number): Node { - return createNode(SyntaxKind.Missing, pos); + function createMissingNode(kind: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node { + if (diagnosticMessage) { + if (reportAtCurrentPosition) { + errorAtPos(scanner.getStartPos(), 0, diagnosticMessage, arg0); + } + else { + var start = scanner.getTokenPos(); + errorAtPos(start, scanner.getTextPos() - start, diagnosticMessage, arg0); + } + } + + return createMissingNodeWithoutError(kind); + } + + function createMissingNodeWithoutError(kind: SyntaxKind) { + var result = createNode(kind, scanner.getStartPos()); + (result).text = ""; + return finishNode(result); } function internIdentifier(text: string): string { @@ -1297,14 +1318,7 @@ module ts { return finishNode(node); } - error(diagnosticMessage || Diagnostics.Identifier_expected); - return createMissingIdentifier(); - } - - function createMissingIdentifier() { - var node = createMissingNode(); - node.text = ""; - return node; + return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition:*/ false, diagnosticMessage || Diagnostics.Identifier_expected); } function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier { @@ -1660,8 +1674,10 @@ module ts { }); if (matchesPattern) { - errorAtPos(scanner.getTokenPos(), 0, Diagnostics.Identifier_expected); - return createMissingNode(); + // Report that we need an identifier. However, report it right after the dot, + // and not on the next token. This is because the next token might actually + // be an identifier and the error woudl be quite confusing. + return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentToken:*/ true, Diagnostics.Identifier_expected); } } @@ -1705,8 +1721,8 @@ module ts { literal = parseLiteralNode(); } else { - parseExpected(SyntaxKind.CloseBraceToken); - literal = createMissingIdentifier(); + literal = createMissingNode( + SyntaxKind.TemplateTail, /*reportAtCurrentPosition:*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken)); } span.literal = literal; @@ -1821,7 +1837,7 @@ module ts { ? doInYieldContext(parseIdentifier) : parseIdentifier(); - if (node.name.kind === SyntaxKind.Missing && node.flags === 0 && isModifier(token)) { + if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifier(token)) { // in cases like // 'use strict' // function foo(static) @@ -3009,7 +3025,9 @@ module ts { // don't want to rollback just because we were missing a type arg. The grammar checker // will report the actual error later on. if (token === SyntaxKind.CommaToken) { - return createNode(SyntaxKind.Missing); + var result = createNode(SyntaxKind.TypeReference); + result.typeName = createMissingNodeWithoutError(SyntaxKind.Identifier); + return finishNode(result); } return parseType(); @@ -4202,7 +4220,6 @@ module ts { case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.Identifier: - case SyntaxKind.Missing: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: @@ -4533,7 +4550,7 @@ module ts { if (typeArguments) { for (var i = 0, n = typeArguments.length; i < n; i++) { var arg = typeArguments[i]; - if (arg.kind === SyntaxKind.Missing) { + if (arg.kind === SyntaxKind.TypeReference && getFullWidth((arg).typeName) === 0) { return grammarErrorAtPos(arg.pos, 0, Diagnostics.Type_expected); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bc7ca28e515..f4fc13f76d9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -137,8 +137,9 @@ module ts { SetKeyword, StringKeyword, TypeKeyword, + // Parse tree nodes - Missing, + // Names QualifiedName, ComputedPropertyName, @@ -235,6 +236,7 @@ module ts { // Top-level nodes SourceFile, Program, + // Synthesized list SyntaxList, // Enum value count @@ -263,7 +265,8 @@ module ts { FirstOperator = SemicolonToken, LastOperator = CaretEqualsToken, FirstBinaryOperator = LessThanToken, - LastBinaryOperator = CaretEqualsToken + LastBinaryOperator = CaretEqualsToken, + FirstNode = QualifiedName, } export const enum NodeFlags { diff --git a/src/services/formatting.ts b/src/services/formatting.ts index fc64329c3e0..d986048cac3 100644 --- a/src/services/formatting.ts +++ b/src/services/formatting.ts @@ -483,8 +483,8 @@ module ts.formatting { if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { return inheritedIndentation; } - - if (child.kind === SyntaxKind.Missing) { + + if (child.getFullWidth() === 0) { return inheritedIndentation; } diff --git a/src/services/services.ts b/src/services/services.ts index e3012f42846..df762437e1e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -218,7 +218,7 @@ module ts { } private createChildren(sourceFile?: SourceFile) { - if (this.kind > SyntaxKind.Missing) { + if (this.kind >= SyntaxKind.FirstNode) { scanner.setText((sourceFile || this.getSourceFile()).text); var children: Node[] = []; var pos = this.pos; @@ -264,8 +264,11 @@ module ts { var children = this.getChildren(); for (var i = 0; i < children.length; i++) { var child = children[i]; - if (child.kind < SyntaxKind.Missing) return child; - if (child.kind > SyntaxKind.Missing) return child.getFirstToken(sourceFile); + if (child.kind < SyntaxKind.FirstNode) { + return child; + } + + return child.getFirstToken(sourceFile); } } @@ -273,8 +276,11 @@ module ts { var children = this.getChildren(sourceFile); for (var i = children.length - 1; i >= 0; i--) { var child = children[i]; - if (child.kind < SyntaxKind.Missing) return child; - if (child.kind > SyntaxKind.Missing) return child.getLastToken(sourceFile); + if (child.kind < SyntaxKind.FirstNode) { + return child; + } + + return child.getLastToken(sourceFile); } } } @@ -758,7 +764,7 @@ module ts { case SyntaxKind.Method: var functionDeclaration = node; - if (functionDeclaration.name && functionDeclaration.name.kind !== SyntaxKind.Missing) { + if (functionDeclaration.name && functionDeclaration.name.getFullWidth() > 0) { var lastDeclaration = namedDeclarations.length > 0 ? namedDeclarations[namedDeclarations.length - 1] : undefined; @@ -2888,7 +2894,7 @@ module ts { if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { var right = (location.parent).name; // Either the location is on the right of a property access, or on the left and the right is missing - if (right === location || (right && right.kind === SyntaxKind.Missing)){ + if (right === location || (right && right.getFullWidth() === 0)){ location = location.parent; } } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index e2b65e3c6e1..b8cb181a287 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -386,7 +386,7 @@ module ts.SignatureHelp { // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. if (template.kind === SyntaxKind.TemplateExpression) { var lastSpan = lastOrUndefined((template).templateSpans); - if (lastSpan.literal.kind === SyntaxKind.Missing) { + if (lastSpan.literal.getFullWidth() === 0) { applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); } } diff --git a/src/services/smartIndenter.ts b/src/services/smartIndenter.ts index 3fbfe142282..d13d2a62c68 100644 --- a/src/services/smartIndenter.ts +++ b/src/services/smartIndenter.ts @@ -393,6 +393,10 @@ module ts.formatting { * This function is always called when position of the cursor is located after the node */ function isCompletedNode(n: Node, sourceFile: SourceFile): boolean { + if (n.getFullWidth() === 0) { + return false; + } + switch (n.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: @@ -426,8 +430,6 @@ module ts.formatting { return isCompletedNode((n).expression, sourceFile); case SyntaxKind.ArrayLiteralExpression: return nodeEndsWith(n, SyntaxKind.CloseBracketToken, sourceFile); - case SyntaxKind.Missing: - return false; case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: // there is no such thing as terminator token for CaseClause\DefaultClause so for simplicitly always consider them non-completed diff --git a/tests/baselines/reference/enumConflictsWithGlobalIdentifier.errors.txt b/tests/baselines/reference/enumConflictsWithGlobalIdentifier.errors.txt index b91ddad3eb4..e6859558f74 100644 --- a/tests/baselines/reference/enumConflictsWithGlobalIdentifier.errors.txt +++ b/tests/baselines/reference/enumConflictsWithGlobalIdentifier.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/enumConflictsWithGlobalIdentifier.ts(5,1): error TS1003: Identifier expected. +tests/cases/compiler/enumConflictsWithGlobalIdentifier.ts(4,29): error TS1003: Identifier expected. tests/cases/compiler/enumConflictsWithGlobalIdentifier.ts(4,9): error TS2304: Cannot find name 'IgnoreRulesSpecific'. @@ -7,9 +7,9 @@ tests/cases/compiler/enumConflictsWithGlobalIdentifier.ts(4,9): error TS2304: Ca IgnoreRulesSpecific = 0, } var x = IgnoreRulesSpecific. + +!!! error TS1003: Identifier expected. ~~~~~~~~~~~~~~~~~~~ !!! error TS2304: Cannot find name 'IgnoreRulesSpecific'. var y = Position.IgnoreRulesSpecific; - -!!! error TS1003: Identifier expected. \ No newline at end of file diff --git a/tests/baselines/reference/enumMemberResolution.errors.txt b/tests/baselines/reference/enumMemberResolution.errors.txt index 18910485236..f446c0d7909 100644 --- a/tests/baselines/reference/enumMemberResolution.errors.txt +++ b/tests/baselines/reference/enumMemberResolution.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/enumMemberResolution.ts(5,1): error TS1003: Identifier expected. +tests/cases/compiler/enumMemberResolution.ts(4,29): error TS1003: Identifier expected. tests/cases/compiler/enumMemberResolution.ts(4,9): error TS2304: Cannot find name 'IgnoreRulesSpecific'. @@ -7,10 +7,10 @@ tests/cases/compiler/enumMemberResolution.ts(4,9): error TS2304: Cannot find nam IgnoreRulesSpecific = 0 } var x = IgnoreRulesSpecific. // error + +!!! error TS1003: Identifier expected. ~~~~~~~~~~~~~~~~~~~ !!! error TS2304: Cannot find name 'IgnoreRulesSpecific'. var y = 1; - -!!! error TS1003: Identifier expected. var z = Position2.IgnoreRulesSpecific; // no error \ No newline at end of file diff --git a/tests/baselines/reference/parserPostfixPostfixExpression1.errors.txt b/tests/baselines/reference/parserPostfixPostfixExpression1.errors.txt index ad664b91ab5..07c7352bbfc 100644 --- a/tests/baselines/reference/parserPostfixPostfixExpression1.errors.txt +++ b/tests/baselines/reference/parserPostfixPostfixExpression1.errors.txt @@ -1,16 +1,13 @@ tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts(1,5): error TS1005: ';' expected. tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts(1,7): error TS1109: Expression expected. tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts(1,1): error TS2304: Cannot find name 'a'. -tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts(1,7): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. -==== tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts (4 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixPostfixExpression1.ts (3 errors) ==== a++ ++; ~~ !!! error TS1005: ';' expected. ~ !!! error TS1109: Expression expected. ~ -!!! error TS2304: Cannot find name 'a'. - -!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. \ No newline at end of file +!!! error TS2304: Cannot find name 'a'. \ No newline at end of file diff --git a/tests/baselines/reference/parserPostfixUnaryExpression1.errors.txt b/tests/baselines/reference/parserPostfixUnaryExpression1.errors.txt index 1e1f4ea709a..364efe36a73 100644 --- a/tests/baselines/reference/parserPostfixUnaryExpression1.errors.txt +++ b/tests/baselines/reference/parserPostfixUnaryExpression1.errors.txt @@ -1,16 +1,13 @@ tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts(1,8): error TS1005: ';' expected. tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts(1,10): error TS1109: Expression expected. tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts(1,1): error TS2304: Cannot find name 'foo'. -tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts(1,10): error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. -==== tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts (4 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/Expressions/parserPostfixUnaryExpression1.ts (3 errors) ==== foo ++ ++; ~~ !!! error TS1005: ';' expected. ~ !!! error TS1109: Expression expected. ~~~ -!!! error TS2304: Cannot find name 'foo'. - -!!! error TS2357: The operand of an increment or decrement operator must be a variable, property or indexer. \ No newline at end of file +!!! error TS2304: Cannot find name 'foo'. \ No newline at end of file From 1633e4abddcc3dd6ba92963e41cd250c26aeb423 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 16:26:34 -0800 Subject: [PATCH 18/21] Remove unnecessary diagnostic flag. --- src/compiler/parser.ts | 2 +- src/compiler/types.ts | 6 ------ src/services/formatting.ts | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e6f57c6e7f3..44d44995937 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1092,7 +1092,6 @@ module ts { : -1; if (start !== lastErrorPos) { var diagnostic = createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); - diagnostic.isParseError = true; file.parseDiagnostics.push(diagnostic); } @@ -4190,6 +4189,7 @@ module ts { file.parseDiagnostics = []; file.grammarDiagnostics = []; file.semanticDiagnostics = []; + var referenceComments = processReferenceComments(); file.referencedFiles = referenceComments.referencedFiles; file.amdDependencies = referenceComments.amdDependencies; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f4fc13f76d9..6f737083efc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1277,12 +1277,6 @@ module ts { * Early error - any error (can be produced at parsing\binding\typechecking step) that blocks emit */ isEarly?: boolean; - /** - * Parse error - error produced by parser when it scanner returns a token - * that parser does not understand in its current state - * (as opposed to grammar error when parser can interpret the token but interpretation is not legal from the grammar perespective) - */ - isParseError?: boolean; } export enum DiagnosticCategory { diff --git a/src/services/formatting.ts b/src/services/formatting.ts index d986048cac3..741b853b7ba 100644 --- a/src/services/formatting.ts +++ b/src/services/formatting.ts @@ -192,7 +192,7 @@ module ts.formatting { // pick only errors that fall in range var sorted = errors - .filter(d => d.isParseError && rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length)) + .filter(d => rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length)) .sort((e1, e2) => e1.start - e2.start); if (!sorted.length) { @@ -252,7 +252,7 @@ module ts.formatting { rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] { - var rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.getSyntacticDiagnostics(), originalRange); + var rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange); // formatting context is used by rules provider var formattingContext = new FormattingContext(sourceFile, requestKind); From 9b54a0b54f648076b01623c8479d60bac3f64356 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 16:34:03 -0800 Subject: [PATCH 19/21] Don't use the parsing error path for reporting ///reference errors. --- src/compiler/parser.ts | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 44d44995937..0d1806adca4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -828,7 +828,7 @@ module ts { export interface ReferencePathMatchResult { fileReference?: FileReference - diagnostic?: DiagnosticMessage + diagnosticMessage?: DiagnosticMessage isNoDefaultLib?: boolean } @@ -857,7 +857,7 @@ module ts { } else { return { - diagnostic: Diagnostics.Invalid_reference_directive_syntax, + diagnosticMessage: Diagnostics.Invalid_reference_directive_syntax, isNoDefaultLib: false }; } @@ -1083,14 +1083,16 @@ module ts { var start = scanner.getTokenPos(); var length = scanner.getTextPos() - start; - errorAtPos(start, length, message, arg0, arg1, arg2); + errorAtPosition(start, length, message, arg0, arg1, arg2); } - function errorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { - var lastErrorPos = file.parseDiagnostics.length + function errorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { + var lastErrorPosition = file.parseDiagnostics.length ? file.parseDiagnostics[file.parseDiagnostics.length - 1].start : -1; - if (start !== lastErrorPos) { + + // Don't report another error if it would just be at the same position as the last error. + if (start !== lastErrorPosition) { var diagnostic = createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); file.parseDiagnostics.push(diagnostic); } @@ -1102,7 +1104,7 @@ module ts { function scanError(message: DiagnosticMessage) { var pos = scanner.getTextPos(); - errorAtPos(pos, 0, message); + errorAtPosition(pos, 0, message); } function onComment(pos: number, end: number) { @@ -1204,17 +1206,18 @@ module ts { return inStrictModeContext() ? token > SyntaxKind.LastFutureReservedWord : token > SyntaxKind.LastReservedWord; } - function parseExpected(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): boolean { - if (token === t) { + function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): boolean { + if (token === kind) { nextToken(); return true; } + // Report specific message if provided with one. Otherwise, report generic fallback message. if (diagnosticMessage) { error(diagnosticMessage, arg0); } else { - error(Diagnostics._0_expected, tokenToString(t)); + error(Diagnostics._0_expected, tokenToString(kind)); } return false; } @@ -1281,16 +1284,13 @@ module ts { } function createMissingNode(kind: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node { - if (diagnosticMessage) { - if (reportAtCurrentPosition) { - errorAtPos(scanner.getStartPos(), 0, diagnosticMessage, arg0); - } - else { - var start = scanner.getTokenPos(); - errorAtPos(start, scanner.getTextPos() - start, diagnosticMessage, arg0); - } + if (reportAtCurrentPosition) { + errorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0); } - + else { + error(diagnosticMessage, arg0); + } + return createMissingNodeWithoutError(kind); } @@ -4113,12 +4113,12 @@ module ts { if (referencePathMatchResult) { var fileReference = referencePathMatchResult.fileReference; file.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib; - var diagnostic = referencePathMatchResult.diagnostic; + var diagnosticMessage = referencePathMatchResult.diagnosticMessage; if (fileReference) { referencedFiles.push(fileReference); } - if (diagnostic) { - errorAtPos(range.pos, range.end - range.pos, diagnostic); + if (diagnosticMessage) { + file.parseDiagnostics.push(createFileDiagnostic(file, range.pos, range.end - range.pos, diagnosticMessage)); } } else { @@ -4126,7 +4126,7 @@ module ts { var amdModuleNameMatchResult = amdModuleNameRegEx.exec(comment); if(amdModuleNameMatchResult) { if(amdModuleName) { - errorAtPos(range.pos, range.end - range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments); + file.parseDiagnostics.push(createFileDiagnostic(file, range.pos, range.end - range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments)); } amdModuleName = amdModuleNameMatchResult[2]; } From ae3b03022380ffc746e2ddb031a407e486b2b9dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 16:39:22 -0800 Subject: [PATCH 20/21] Don't set kinds after the fact. --- src/compiler/parser.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0d1806adca4..8348d7d802b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3209,8 +3209,8 @@ module ts { } // STATEMENTS - function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block { - var node = createNode(SyntaxKind.Block); + function parseBlock(kind: SyntaxKind, ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block { + var node = createNode(kind); if (parseExpected(SyntaxKind.OpenBraceToken) || ignoreMissingOpenBrace) { node.statements = parseList(ParsingContext.BlockStatements, checkForStrictMode, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); @@ -3225,8 +3225,7 @@ module ts { var savedYieldContext = inYieldContext(); setYieldContext(allowYield); - var block = parseBlock(ignoreMissingOpenBrace, /*checkForStrictMode*/ true); - block.kind = SyntaxKind.FunctionBlock; + var block = parseBlock(SyntaxKind.FunctionBlock, ignoreMissingOpenBrace, /*checkForStrictMode*/ true); setYieldContext(savedYieldContext); @@ -3436,8 +3435,9 @@ module ts { function parseTokenAndBlock(token: SyntaxKind): Block { var pos = getNodePos(); parseExpected(token); - var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); - result.kind = token === SyntaxKind.TryKeyword ? SyntaxKind.TryBlock : SyntaxKind.FinallyBlock; + var result = parseBlock( + token === SyntaxKind.TryKeyword ? SyntaxKind.TryBlock : SyntaxKind.FinallyBlock, + /* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); result.pos = pos; return result; } @@ -3449,8 +3449,7 @@ module ts { var variable = parseIdentifier(); var typeAnnotation = parseTypeAnnotation(); parseExpected(SyntaxKind.CloseParenToken); - var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); - result.kind = SyntaxKind.CatchBlock; + var result = parseBlock(SyntaxKind.CatchBlock, /* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); result.pos = pos; result.variable = variable; result.type = typeAnnotation; @@ -3549,7 +3548,7 @@ module ts { function parseStatement(): Statement { switch (token) { case SyntaxKind.OpenBraceToken: - return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); + return parseBlock(SyntaxKind.Block, /* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: From df4f9044d90cdb521c5ac731cab4652fb7e85f16 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 1 Dec 2014 17:07:13 -0800 Subject: [PATCH 21/21] Make sure we have an appropriate error for heritage clause lists. --- src/compiler/diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/parser.ts | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 6b545ddfd0d..69776f23a08 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -137,6 +137,7 @@ module ts { Classes_can_only_extend_a_single_class: { code: 1174, category: DiagnosticCategory.Error, key: "Classes can only extend a single class." }, implements_clause_already_seen: { code: 1175, category: DiagnosticCategory.Error, key: "'implements' clause already seen." }, Interface_declaration_cannot_have_implements_clause: { code: 1176, category: DiagnosticCategory.Error, key: "Interface declaration cannot have 'implements' clause." }, + Unexpected_token_expected: { code: 1177, category: DiagnosticCategory.Error, key: "Unexpected token. '{' expected." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8d9dbe94097..317fc33953c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -539,6 +539,10 @@ "category": "Error", "code": 1176 }, + "Unexpected token. '{' expected.": { + "category": "Error", + "code": 1177 + }, "Duplicate identifier '{0}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8348d7d802b..aabcae0324f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -779,7 +779,7 @@ module ts { TypeMembers, // Members in interface or type literal ClassMembers, // Members in class declaration EnumMembers, // Members in enum declaration - BaseTypeReferences, // Type references in extends or implements clause + TypeReferences, // Type references in extends or implements clause VariableDeclarations, // Variable declarations in variable statement ArgumentExpressions, // Expressions in argument list ObjectLiteralMembers, // Members in object literal @@ -808,7 +808,7 @@ module ts { case ParsingContext.TypeMembers: return Diagnostics.Property_or_signature_expected; case ParsingContext.ClassMembers: return Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected; case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; - case ParsingContext.BaseTypeReferences: return Diagnostics.Type_reference_expected; + case ParsingContext.TypeReferences: return Diagnostics.Type_reference_expected; case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; @@ -817,6 +817,7 @@ module ts { case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; + case ParsingContext.HeritageClauses: return Diagnostics.Unexpected_token_expected; } }; @@ -1416,7 +1417,7 @@ module ts { return token === SyntaxKind.OpenBracketToken || isLiteralPropertyName(); case ParsingContext.ObjectLiteralMembers: return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.AsteriskToken || isLiteralPropertyName(); - case ParsingContext.BaseTypeReferences: + case ParsingContext.TypeReferences: return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier()))); case ParsingContext.VariableDeclarations: case ParsingContext.TypeParameters: @@ -1455,7 +1456,7 @@ module ts { return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; - case ParsingContext.BaseTypeReferences: + case ParsingContext.TypeReferences: return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.ExtendsKeyword || token === SyntaxKind.ImplementsKeyword; case ParsingContext.VariableDeclarations: return isVariableDeclaratorListTerminator(); @@ -3860,7 +3861,7 @@ module ts { var node = createNode(SyntaxKind.HeritageClause); node.token = token; nextToken(); - node.types = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference); + node.types = parseDelimitedList(ParsingContext.TypeReferences, parseTypeReference); return finishNode(node); }