diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1a1075d9d9e..195782a9e1f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -53,6 +53,10 @@ module ts { } } + export function hasUnknownComputedName(declaration: Declaration): boolean { + return declaration.name && declaration.name.kind === SyntaxKind.ComputedPropertyName; + } + export function bindSourceFile(file: SourceFile) { var parent: Node; @@ -84,13 +88,14 @@ module ts { if (symbolKind & SymbolFlags.Value && !symbol.valueDeclaration) symbol.valueDeclaration = node; } - // TODO(jfreeman): Implement getDeclarationName for property name + // Should not be called on a declaration with a computed property name. function getDeclarationName(node: Declaration): string { if (node.name) { if (node.kind === SyntaxKind.ModuleDeclaration && node.name.kind === SyntaxKind.StringLiteral) { return '"' + (node.name).text + '"'; } - return (node.name).text; + Debug.assert(node.name.kind !== SyntaxKind.ComputedPropertyName); + return (node.name).text; } switch (node.kind) { case SyntaxKind.ConstructorType: @@ -111,6 +116,12 @@ module ts { } function declareSymbol(symbols: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { + // Nodes with computed property names will not get symbols, because the type checker + // does not make properties for them. + if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { + return undefined; + } + var name = getDeclarationName(node); if (name !== undefined) { var symbol = hasProperty(symbols, name) ? symbols[name] : (symbols[name] = createSymbol(0, name)); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5ee9fa4ab3e..f4a94f854f7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1663,7 +1663,7 @@ module ts { if (declaration.kind === SyntaxKind.Parameter) { var func = declaration.parent; // For a parameter of a set accessor, use the type of the get accessor if one is present - if (func.kind === SyntaxKind.SetAccessor) { + if (func.kind === SyntaxKind.SetAccessor && !hasUnknownComputedName(func)) { var getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { return getReturnTypeOfSignature(getSignatureFromDeclaration(getter)); @@ -2531,7 +2531,7 @@ module ts { else { // TypeScript 1.0 spec (April 2014): // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. - if (declaration.kind === SyntaxKind.GetAccessor) { + if (declaration.kind === SyntaxKind.GetAccessor && !hasUnknownComputedName(declaration)) { var setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); returnType = getAnnotatedAccessorType(setter); } @@ -6648,9 +6648,6 @@ module ts { checkSourceElement(node.type); } if (fullTypeCheck) { - checkCollisionWithCapturedSuperVariable(node, node.name); - checkCollisionWithCapturedThisVariable(node, node.name); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); checkCollisionWithArgumentsInGeneratedCode(node); if (compilerOptions.noImplicitAny && !node.type) { switch (node.kind) { @@ -6711,17 +6708,14 @@ module ts { } function checkPropertyDeclaration(node: PropertyDeclaration) { - // TODO - checkVariableDeclaration(node); + checkVariableOrPropertyCommon(node); } function checkMethodDeclaration(node: MethodDeclaration) { - // TODO - checkFunctionDeclaration(node); + checkFunctionLikeDeclaration(node); } function checkConstructorDeclaration(node: ConstructorDeclaration) { - // TODO checkSignatureDeclaration(node); checkSourceElement(node.body); @@ -6812,29 +6806,32 @@ module ts { } } - // TypeScript 1.0 spec (April 2014): 8.4.3 - // Accessors for the same member name must specify the same accessibility. - var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - var otherAccessor = getDeclarationOfKind(node.symbol, otherKind); - if (otherAccessor) { - if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) { - error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); - } + if (!hasUnknownComputedName(node)) { + // TypeScript 1.0 spec (April 2014): 8.4.3 + // Accessors for the same member name must specify the same accessibility. + var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; + var otherAccessor = getDeclarationOfKind(node.symbol, otherKind); + if (otherAccessor) { + if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) { + error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); + } - var thisType = getAnnotatedAccessorType(node); - var otherType = getAnnotatedAccessorType(otherAccessor); - // TypeScript 1.0 spec (April 2014): 4.5 - // If both accessors include type annotations, the specified types must be identical. - if (thisType && otherType) { - if (!isTypeIdenticalTo(thisType, otherType)) { - error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type); + var thisType = getAnnotatedAccessorType(node); + var otherType = getAnnotatedAccessorType(otherAccessor); + // TypeScript 1.0 spec (April 2014): 4.5 + // If both accessors include type annotations, the specified types must be identical. + if (thisType && otherType) { + if (!isTypeIdenticalTo(thisType, otherType)) { + error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type); + } } } + + checkAndStoreTypeOfAccessors(getSymbolOfNode(node)); } } - checkFunctionDeclaration(node); - checkAndStoreTypeOfAccessors(getSymbolOfNode(node)); + checkFunctionLikeDeclaration(node); } function checkTypeReference(node: TypeReferenceNode) { @@ -7088,7 +7085,7 @@ module ts { } if (duplicateFunctionDeclaration) { - forEach( declarations, declaration => { + forEach(declarations, declaration => { error(declaration.name, Diagnostics.Duplicate_function_implementation); }); } @@ -7204,26 +7201,37 @@ module ts { } } - function checkFunctionDeclaration(node: FunctionLikeDeclaration): void { + function checkFunctionDeclaration(node: FunctionDeclaration): void { + checkFunctionLikeDeclaration(node); + if (fullTypeCheck) { + checkCollisionWithCapturedSuperVariable(node, node.name); + checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithRequireExportsInGeneratedCode(node, node.name); + } + } + + function checkFunctionLikeDeclaration(node: FunctionLikeDeclaration): void { checkSignatureDeclaration(node); - var symbol = getSymbolOfNode(node); - // first we want to check the local symbol that contain this declaration - // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol - // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode - var localSymbol = node.localSymbol || symbol; + if (!hasUnknownComputedName(node)) { + // first we want to check the local symbol that contain this declaration + // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol + // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode + var symbol = getSymbolOfNode(node); + var localSymbol = node.localSymbol || symbol; - var firstDeclaration = getDeclarationOfKind(localSymbol, node.kind); - // Only type check the symbol once - if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(localSymbol); - } + var firstDeclaration = getDeclarationOfKind(localSymbol, node.kind); + // Only type check the symbol once + if (node === firstDeclaration) { + checkFunctionOrConstructorSymbol(localSymbol); + } - if (symbol.parent) { - // run check once for the first declaration - if (getDeclarationOfKind(symbol, node.kind) === node) { - // run check on export symbol to check that modifiers agree across all exported declarations - checkFunctionOrConstructorSymbol(symbol); + if (symbol.parent) { + // run check once for the first declaration + if (getDeclarationOfKind(symbol, node.kind) === node) { + // run check on export symbol to check that modifiers agree across all exported declarations + checkFunctionOrConstructorSymbol(symbol); + } } } @@ -7344,9 +7352,8 @@ module ts { } } - // TODO(jfreeman): Decide what to do for computed properties - function needCollisionCheckForIdentifier(node: Node, identifier: DeclarationName, name: string): boolean { - if (!(identifier && (identifier).text === name)) { + function needCollisionCheckForIdentifier(node: Node, identifier: Identifier, name: string): boolean { + if (!(identifier && identifier.text === name)) { return false; } @@ -7371,8 +7378,7 @@ module ts { return true; } - // TODO(jfreeman): Decide what to do for computed properties - function checkCollisionWithCapturedThisVariable(node: Node, name: DeclarationName): void { + function checkCollisionWithCapturedThisVariable(node: Node, name: Identifier): void { if (!needCollisionCheckForIdentifier(node, name, "_this")) { return; } @@ -7397,7 +7403,7 @@ module ts { } } - function checkCollisionWithCapturedSuperVariable(node: Node, name: DeclarationName) { + function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) { if (!needCollisionCheckForIdentifier(node, name, "_super")) { return; } @@ -7420,8 +7426,7 @@ module ts { } } - // TODO(jfreeman): Decide what to do for computed properties - function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: DeclarationName) { + function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) { if (!needCollisionCheckForIdentifier(node, name, "require") && !needCollisionCheckForIdentifier(node, name, "exports")) { return; } @@ -7472,40 +7477,49 @@ module ts { } } - function checkVariableDeclaration(node: VariableDeclaration | PropertyDeclaration) { + function checkVariableOrPropertyCommon(node: VariableDeclaration | PropertyDeclaration) { checkSourceElement(node.type); - checkExportsOnMergedDeclarations(node); if (fullTypeCheck) { var symbol = getSymbolOfNode(node); - - var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); var type: Type; - var useTypeFromValueDeclaration = node === symbol.valueDeclaration; - if (useTypeFromValueDeclaration) { - type = typeOfValueDeclaration; - } - else { + if (hasUnknownComputedName(node) || symbol.valueDeclaration !== node) { type = getTypeOfVariableOrPropertyDeclaration(node); } + else { + type = getTypeOfVariableOrParameterOrProperty(symbol); + } + if (node.initializer && !(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { + // Use default messages + checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*headMessage*/ undefined); + } + return type; + } + + // All callers who expect a value to be returned are in fullTypeCheck mode. + // If we are not in type check mode, no need to get the type. + return undefined; + } + + function checkVariableDeclaration(node: VariableDeclaration) { + var type = checkVariableOrPropertyCommon(node); + if (fullTypeCheck) { + checkExportsOnMergedDeclarations(node); if (node.initializer) { - if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { - // Use default messages - checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*headMessage*/ undefined); - } - //TODO(jfreeman): Check that it is not a computed property - checkCollisionWithConstDeclarations(node); + checkCollisionWithConstDeclarations(node); } checkCollisionWithCapturedSuperVariable(node, node.name); checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - if (!useTypeFromValueDeclaration) { + var symbol = getSymbolOfNode(node); + if (node !== symbol.valueDeclaration) { // TypeScript 1.0 spec (April 2014): 5.1 // Multiple declarations for the same variable name in the same declaration space are permitted, // provided that each declaration associates the same type with the variable. + var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); if (typeOfValueDeclaration !== unknownType && type !== unknownType && !isTypeIdenticalTo(typeOfValueDeclaration, type)) { error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(typeOfValueDeclaration), typeToString(type)); } @@ -8365,7 +8379,7 @@ module ts { case SyntaxKind.ParenType: return checkSourceElement((node).type); case SyntaxKind.FunctionDeclaration: - return checkFunctionDeclaration(node); + return checkFunctionDeclaration(node); case SyntaxKind.Block: return checkBlock(node); case SyntaxKind.FunctionBlock: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 76cabe94e2e..5b3d5de82ba 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -71,8 +71,9 @@ module ts { return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier; } - // TODO(jfreeman): Implement declarationNameToString for computed properties // Return display name of an identifier + // Computed property names will just be emitted as "[]", where is the source + // text of the expression in the computed property. export function declarationNameToString(name: DeclarationName) { return name.kind === SyntaxKind.Missing ? "(Missing)" : getTextOfNode(name); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 108e00e48b4..687adb5f119 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -371,11 +371,9 @@ module ts { * Examples: * FunctionDeclaration * MethodDeclaration - * ConstructorDeclaration * AccessorDeclaration - * FunctionExpression */ - export interface FunctionLikeDeclaration extends Declaration, ParsedSignature { + export interface FunctionLikeDeclaration extends SignatureDeclaration { asteriskToken?: Node; body?: Block | Expression; } @@ -389,7 +387,7 @@ module ts { body?: Block; } - export interface ConstructorDeclaration extends FunctionLikeDeclaration { + export interface ConstructorDeclaration extends Node, ParsedSignature { body?: Block; } @@ -458,7 +456,7 @@ module ts { whenFalse: Expression; } - export interface FunctionExpression extends Expression, FunctionLikeDeclaration { + export interface FunctionExpression extends Expression, SignatureDeclaration { name?: Identifier; body: Block | Expression; // Required, whereas the member inherited from FunctionDeclaration is optional } diff --git a/tests/baselines/reference/computedPropertyNames1.js b/tests/baselines/reference/computedPropertyNames1.js index d5e3f3e0392..8f3e448d698 100644 --- a/tests/baselines/reference/computedPropertyNames1.js +++ b/tests/baselines/reference/computedPropertyNames1.js @@ -1,7 +1,7 @@ //// [computedPropertyNames1.ts] var v = { get [0 + 1]() { return 0 }, - set [0 + 1](v) { } + set [0 + 1](v: string) { } //No error } //// [computedPropertyNames1.js] @@ -10,5 +10,5 @@ var v = { return 0; }, set [0 + 1](v) { - } + } //No error }; diff --git a/tests/baselines/reference/computedPropertyNames1.types b/tests/baselines/reference/computedPropertyNames1.types new file mode 100644 index 00000000000..b44aa3c69d0 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames1.types @@ -0,0 +1,12 @@ +=== tests/cases/conformance/es6/computedProperties/computedPropertyNames1.ts === +var v = { +>v : {} +>{ get [0 + 1]() { return 0 }, set [0 + 1](v: string) { } //No error} : {} + + get [0 + 1]() { return 0 }, +>0 + 1 : number + + set [0 + 1](v: string) { } //No error +>0 + 1 : number +>v : string +} diff --git a/tests/baselines/reference/computedPropertyNamesOnOverloads.errors.txt b/tests/baselines/reference/computedPropertyNamesOnOverloads.errors.txt new file mode 100644 index 00000000000..b3c2957a13f --- /dev/null +++ b/tests/baselines/reference/computedPropertyNamesOnOverloads.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/es6/computedProperties/computedPropertyNamesOnOverloads.ts(4,5): error TS1168: Computed property names are not allowed in method overloads. +tests/cases/conformance/es6/computedProperties/computedPropertyNamesOnOverloads.ts(5,5): error TS1168: Computed property names are not allowed in method overloads. + + +==== tests/cases/conformance/es6/computedProperties/computedPropertyNamesOnOverloads.ts (2 errors) ==== + var methodName = "method"; + var accessorName = "accessor"; + class C { + [methodName](v: string); + ~~~~~~~~~~~~ +!!! error TS1168: Computed property names are not allowed in method overloads. + [methodName](); + ~~~~~~~~~~~~ +!!! error TS1168: Computed property names are not allowed in method overloads. + [methodName](v?: string) { } + } \ No newline at end of file diff --git a/tests/baselines/reference/parserComputedPropertyName11.errors.txt b/tests/baselines/reference/parserComputedPropertyName11.errors.txt index a47865ba3f8..f47aa75031d 100644 --- a/tests/baselines/reference/parserComputedPropertyName11.errors.txt +++ b/tests/baselines/reference/parserComputedPropertyName11.errors.txt @@ -1,12 +1,9 @@ tests/cases/conformance/parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName11.ts(2,4): error TS1168: Computed property names are not allowed in method overloads. -tests/cases/conformance/parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName11.ts(2,4): error TS2391: Function implementation is missing or not immediately following the declaration. -==== tests/cases/conformance/parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName11.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName11.ts (1 errors) ==== class C { [e](); ~~~ !!! error TS1168: Computed property names are not allowed in method overloads. - ~~~ -!!! error TS2391: Function implementation is missing or not immediately following the declaration. } \ No newline at end of file diff --git a/tests/baselines/reference/parserES5ComputedPropertyName11.errors.txt b/tests/baselines/reference/parserES5ComputedPropertyName11.errors.txt index f37cf910a15..124db7da75a 100644 --- a/tests/baselines/reference/parserES5ComputedPropertyName11.errors.txt +++ b/tests/baselines/reference/parserES5ComputedPropertyName11.errors.txt @@ -1,12 +1,9 @@ tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName11.ts(2,4): error TS1168: Computed property names are not allowed in method overloads. -tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName11.ts(2,4): error TS2391: Function implementation is missing or not immediately following the declaration. -==== tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName11.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName11.ts (1 errors) ==== class C { [e](); ~~~ !!! error TS1168: Computed property names are not allowed in method overloads. - ~~~ -!!! error TS2391: Function implementation is missing or not immediately following the declaration. } \ No newline at end of file diff --git a/tests/cases/conformance/es6/computedProperties/computedPropertyNames1.ts b/tests/cases/conformance/es6/computedProperties/computedPropertyNames1.ts index b35ab29b129..b260bd8c539 100644 --- a/tests/cases/conformance/es6/computedProperties/computedPropertyNames1.ts +++ b/tests/cases/conformance/es6/computedProperties/computedPropertyNames1.ts @@ -1,5 +1,5 @@ // @target: es6 var v = { get [0 + 1]() { return 0 }, - set [0 + 1](v) { } + set [0 + 1](v: string) { } //No error } \ No newline at end of file