8000 Merge pull request #3365 from Microsoft/painInTheASI · imskojs/TypeScript@6b77c24 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b77c24

Browse files
Merge pull request microsoft#3365 from Microsoft/painInTheASI
Parse namespaces/types aliases/interfaces/ambient declarations only when they cannot be legal JS
2 parents c83cf58 + 3d9293e commit 6b77c24

File tree

74 files changed

+1029
-26
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1029
-26
lines changed

src/compiler/parser.ts

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,7 +1816,7 @@ module ts {
18161816
if (matchesPattern) {
18171817
// Report that we need an identifier. However, report it right after the dot,
18181818
// and not on the next token. This is because the next token might actually
1819-
// be an identifier and the error woudl be quite confusing.
1819+
// be an identifier and the error would be quite confusing.
18201820
return <Identifier>createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentToken*/ true, Diagnostics.Identifier_expected);
18211821
}
18221822
}
@@ -2686,7 +2686,7 @@ module ts {
26862686

26872687
function nextTokenIsIdentifierOnSameLine() {
26882688
nextToken();
2689-
return !scanner.hasPrecedingLineBreak() && isIdentifier()
2689+
return !scanner.hasPrecedingLineBreak() && isIdentifier();
26902690
}
26912691

26922692
function parseYieldExpression(): YieldExpression {
@@ -3809,14 +3809,42 @@ module ts {
38093809
case SyntaxKind.ClassKeyword:
38103810
case SyntaxKind.EnumKeyword:
38113811
return StatementFlags.Statement;
3812+
3813+
// 'declare', 'module', 'namespace', 'interface'* and 'type' are all legal JavaScript identifiers;
3814+
// however, an identifier cannot be followed by another identifier on the same line. This is what we
3815+
// count on to parse out the respective declarations. For instance, we exploit this to say that
3816+
//
3817+
// namespace n
3818+
//
3819+
// can be none other than the beginning of a namespace declaration, but need to respect that JavaScript sees
3820+
//
3821+
// namespace
3822+
// n
3823+
//
3824+
// as the identifier 'namespace' on one line followed by the identifier 'n' on another.
3825+
// We need to look one token ahead to see if it permissible to try parsing a declaration.
3826+
//
3827+
// *Note*: 'interface' is actually a strict mode reserved word. So while
3828+
//
3829+
// "use strict"
3830+
// interface
3831+
// I {}
3832+
//
3833+
// could be legal, it would add complexity for very little gain.
38123834
case SyntaxKind.InterfaceKeyword:
38133835
case SyntaxKind.TypeKeyword:
3814-
nextToken();
3815-
return isIdentifierOrKeyword() ? StatementFlags.Statement : StatementFlags.None;
3836+
return nextTokenIsIdentifierOnSameLine() ? StatementFlags.Statement : StatementFlags.None;
38163837
case SyntaxKind.ModuleKeyword:
38173838
case SyntaxKind.NamespaceKeyword:
3839+
return nextTokenIsIdentifierOrStringLiteralOnSameLine() ? StatementFlags.ModuleElement : StatementFlags.None;
3840+
case SyntaxKind.DeclareKeyword:
38183841
nextToken();
3819-
return isIdentifierOrKeyword() || token === SyntaxKind.StringLiteral ? StatementFlags.ModuleElement : StatementFlags.None;
3842+
// ASI takes effect for this modifier.
3843+
if (scanner.hasPrecedingLineBreak()) {
3844+
return StatementFlags.None;
3845+
}
3846+
continue;
3847+
38203848
case SyntaxKind.ImportKeyword:
38213849
nextToken();
38223850
return token === SyntaxKind.StringLiteral || token === SyntaxKind.AsteriskToken ||
@@ -3829,7 +3857,6 @@ module ts {
38293857
return StatementFlags.ModuleElement;
38303858
}
38313859
continue;
3832-
case SyntaxKind.DeclareKeyword:
38333860
case SyntaxKind.PublicKeyword:
38343861
case SyntaxKind.PrivateKeyword:
38353862
case SyntaxKind.ProtectedKeyword:
@@ -3971,27 +3998,28 @@ module ts {
39713998
case SyntaxKind.ThrowKeyword:
39723999
return parseThrowStatement();
39734000
case SyntaxKind.TryKeyword:
3974-
// Include the next two for error recovery.
4001+
// Include 'catch' and 'finally' for error recovery.
39754002
case SyntaxKind.CatchKeyword:
39764003
case SyntaxKind.FinallyKeyword:
39774004
return parseTryStatement();
39784005
case SyntaxKind.DebuggerKeyword:
39794006
return parseDebuggerStatement();
39804007
case SyntaxKind.AtToken:
39814008
return parseDeclaration();
3982-
case SyntaxKind.ConstKeyword:
4009+
4010+
case SyntaxKind.InterfaceKeyword:
4011+
case SyntaxKind.TypeKeyword:
4012+
case SyntaxKind.ModuleKeyword:
4013+
case SyntaxKind.NamespaceKeyword:
39834014
case SyntaxKind.DeclareKeyword:
4015+
case SyntaxKind.ConstKeyword:
39844016
case SyntaxKind.EnumKeyword:
39854017
case SyntaxKind.ExportKeyword:
39864018
case SyntaxKind.ImportKeyword:
3987-
case SyntaxKind.InterfaceKeyword:
3988-
case SyntaxKind.ModuleKeyword:
3989-
case SyntaxKind.NamespaceKeyword:
39904019
case SyntaxKind.PrivateKeyword:
39914020
case SyntaxKind.ProtectedKeyword:
39924021
case SyntaxKind.PublicKeyword:
39934022
case SyntaxKind.StaticKeyword:
3994-
case SyntaxKind.TypeKeyword:
39954023
if (getDeclarationFlags() & flags) {
39964024
return parseDeclaration();
39974025
}
@@ -4042,6 +4070,11 @@ module ts {
40424070
}
40434071
}
40444072

4073+
function nextTokenIsIdentifierOrStringLiteralOnSameLine() {
4074+
nextToken();
4075+
return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token === SyntaxKind.StringLiteral);
4076+
}
4077+
40454078
function parseFunctionBlockOrSemicolon(isGenerator: boolean, diagnosticMessage?: DiagnosticMessage): Block {
40464079
if (token !== SyntaxKind.OpenBraceToken && canParseSemicolon()) {
40474080
parseSemicolon();
@@ -4583,7 +4616,7 @@ module ts {
45834616
function parseModuleBlock(): ModuleBlock {
45844617
let node = <ModuleBlock>createNode(SyntaxKind.ModuleBlock, scanner.getStartPos());
45854618
if (parseExpected(SyntaxKind.OpenBraceToken)) {
4586-
node.statements = parseList(ParsingContext.ModuleElements, /*checkForStrictMode*/false, parseModuleElement);
4619+
node.statements = parseList(ParsingContext.ModuleElements, /*checkForStrictMode*/ false, parseModuleElement);
45874620
parseExpected(SyntaxKind.CloseBraceToken);
45884621
}
45894622
else {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [asiPreventsParsingAsAmbientExternalModule01.ts]
2+
3+
var declare: number;
4+
var module: string;
5+
6+
declare // this is the identifier 'declare'
7+
module // this is the identifier 'module'
8+
"my external module" // this is just a string
9+
{ } // this is a block body
10+
11+
//// [asiPreventsParsingAsAmbientExternalModule01.js]
12+
var declare;
13+
var module;
14+
declare; // this is the identifier 'declare'
15+
module; // this is the identifier 'module'
16+
"my external module"; // this is just a string
17+
{ } // this is a block body
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/conformance/externalModules/asiPreventsParsingAsAmbientExternalModule01.ts ===
2+
3+
var declare: number;
4+
>declare : Symbol(declare, Decl(asiPreventsParsingAsAmbientExternalModule01.ts, 1, 3))
5+
6+
var module: string;
7+
>module : Symbol(module, Decl(asiPreventsParsingAsAmbientExternalModule01.ts, 2, 3))
8+
9+
declare // this is the identifier 'declare'
10+
>declare : Symbol(declare, Decl(asiPreventsParsingAsAmbientExternalModule01.ts, 1, 3))
11+
12+
module // this is the identifier 'module'
13+
>module : Symbol(module, Decl(asiPreventsParsingAsAmbientExternalModule01.ts, 2, 3))
14+
15+
"my external module" // this is just a string
16+
{ } // this is a block body
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/conformance/externalModules/asiPreventsParsingAsAmbientExternalModule01.ts ===
2+
3+
var declare: number;
4+
>declare : number
5+
6+
var module: string;
7+
>module : string
8+
9+
declare // this is the identifier 'declare'
10+
>declare : number
11+
12+
module // this is the identifier 'module'
13+
>module : string
14+
15+
"my external module" // this is just a string
16+
>"my external module" : string
17+
18+
{ } // this is a block body
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [asiPreventsParsingAsAmbientExternalModule02.ts]
2+
3+
var declare: number;
4+
var module: string;
5+
6+
module container {
7+
declare // this is the identifier 'declare'
8+
module // this is the identifier 'module'
9+
"my external module" // this is just a string
10+
{ } // this is a block body
11+
}
12+
13+
//// [asiPreventsParsingAsAmbientExternalModule02.js]
14+
var declare;
15+
var module;
16+
var container;
17+
(function (container) {
18+
declare; // this is the identifier 'declare'
19+
module; // this is the identifier 'module'
20+
"my external module"; // this is just a string
21+
{ } // this is a block body
22+
})(container || (container = {}));
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/externalModules/asiPreventsParsingAsAmbientExternalModule02.ts ===
2+
3+
var declare: number;
4+
>declare : Symbol(declare, Decl(asiPreventsParsingAsAmbientExternalModule02.ts, 1, 3))
5+
6+
var module: string;
7+
>module : Symbol(module, Decl(asiPreventsParsingAsAmbientExternalModule02.ts, 2, 3))
8+
9+
module container {
10+
>container : Symbol(container, Decl(asiPreventsParsingAsAmbientExternalModule02.ts, 2, 19))
11+
12+
declare // this is the identifier 'declare'
13+
>declare : Symbol(declare, Decl(asiPreventsParsingAsAmbientExternalModule02.ts, 1, 3))
14+
15+
module // this is the identifier 'module'
16+
>module : Symbol(module, Decl(asiPreventsParsingAsAmbientExternalModule02.ts, 2, 3))
17+
18+
"my external module" // this is just a string
19+
{ } // this is a block body
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/externalModules/asiPreventsParsingAsAmbientExternalModule02.ts ===
2+
3+
var declare: number;
4+
>declare : number
5+
6+
var module: string;
7+
>module : string
8+
9+
module container {
10+
>container : typeof container
11+
12+
declare // this is the identifier 'declare'
13+
>declare : number
14+
15+
module // this is the identifier 'module'
16+
>module : string
17+
18+
"my external module" // this is just a string
19+
>"my external module" : string
20+
21+
{ } // this is a block body
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [asiPreventsParsingAsInterface01.ts]
2+
3+
var interface: number, I: string;
4+
5+
interface // This should be the identifier 'interface'
6+
I // This should be the identifier 'I'
7+
{} // This should be a block body
8+
9+
//// [asiPreventsParsingAsInterface01.js]
10+
var interface, I;
11+
interface; // This should be the identifier 'interface'
12+
I; // This should be the identifier 'I'
13+
{ } // This should be a block body
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/interfaces/interfaceDeclarations/asiPreventsParsingAsInterface01.ts ===
2+
3+
var interface: number, I: string;
4+
>interface : Symbol(interface, Decl(asiPreventsParsingAsInterface01.ts, 1, 3))
5+
>I : Symbol(I, Decl(asiPreventsParsingAsInterface01.ts, 1, 22))
6+
7+
interface // This should be the identifier 'interface'
8+
>interface : Symbol(interface, Decl(asiPreventsParsingAsInterface01.ts, 1, 3))
9+
10+
I // This should be the identifier 'I'
11+
>I : Symbol(I, Decl(asiPreventsParsingAsInterface01.ts, 1, 22))
12+
13+
{} // This should be a block body
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/interfaces/interfaceDeclarations/asiPreventsParsingAsInterface01.ts ===
2+
3+
var interface: number, I: string;
4+
>interface : number
5+
>I : string
6+
7+
interface // This should be the identifier 'interface'
8+
>interface : number
9+
10+
I // This should be the identifier 'I'
11+
>I : string
12+
13+
{} // This should be a block body

0 commit comments

Comments
 (0)
0