diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index eaad8593eee..e403ed994e5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -630,7 +630,7 @@ namespace ts { function getStrictModeIdentifierMessage(node: Node) { // Provide specialized messages to help the user understand why we think they're in // strict mode. - if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) { + if (getContainingClass(node)) { return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; } @@ -688,7 +688,7 @@ namespace ts { function getStrictModeEvalOrArgumentsMessage(node: Node) { // Provide specialized messages to help the user understand why we think they're in // strict mode. - if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) { + if (getContainingClass(node)) { return Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode; } @@ -1031,7 +1031,7 @@ namespace ts { // containing class. if (node.flags & NodeFlags.AccessibilityModifier && node.parent.kind === SyntaxKind.Constructor && - (node.parent.parent.kind === SyntaxKind.ClassDeclaration || node.parent.parent.kind === SyntaxKind.ClassExpression)) { + isClassLike(node.parent.parent)) { let classDeclaration = node.parent.parent; declareSymbol(classDeclaration.symbol.members, classDeclaration.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index efc3e32166e..232a3db419a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -386,8 +386,8 @@ namespace ts { // local variables of the constructor. This effectively means that entities from outer scopes // by the same name as a constructor parameter or local variable are inaccessible // in initializer expressions for instance member variables. - if (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) { - let ctor = findConstructorDeclaration(location.parent); + if (isClassLike(location.parent) && !(location.flags & NodeFlags.Static)) { + let ctor = findConstructorDeclaration(location.parent); if (ctor && ctor.locals) { if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) { // Remember the property node, it will be used later to report appropriate error @@ -397,6 +397,7 @@ namespace ts { } break; case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { if (lastLocation && lastLocation.flags & NodeFlags.Static) { @@ -420,7 +421,7 @@ namespace ts { // case SyntaxKind.ComputedPropertyName: grandparent = location.parent.parent; - if (grandparent.kind === SyntaxKind.ClassDeclaration || grandparent.kind === SyntaxKind.InterfaceDeclaration) { + if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) { // A reference to this grandparent's type parameters would be an error if (result = getSymbol(getSymbolOfNode(grandparent).members, name, meaning & SymbolFlags.Type)) { error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type); @@ -1009,7 +1010,7 @@ namespace ts { return false; } - function findConstructorDeclaration(node: ClassDeclaration): ConstructorDeclaration { + function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration { let members = node.members; for (let member of members) { if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) { @@ -2562,10 +2563,10 @@ namespace ts { if (!node) { return typeParameters; } - if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.FunctionDeclaration || - node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.MethodDeclaration || - node.kind === SyntaxKind.ArrowFunction) { - let declarations = (node).typeParameters; + if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || + node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression || + node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) { + let declarations = (node).typeParameters; if (declarations) { return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations); } @@ -2575,8 +2576,8 @@ namespace ts { // The outer type parameters are those defined by enclosing generic classes, methods, or functions. function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] { - var kind = symbol.flags & SymbolFlags.Class ? SyntaxKind.ClassDeclaration : SyntaxKind.InterfaceDeclaration; - return appendOuterTypeParameters(undefined, getDeclarationOfKind(symbol, kind)); + var declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); + return appendOuterTypeParameters(undefined, declaration); } // The local type parameters are the combined set of type parameters from all declarations of the class, @@ -2584,7 +2585,8 @@ namespace ts { function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] { let result: TypeParameter[]; for (let node of symbol.declarations) { - if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.TypeAliasDeclaration) { + if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration || + node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) { let declaration = node; if (declaration.typeParameters) { result = appendTypeParameters(result, declaration.typeParameters); @@ -5884,9 +5886,9 @@ namespace ts { } function captureLexicalThis(node: Node, container: Node): void { - let classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined; getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis; if (container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.Constructor) { + let classNode = container.parent; getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis; } else { @@ -5939,7 +5941,7 @@ namespace ts { captureLexicalThis(node, container); } - let classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined; + let classNode = isClassLike(container.parent) ? container.parent : undefined; if (classNode) { let symbol = getSymbolOfNode(classNode); return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol); @@ -5958,7 +5960,7 @@ namespace ts { function checkSuperExpression(node: Node): Type { let isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (node.parent).expression === node; - let classDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration); + let classDeclaration = getContainingClass(node); let classType = classDeclaration && getDeclaredTypeOfSymbol(getSymbolOfNode(classDeclaration)); let baseClassType = classType && getBaseTypes(classType)[0]; @@ -5993,7 +5995,7 @@ namespace ts { } // topmost container must be something that is directly nested in the class declaration - if (container && container.parent && container.parent.kind === SyntaxKind.ClassDeclaration) { + if (container && isClassLike(container.parent)) { if (container.flags & NodeFlags.Static) { canUseSuperExpression = container.kind === SyntaxKind.MethodDeclaration || @@ -6652,7 +6654,7 @@ namespace ts { } // Property is known to be private or protected at this point // Get the declaring and enclosing class instance types - let enclosingClassDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration); + let enclosingClassDeclaration = getContainingClass(node); let enclosingClass = enclosingClassDeclaration ? getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)) : undefined; let declaringClass = getDeclaredTypeOfSymbol(prop.parent); // Private property is accessible if declaring and enclosing class are the same @@ -7426,7 +7428,7 @@ namespace ts { if (superType !== unknownType) { // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated // with the type arguments specified in the extends clause. - let baseTypeNode = getClassExtendsHeritageClauseElement(getAncestor(node, SyntaxKind.ClassDeclaration)); + let baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node)); let baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments); return resolveCall(node, baseConstructors, candidatesOutArray); } @@ -8354,7 +8356,7 @@ namespace ts { if (isFunctionLike(parent) && current === (parent).body) { return false; } - else if (current.kind === SyntaxKind.ClassDeclaration || current.kind === SyntaxKind.ClassExpression) { + else if (isClassLike(current)) { return true; } @@ -9648,7 +9650,7 @@ namespace ts { } // bubble up and find containing type - let enclosingClass = getAncestor(node, SyntaxKind.ClassDeclaration); + let enclosingClass = getContainingClass(node); // if containing type was not found or it is ambient - exit (no codegen) if (!enclosingClass || isInAmbientContext(enclosingClass)) { return; @@ -10456,8 +10458,8 @@ namespace ts { checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); }); - if (type.flags & TypeFlags.Class && type.symbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration) { - let classDeclaration = type.symbol.valueDeclaration; + if (type.flags & TypeFlags.Class && isClassLike(type.symbol.valueDeclaration)) { + let classDeclaration = type.symbol.valueDeclaration; for (let member of classDeclaration.members) { // Only process instance properties with computed names here. // Static properties cannot be in conflict with indexers, @@ -11727,6 +11729,11 @@ namespace ts { case SyntaxKind.EnumDeclaration: copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember); break; + case SyntaxKind.ClassExpression: + if ((location).name) { + copySymbol(location.symbol, meaning); + } + // Fall through case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: if (!(memberFlags & NodeFlags.Static)) { @@ -11766,42 +11773,6 @@ namespace ts { } } } - - if (isInsideWithStatementBody(location)) { - // We cannot answer semantic questions within a with block, do not proceed any further - return []; - } - - while (location) { - if (location.locals && !isGlobalSourceFile(location)) { - copySymbols(location.locals, meaning); - } - switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalModule(location)) break; - case SyntaxKind.ModuleDeclaration: - copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember); - break; - case SyntaxKind.EnumDeclaration: - copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember); - break; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - if (!(memberFlags & NodeFlags.Static)) { - copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type); - } - break; - case SyntaxKind.FunctionExpression: - if ((location).name) { - copySymbol(location.symbol, meaning); - } - break; - } - memberFlags = location.flags; - location = location.parent; - } - copySymbols(globals, meaning); - return symbolsToArray(symbols); } function isTypeDeclarationName(name: Node): boolean { @@ -13213,7 +13184,7 @@ namespace ts { } } - if (node.parent.kind === SyntaxKind.ClassDeclaration) { + if (isClassLike(node.parent)) { if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional)) { return true; } @@ -13505,7 +13476,7 @@ namespace ts { } function checkGrammarProperty(node: PropertyDeclaration) { - if (node.parent.kind === SyntaxKind.ClassDeclaration) { + if (isClassLike(node.parent)) { if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional) || checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) { return true; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e0fc5fc0269..7f9888c73d3 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -542,6 +542,7 @@ namespace ts { case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: // These are not allowed inside a generator now, but eventually they may be allowed // as local types. Regardless, any yield statements contained within them should be // skipped in this traversal. @@ -579,26 +580,15 @@ namespace ts { return true; } } - return false; } export function isAccessor(node: Node): boolean { - if (node) { - switch (node.kind) { - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return true; - } - } - - return false; + return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor); } export function isClassLike(node: Node): boolean { - if (node) { - return node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression; - } + return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression); } export function isFunctionLike(node: Node): boolean { @@ -620,7 +610,6 @@ namespace ts { return true; } } - return false; } @@ -641,6 +630,15 @@ namespace ts { } } + export function getContainingClass(node: Node): ClassLikeDeclaration { + while (true) { + node = node.parent; + if (!node || isClassLike(node)) { + return node; + } + } + } + export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { while (true) { node = node.parent; @@ -653,7 +651,7 @@ namespace ts { // then the computed property is not a 'this' container. // A computed property name in a class needs to be a this container // so that we can error on it. - if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { + if (isClassLike(node.parent.parent)) { return node; } // If this is a computed property, then the parent should not @@ -708,7 +706,7 @@ namespace ts { // then the computed property is not a 'super' container. // A computed property name in a class needs to be a super container // so that we can error on it. - if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { + if (isClassLike(node.parent.parent)) { return node; } // If this is a computed property, then the parent should not @@ -1087,6 +1085,7 @@ namespace ts { case SyntaxKind.ArrowFunction: case SyntaxKind.BindingElement: case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: case SyntaxKind.Constructor: case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumMember: @@ -1917,7 +1916,7 @@ namespace ts { export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { return node.kind === SyntaxKind.ExpressionWithTypeArguments && (node.parent).token === SyntaxKind.ExtendsKeyword && - node.parent.parent.kind === SyntaxKind.ClassDeclaration; + isClassLike(node.parent.parent); } // Returns false if this heritage clause element's expression contains something unsupported