From 9af8ae43866e73eeeb7744a40f42d6716980472c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 3 Mar 2015 15:09:40 -0800 Subject: [PATCH] Parsing, binding, checking of export default with function/class --- src/compiler/binder.ts | 9 ++++++--- src/compiler/checker.ts | 37 +++++++++++++++++++++++++++++-------- src/compiler/emitter.ts | 23 +++++++++++++++-------- src/compiler/parser.ts | 16 ++++++++++++---- src/compiler/types.ts | 19 ++++++++++--------- src/compiler/utilities.ts | 1 + 6 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8541c3b36b38e..65b5c9035a201 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -112,10 +112,13 @@ module ts { return "__new"; case SyntaxKind.IndexSignature: return "__index"; - case SyntaxKind.ExportAssignment: - return "default"; case SyntaxKind.ExportDeclaration: return "__export"; + case SyntaxKind.ExportAssignment: + return "default"; + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + return node.flags & NodeFlags.Default ? "default" : undefined; } } @@ -126,7 +129,7 @@ module ts { function declareSymbol(symbols: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - var name = getDeclarationName(node); + var name = node.flags & NodeFlags.Default && parent ? "default" : getDeclarationName(node); if (name !== undefined) { var symbol = hasProperty(symbols, name) ? symbols[name] : (symbols[name] = createSymbol(0, name)); if (symbol.flags & excludes) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7699f333d54f6..d3a10ca0d1cc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8268,7 +8268,7 @@ module ts { // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { + if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { // This check will account for methods in class/interface declarations, // as well as accessors in classes/object literals checkComputedPropertyName(node.name); @@ -8998,10 +8998,12 @@ module ts { // Grammar checking checkGrammarClassDeclarationHeritageClauses(node); - checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); + if (node.name) { + checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); + checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithRequireExportsInGeneratedCode(node, node.name); + } checkTypeParameters(node.typeParameters); - checkCollisionWithCapturedThisVariable(node, node.name); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); var type = getDeclaredTypeOfSymbol(symbol); @@ -9014,9 +9016,9 @@ module ts { if (type.baseTypes.length) { if (produceDiagnostics) { var baseType = type.baseTypes[0]; - checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Class_0_incorrectly_extends_base_class_1); + checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); var staticBaseType = getTypeOfSymbol(baseType.symbol); - checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name, + checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); if (baseType.symbol !== resolveEntityName(baseTypeNode.typeName, SymbolFlags.Value)) { error(baseTypeNode, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType)); @@ -9038,7 +9040,7 @@ module ts { if (t !== unknownType) { var declaredType = (t.flags & TypeFlags.Reference) ? (t).target : t; if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) { - checkTypeAssignableTo(type, t, node.name, Diagnostics.Class_0_incorrectly_implements_interface_1); + checkTypeAssignableTo(type, t, node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); } else { error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); @@ -10414,6 +10416,10 @@ module ts { function generateNames(node: Node) { switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + generateNameForFunctionOrClassDeclaration(node); + break; case SyntaxKind.ModuleDeclaration: generateNameForModuleOrEnum(node); generateNames((node).body); @@ -10427,6 +10433,9 @@ module ts { case SyntaxKind.ExportDeclaration: generateNameForExportDeclaration(node); break; + case SyntaxKind.ExportAssignment: + generateNameForExportAssignment(node); + break; case SyntaxKind.SourceFile: case SyntaxKind.ModuleBlock: forEach((node).statements, generateNames); @@ -10464,6 +10473,12 @@ module ts { getNodeLinks(node).generatedName = unescapeIdentifier(name); } + function generateNameForFunctionOrClassDeclaration(node: Declaration) { + if (!node.name) { + assignGeneratedName(node, makeUniqueName("default")); + } + } + function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { if (node.name.kind === SyntaxKind.Identifier) { var name = node.name.text; @@ -10490,9 +10505,15 @@ module ts { generateNameForImportOrExportDeclaration(node); } } + + function generateNameForExportAssignment(node: ExportAssignment) { + if (node.expression.kind !== SyntaxKind.Identifier) { + assignGeneratedName(node, makeUniqueName("default")); + } + } } - function getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration | ExportDeclaration) { + function getGeneratedNameForNode(node: Node) { var links = getNodeLinks(node); if (!links.generatedName) { getGeneratedNamesForSourceFile(getSourceFile(node)); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8551d8301e789..f2f04bcd2de37 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1580,7 +1580,7 @@ module ts { var tempParameters: Identifier[]; var externalImports: ExternalImportInfo[]; var exportSpecifiers: Map; - var exportDefault: ExportAssignment | ExportSpecifier; + var exportDefault: FunctionDeclaration | ClassDeclaration | ExportAssignment | ExportSpecifier; /** write emitted output to disk*/ var writeEmittedFiles = writeJavaScriptFile; @@ -3912,7 +3912,7 @@ module ts { emitExpressionFunctionBody(node, node.body); } - if (node.flags & NodeFlags.Export) { + if (node.flags & NodeFlags.Export && !(node.flags & NodeFlags.Default)) { writeLine(); emitStart(node); emitModuleMemberName(node); @@ -4243,11 +4243,10 @@ module ts { emitMemberFunctions(node); emitMemberAssignments(node, NodeFlags.Static); writeLine(); - function emitClassReturnStatement() { + emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => { write("return "); emitNode(node.name); - } - emitToken(SyntaxKind.CloseBraceToken, node.members.end, emitClassReturnStatement); + }); write(";"); decreaseIndent(); writeLine(); @@ -4260,7 +4259,7 @@ module ts { } write(");"); emitEnd(node); - if (node.flags & NodeFlags.Export) { + if (node.flags & NodeFlags.Export && !(node.flags & NodeFlags.Default)) { writeLine(); emitStart(node); emitModuleMemberName(node); @@ -4269,7 +4268,7 @@ module ts { emitEnd(node); write(";"); } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile && node.name) { emitExportMemberAssignments(node.name); } @@ -4680,6 +4679,11 @@ module ts { else if (node.kind === SyntaxKind.ExportAssignment) { exportDefault = exportDefault || node; } + else if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ClassDeclaration) { + if (node.flags & NodeFlags.Export && node.flags & NodeFlags.Default) { + exportDefault = exportDefault || node; + } + } else { var info = createExternalImportInfo(node); if (info) { @@ -4788,9 +4792,12 @@ module ts { if (exportDefault.kind === SyntaxKind.ExportAssignment) { emit((exportDefault).expression); } - else { + else if (exportDefault.kind === SyntaxKind.ExportSpecifier) { emit((exportDefault).propertyName); } + else { + emit((exportDefault).name); + } write(";"); emitEnd(exportDefault); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index abccfb96ffaa4..9ebfc108c6a48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -358,6 +358,7 @@ module ts { case SyntaxKind.ExportKeyword: return NodeFlags.Export; case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; case SyntaxKind.ConstKeyword: return NodeFlags.Const; + case SyntaxKind.DefaultKeyword: return NodeFlags.Default; } return 0; } @@ -1501,11 +1502,13 @@ module ts { if (token === SyntaxKind.ExportKeyword) { nextToken(); if (token === SyntaxKind.DefaultKeyword) { - nextToken(); - return token === SyntaxKind.ClassKeyword || token === SyntaxKind.FunctionKeyword; + return lookAhead(nextTokenIsClassOrFunction); } return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier(); } + if (token === SyntaxKind.DefaultKeyword) { + return nextTokenIsClassOrFunction(); + } nextToken(); return canFollowModifier(); } @@ -1517,6 +1520,11 @@ module ts { || isLiteralPropertyName(); } + function nextTokenIsClassOrFunction(): boolean { + nextToken(); + return token === SyntaxKind.ClassKeyword || token === SyntaxKind.FunctionKeyword; + } + // True if positioned at the start of a list element function isListElement(parsingContext: ParsingContext, inErrorRecovery: boolean): boolean { var node = currentNode(parsingContext); @@ -4323,7 +4331,7 @@ module ts { setModifiers(node, modifiers); parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - node.name = parseIdentifier(); + node.name = node.flags & NodeFlags.Default ? parseOptionalIdentifier() : parseIdentifier(); fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, /*requireCompleteParameterList:*/ false, node); node.body = parseFunctionBlockOrSemicolon(!!node.asteriskToken, Diagnostics.or_expected); return finishNode(node); @@ -4499,7 +4507,7 @@ module ts { var node = createNode(SyntaxKind.ClassDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.ClassKeyword); - node.name = parseIdentifier(); + node.name = node.flags & NodeFlags.Default ? parseOptionalIdentifier() : parseIdentifier(); node.typeParameters = parseTypeParameters(); node.heritageClauses = parseHeritageClauses(/*isClassHeritageClause:*/ true); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5115fd0d25cd0..24f2b23db60ab 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -298,14 +298,15 @@ module ts { Private = 0x00000020, // Property/Method Protected = 0x00000040, // Property/Method Static = 0x00000080, // Property/Method - MultiLine = 0x00000100, // Multi-line array or object literal - Synthetic = 0x00000200, // Synthetic node (for full fidelity) - DeclarationFile = 0x00000400, // Node is a .d.ts file - Let = 0x00000800, // Variable declaration - Const = 0x00001000, // Variable declaration - OctalLiteral = 0x00002000, - - Modifier = Export | Ambient | Public | Private | Protected | Static, + Default = 0x00000100, // Function/Class (export default declaration) + MultiLine = 0x00000200, // Multi-line array or object literal + Synthetic = 0x00000400, // Synthetic node (for full fidelity) + DeclarationFile = 0x00000800, // Node is a .d.ts file + Let = 0x00001000, // Variable declaration + Const = 0x00002000, // Variable declaration + OctalLiteral = 0x00004000, + + Modifier = Export | Ambient | Public | Private | Protected | Static | Default, AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const } @@ -1185,7 +1186,7 @@ module ts { } export interface EmitResolver { - getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration | ExportDeclaration): string; + getGeneratedNameForNode(node: Node): string; getExpressionNameSubstitution(node: Identifier): string; hasExportDefaultValue(node: SourceFile): boolean; isReferencedImportDeclaration(node: Node): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c5bb46aa9624c..269e9007cdff9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -901,6 +901,7 @@ module ts { case SyntaxKind.ExportKeyword: case SyntaxKind.DeclareKeyword: case SyntaxKind.ConstKeyword: + case SyntaxKind.DefaultKeyword: return true; } return false;