Checker refactor for FunctionLike and VariableOrProperty

This commit is contained in:
Jason Freeman 2014-11-19 15:19:18 -08:00
parent 2bc1f4f4fa
commit f6266fc99e
10 changed files with 134 additions and 88 deletions

View File

@ -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 '"' + (<LiteralExpression>node.name).text + '"';
}
return (<Identifier>node.name).text;
Debug.assert(node.name.kind !== SyntaxKind.ComputedPropertyName);
return (<Identifier | LiteralExpression>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));

View File

@ -1663,7 +1663,7 @@ module ts {
if (declaration.kind === SyntaxKind.Parameter) {
var func = <FunctionLikeDeclaration>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 = <AccessorDeclaration>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 = <AccessorDeclaration>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 = <AccessorDeclaration>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 = <AccessorDeclaration>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>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(<VariableDeclaration>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((<ParenTypeNode>node).type);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionLikeDeclaration>node);
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:
return checkBlock(<Block>node);
case SyntaxKind.FunctionBlock:

View File

@ -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 "[<expr>]", where <expr> is the source
// text of the expression in the computed property.
export function declarationNameToString(name: DeclarationName) {
return name.kind === SyntaxKind.Missing ? "(Missing)" : getTextOfNode(name);
}

View File

@ -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
}

View File

@ -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
};

View File

@ -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
}

View File

@ -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) { }
}

View File

@ -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.
}

View File

@ -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.
}

View File

@ -1,5 +1,5 @@
// @target: es6
var v = {
get [0 + 1]() { return 0 },
set [0 + 1](v) { }
set [0 + 1](v: string) { } //No error
}