From 57dce1c0bcbfbb9be5609abc1391cfd29e65ed66 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 14 Feb 2017 12:42:32 -0800 Subject: [PATCH] Error on emit declaration of extends class w/o symbol Error when emitting an extends clause for a type that has no symbol. This error only occurs on exported classes. This prevents the emitter from producing types that extend from intersections, which are not parseable right now. --- src/compiler/checker.ts | 3 +++ src/compiler/declarationEmitter.ts | 20 ++++++++++++++++---- src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/types.ts | 1 + src/compiler/utilities.ts | 3 ++- src/services/utilities.ts | 5 +++-- 6 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 704812b0185..51e76f72c59 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20902,6 +20902,9 @@ namespace ts { const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(node)); resolveBaseTypesOfClass(classType); const baseType = classType.resolvedBaseTypes.length ? classType.resolvedBaseTypes[0] : unknownType; + if (!baseType.symbol) { + writer.reportIllegalExtends(); + } getSymbolDisplayBuilder().buildTypeDisplay(baseType, writer, enclosingDeclaration, flags); } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index f737c5e086d..d5ea403e111 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -190,6 +190,7 @@ namespace ts { const writer = createTextWriter(newLine); writer.trackSymbol = trackSymbol; writer.reportInaccessibleThisError = reportInaccessibleThisError; + writer.reportIllegalExtends = reportIllegalExtends; writer.writeKeyword = writer.write; writer.writeOperator = writer.write; writer.writePunctuation = writer.write; @@ -313,6 +314,14 @@ namespace ts { recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning)); } + function reportIllegalExtends() { + if (errorNameNode) { + reportedDeclarationError = true; + emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.Extends_clause_of_exported_class_0_refers_to_a_type_with_no_declaration, + declarationNameToString(errorNameNode))); + } + } + function reportInaccessibleThisError() { if (errorNameNode) { reportedDeclarationError = true; @@ -1071,7 +1080,7 @@ namespace ts { } } - function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) { + function emitHeritageClause(className: Identifier, typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) { if (typeReferences) { write(isImplementsList ? " implements " : " extends "); emitCommaList(typeReferences, emitTypeOfTypeReference); @@ -1086,7 +1095,9 @@ namespace ts { } else { writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError; + errorNameNode = className; resolver.writeBaseConstructorTypeOfClass(enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer); + errorNameNode = undefined; } function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic { @@ -1136,9 +1147,10 @@ namespace ts { emitTypeParameters(node.typeParameters); const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { - emitHeritageClause([baseTypeNode], /*isImplementsList*/ false); + node.name + emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false); } - emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true); + emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true); write(" {"); writeLine(); increaseIndent(); @@ -1160,7 +1172,7 @@ namespace ts { emitTypeParameters(node.typeParameters); const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression)); if (interfaceExtendsTypes && interfaceExtendsTypes.length) { - emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false); + emitHeritageClause(node.name, interfaceExtendsTypes, /*isImplementsList*/ false); } write(" {"); writeLine(); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ec6067751a2..1383c92c672 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2356,6 +2356,10 @@ "category": "Error", "code": 4092 }, + "Extends clause of exported class '{0}' refers to a type with no declaration.": { + "category": "Error", + "code": 4093 + }, "The current host does not support the '{0}' option.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bdc4eea98da..e00fab70c1a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2474,6 +2474,7 @@ // with import statements it previously saw (but chose not to emit). trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; reportInaccessibleThisError(): void; + reportIllegalExtends(): void; } export const enum TypeFormatFlags { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 39d53a9a9c0..0a88058b9c5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -53,7 +53,8 @@ namespace ts { decreaseIndent: noop, clear: () => str = "", trackSymbol: noop, - reportInaccessibleThisError: noop + reportInaccessibleThisError: noop, + reportIllegalExtends: noop }; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index f83f9ef8231..0d2245abbaa 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1166,7 +1166,8 @@ namespace ts { decreaseIndent: () => { indent--; }, clear: resetWriter, trackSymbol: noop, - reportInaccessibleThisError: noop + reportInaccessibleThisError: noop, + reportIllegalExtends: noop }; function writeIndent() { @@ -1386,4 +1387,4 @@ namespace ts { // First token is the open curly, this is where we want to put the 'super' call. return constructor.body.getFirstToken(sourceFile).getEnd(); } -} \ No newline at end of file +}