Skip to content

Commit

Permalink
Parsing, binding, checking of export default with function/class
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg committed Mar 3, 2015
1 parent 74acbe9 commit 9af8ae4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 32 deletions.
9 changes: 6 additions & 3 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

This comment has been minimized.

Copy link
@CyrusNajmabadi

CyrusNajmabadi Mar 4, 2015

Contributor

I think i'd prefer an assert over just returning undefined. If you have a class/function, it better have a name if it doesn't have the 'default' modifier on it.

}
}

Expand All @@ -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) {
Expand Down
37 changes: 29 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(<ComputedPropertyName>node.name);
Expand Down Expand Up @@ -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 = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
Expand All @@ -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);

This comment has been minimized.

Copy link
@CyrusNajmabadi

CyrusNajmabadi Mar 4, 2015

Contributor

if this is for error reporting, then we should use the helper function we have to pick the best error location for a node.

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));
Expand All @@ -9038,7 +9040,7 @@ module ts {
if (t !== unknownType) {
var declaredType = (t.flags & TypeFlags.Reference) ? (<TypeReference>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);
Expand Down Expand Up @@ -10414,6 +10416,10 @@ module ts {

function generateNames(node: Node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
generateNameForFunctionOrClassDeclaration(<Declaration>node);
break;
case SyntaxKind.ModuleDeclaration:
generateNameForModuleOrEnum(<ModuleDeclaration>node);
generateNames((<ModuleDeclaration>node).body);
Expand All @@ -10427,6 +10433,9 @@ module ts {
case SyntaxKind.ExportDeclaration:
generateNameForExportDeclaration(<ExportDeclaration>node);
break;
case SyntaxKind.ExportAssignment:
generateNameForExportAssignment(<ExportAssignment>node);
break;
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
forEach((<SourceFile | ModuleBlock>node).statements, generateNames);
Expand Down Expand Up @@ -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;
Expand All @@ -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));
Expand Down
23 changes: 15 additions & 8 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,7 @@ module ts {
var tempParameters: Identifier[];
var externalImports: ExternalImportInfo[];
var exportSpecifiers: Map<ExportSpecifier[]>;
var exportDefault: ExportAssignment | ExportSpecifier;
var exportDefault: FunctionDeclaration | ClassDeclaration | ExportAssignment | ExportSpecifier;

/** write emitted output to disk*/
var writeEmittedFiles = writeJavaScriptFile;
Expand Down Expand Up @@ -3912,7 +3912,7 @@ module ts {
emitExpressionFunctionBody(node, <Expression>node.body);
}

if (node.flags & NodeFlags.Export) {
if (node.flags & NodeFlags.Export && !(node.flags & NodeFlags.Default)) {
writeLine();
emitStart(node);
emitModuleMemberName(node);
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -4680,6 +4679,11 @@ module ts {
else if (node.kind === SyntaxKind.ExportAssignment) {
exportDefault = exportDefault || <ExportAssignment>node;
}
else if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ClassDeclaration) {
if (node.flags & NodeFlags.Export && node.flags & NodeFlags.Default) {
exportDefault = exportDefault || <FunctionDeclaration | ClassDeclaration>node;
}
}
else {
var info = createExternalImportInfo(node);
if (info) {
Expand Down Expand Up @@ -4788,9 +4792,12 @@ module ts {
if (exportDefault.kind === SyntaxKind.ExportAssignment) {
emit((<ExportAssignment>exportDefault).expression);
}
else {
else if (exportDefault.kind === SyntaxKind.ExportSpecifier) {
emit((<ExportSpecifier>exportDefault).propertyName);
}
else {
emit((<Declaration>exportDefault).name);
}
write(";");
emitEnd(exportDefault);
}
Expand Down
16 changes: 12 additions & 4 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -4499,7 +4507,7 @@ module ts {
var node = <ClassDeclaration>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);

Expand Down
19 changes: 10 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ module ts {
case SyntaxKind.ExportKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DefaultKeyword:

This comment has been minimized.

Copy link
@CyrusNajmabadi

CyrusNajmabadi Mar 4, 2015

Contributor

Will this affect things like parsing modifiers for other constructs (beyond classes/functions)? If so, do we need to report errors for doing things like "export default enum { }" yadda yadda yadda?

This comment has been minimized.

Copy link
@ahejlsberg

ahejlsberg Mar 4, 2015

Author Member

No, default is considered a modifier only when it immediately precedes function or class.

return true;
}
return false;
Expand Down

0 comments on commit 9af8ae4

Please sign in to comment.