diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0cdd0b21b6b..04c12eb613c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18625,50 +18625,9 @@ namespace ts { } function checkGrammarModifiers(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.Constructor: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportDeclaration: - case SyntaxKind.ExportAssignment: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.Parameter: - break; - case SyntaxKind.FunctionDeclaration: - if (node.modifiers && (node.modifiers.length > 1 || node.modifiers[0].kind !== SyntaxKind.AsyncKeyword) && - node.parent.kind !== SyntaxKind.ModuleBlock && node.parent.kind !== SyntaxKind.SourceFile) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); - } - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.VariableStatement: - case SyntaxKind.TypeAliasDeclaration: - if (node.modifiers && node.parent.kind !== SyntaxKind.ModuleBlock && node.parent.kind !== SyntaxKind.SourceFile) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); - } - break; - case SyntaxKind.EnumDeclaration: - if (node.modifiers && (node.modifiers.length > 1 || node.modifiers[0].kind !== SyntaxKind.ConstKeyword) && - node.parent.kind !== SyntaxKind.ModuleBlock && node.parent.kind !== SyntaxKind.SourceFile) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); - } - break; - default: - return false; - } - - if (!node.modifiers) { - return; + const quickResult = reportObviousModifierErrors(node); + if (quickResult !== undefined) { + return quickResult; } let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node; @@ -18873,6 +18832,61 @@ namespace ts { } } + /** + * true | false: Early return this value from checkGrammarModifiers. + * undefined: Need to do full checking on the modifiers. + */ + function reportObviousModifierErrors(node: Node): boolean | undefined { + return !node.modifiers + ? false + : shouldReportBadModifier(node) + ? grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here) + : undefined; + } + function shouldReportBadModifier(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.Constructor: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ExportDeclaration: + case SyntaxKind.ExportAssignment: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.Parameter: + return false; + default: + if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { + return false; + } + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword); + case SyntaxKind.ClassDeclaration: + return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword); + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.TypeAliasDeclaration: + return true; + case SyntaxKind.EnumDeclaration: + return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword); + default: + Debug.fail(); + return false; + } + } + } + function nodeHasAnyModifiersExcept(node: Node, allowedModifier: SyntaxKind): boolean { + return node.modifiers.length > 1 || node.modifiers[0].kind !== allowedModifier; + } + function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean { if (languageVersion < ScriptTarget.ES6) { return grammarErrorOnNode(asyncModifier, Diagnostics.Async_functions_are_only_available_when_targeting_ECMAScript_2015_or_higher); diff --git a/tests/baselines/reference/abstractClassInLocalScope.js b/tests/baselines/reference/abstractClassInLocalScope.js new file mode 100644 index 00000000000..a49da04c8a1 --- /dev/null +++ b/tests/baselines/reference/abstractClassInLocalScope.js @@ -0,0 +1,31 @@ +//// [abstractClassInLocalScope.ts] +(() => { + abstract class A {} + class B extends A {} + new B(); + return A; +})(); + + +//// [abstractClassInLocalScope.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +(function () { + var A = (function () { + function A() { + } + return A; + }()); + var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; + }(A)); + new B(); + return A; +})(); diff --git a/tests/baselines/reference/abstractClassInLocalScope.symbols b/tests/baselines/reference/abstractClassInLocalScope.symbols new file mode 100644 index 00000000000..8b8033e5c86 --- /dev/null +++ b/tests/baselines/reference/abstractClassInLocalScope.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/abstractClassInLocalScope.ts === +(() => { + abstract class A {} +>A : Symbol(A, Decl(abstractClassInLocalScope.ts, 0, 8)) + + class B extends A {} +>B : Symbol(B, Decl(abstractClassInLocalScope.ts, 1, 23)) +>A : Symbol(A, Decl(abstractClassInLocalScope.ts, 0, 8)) + + new B(); +>B : Symbol(B, Decl(abstractClassInLocalScope.ts, 1, 23)) + + return A; +>A : Symbol(A, Decl(abstractClassInLocalScope.ts, 0, 8)) + +})(); + diff --git a/tests/baselines/reference/abstractClassInLocalScope.types b/tests/baselines/reference/abstractClassInLocalScope.types new file mode 100644 index 00000000000..56456dc7d8c --- /dev/null +++ b/tests/baselines/reference/abstractClassInLocalScope.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/abstractClassInLocalScope.ts === +(() => { +>(() => { abstract class A {} class B extends A {} new B(); return A;})() : typeof A +>(() => { abstract class A {} class B extends A {} new B(); return A;}) : () => typeof A +>() => { abstract class A {} class B extends A {} new B(); return A;} : () => typeof A + + abstract class A {} +>A : A + + class B extends A {} +>B : B +>A : A + + new B(); +>new B() : B +>B : typeof B + + return A; +>A : typeof A + +})(); + diff --git a/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.errors.txt b/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.errors.txt new file mode 100644 index 00000000000..f5c5b417ea3 --- /dev/null +++ b/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/abstractClassInLocalScopeIsAbstract.ts(4,5): error TS2511: Cannot create an instance of the abstract class 'A'. + + +==== tests/cases/compiler/abstractClassInLocalScopeIsAbstract.ts (1 errors) ==== + (() => { + abstract class A {} + class B extends A {} + new A(); + ~~~~~~~ +!!! error TS2511: Cannot create an instance of the abstract class 'A'. + new B(); + })() + \ No newline at end of file diff --git a/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.js b/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.js new file mode 100644 index 00000000000..1f6c1dfdd7a --- /dev/null +++ b/tests/baselines/reference/abstractClassInLocalScopeIsAbstract.js @@ -0,0 +1,31 @@ +//// [abstractClassInLocalScopeIsAbstract.ts] +(() => { + abstract class A {} + class B extends A {} + new A(); + new B(); +})() + + +//// [abstractClassInLocalScopeIsAbstract.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +(function () { + var A = (function () { + function A() { + } + return A; + }()); + var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; + }(A)); + new A(); + new B(); +})(); diff --git a/tests/cases/compiler/abstractClassInLocalScope.ts b/tests/cases/compiler/abstractClassInLocalScope.ts new file mode 100644 index 00000000000..8bc7e80d263 --- /dev/null +++ b/tests/cases/compiler/abstractClassInLocalScope.ts @@ -0,0 +1,6 @@ +(() => { + abstract class A {} + class B extends A {} + new B(); + return A; +})(); diff --git a/tests/cases/compiler/abstractClassInLocalScopeIsAbstract.ts b/tests/cases/compiler/abstractClassInLocalScopeIsAbstract.ts new file mode 100644 index 00000000000..ca98a662016 --- /dev/null +++ b/tests/cases/compiler/abstractClassInLocalScopeIsAbstract.ts @@ -0,0 +1,6 @@ +(() => { + abstract class A {} + class B extends A {} + new A(); + new B(); +})()