diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6adea60304d..a0e55520237 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7099,23 +7099,6 @@ module ts { return mapToArray(symbols); } - // True if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc) - function isTypeDeclarationName(name: Node): boolean { - return name.kind == SyntaxKind.Identifier && - isTypeDeclaration(name.parent) && - (name.parent).name === name; - } - - function isTypeDeclaration(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.TypeParameter: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - return true; - } - } - // True if the given identifier is part of a type reference function isTypeReferenceIdentifier(entityName: EntityName): boolean { var node: Node = entityName; @@ -7194,75 +7177,6 @@ module ts { return false; } - function isTypeNode(node: Node): boolean { - if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) { - return true; - } - - switch (node.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.BooleanKeyword: - return true; - case SyntaxKind.VoidKeyword: - return node.parent.kind !== SyntaxKind.PrefixOperator; - case SyntaxKind.StringLiteral: - // Specialized signatures can have string literals as their parameters' type names - return node.parent.kind === SyntaxKind.Parameter; - // Identifiers and qualified names may be type nodes, depending on their context. Climb - // above them to find the lowest container - case SyntaxKind.Identifier: - // If the identifier is the RHS of a qualified name, then it's a type iff its parent is. - if (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - // Fall through - case SyntaxKind.QualifiedName: - // At this point, node is either a qualified name or an identifier - var parent = node.parent; - if (parent.kind === SyntaxKind.TypeQuery) { - return false; - } - // Do not recursively call isTypeNode on the parent. In the example: - // - // var a: A.B.C; - // - // Calling isTypeNode would consider the qualified name A.B a type node. Only C or - // A.B.C is a type node. - if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) { - return true; - } - switch (parent.kind) { - case SyntaxKind.TypeParameter: - return node === (parent).constraint; - case SyntaxKind.Property: - case SyntaxKind.Parameter: - case SyntaxKind.VariableDeclaration: - return node === (parent).type; - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.Constructor: - case SyntaxKind.Method: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return node === (parent).type; - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - return node === (parent).type; - case SyntaxKind.TypeAssertion: - return node === (parent).type; - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - return (parent).typeArguments.indexOf(node) >= 0; - } - } - - return false; - } - function isInRightSideOfImportOrExportAssignment(node: EntityName) { while (node.parent.kind === SyntaxKind.QualifiedName) { node = node.parent; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9e4d5cd1da4..b9865772c06 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -402,6 +402,100 @@ module ts { return false; } + /** + * Note: this function only works when given a node with valid parent pointers. + */ + export function isTypeNode(node: Node): boolean { + if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) { + return true; + } + + switch (node.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.BooleanKeyword: + return true; + case SyntaxKind.VoidKeyword: + return node.parent.kind !== SyntaxKind.PrefixOperator; + case SyntaxKind.StringLiteral: + // Specialized signatures can have string literals as their parameters' type names + return node.parent.kind === SyntaxKind.Parameter; + // Identifiers and qualified names may be type nodes, depending on their context. Climb + // above them to find the lowest container + case SyntaxKind.Identifier: + // If the identifier is the RHS of a qualified name, then it's a type iff its parent is. + if (node.parent.kind === SyntaxKind.QualifiedName) { + node = node.parent; + } + // Fall through + case SyntaxKind.QualifiedName: + // At this point, node is either a qualified name or an identifier + var parent = node.parent; + if (parent.kind === SyntaxKind.TypeQuery) { + return false; + } + // Do not recursively call isTypeNode on the parent. In the example: + // + // var a: A.B.C; + // + // Calling isTypeNode would consider the qualified name A.B a type node. Only C or + // A.B.C is a type node. + if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) { + return true; + } + switch (parent.kind) { + case SyntaxKind.TypeParameter: + return node === (parent).constraint; + case SyntaxKind.Property: + case SyntaxKind.Parameter: + case SyntaxKind.VariableDeclaration: + return node === (parent).type; + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.Constructor: + case SyntaxKind.Method: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return node === (parent).type; + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + return node === (parent).type; + case SyntaxKind.TypeAssertion: + return node === (parent).type; + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + return (parent).typeArguments.indexOf(node) >= 0; + } + } + + return false; + } + + /** + * Note: this function only works when given a node with valid parent pointers. + * + * returns true if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc) + */ + export function isTypeDeclarationName(name: Node): boolean { + return name.kind == SyntaxKind.Identifier && + isTypeDeclaration(name.parent) && + (name.parent).name === name; + } + + + export function isTypeDeclaration(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.TypeParameter: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.EnumDeclaration: + return true; + } + } + export function getContainingFunction(node: Node): SignatureDeclaration { while (true) { node = node.parent; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d33b5c0715d..e746bd9e7c9 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1576,7 +1576,7 @@ module FourSlash { private verifyClassifications(expected: { classificationType: string; text: string }[], actual: ts.ClassifiedSpan[]) { if (actual.length !== expected.length) { - this.raiseError('verifySyntacticClassification failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length); + this.raiseError('verifyClassifications failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length); } for (var i = 0; i < expected.length; i++) { @@ -1585,7 +1585,7 @@ module FourSlash { var expectedType: string = (ts.ClassificationTypeNames)[expectedClassification.classificationType]; if (expectedType !== actualClassification.classificationType) { - this.raiseError('verifySyntacticClassification failed - expected classifications type to be ' + + this.raiseError('verifyClassifications failed - expected classifications type to be ' + expectedType + ', but was ' + actualClassification.classificationType); } @@ -1593,7 +1593,7 @@ module FourSlash { var actualSpan = actualClassification.textSpan; var actualText = this.activeFile.content.substr(actualSpan.start(), actualSpan.length()); if (expectedClassification.text !== actualText) { - this.raiseError('verifySyntacticClassification failed - expected classificatied text to be ' + + this.raiseError('verifyClassifications failed - expected classificatied text to be ' + expectedClassification.text + ', but was ' + actualText); } diff --git a/src/services/services.ts b/src/services/services.ts index ced52fd69c3..7463acc6c68 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4044,7 +4044,7 @@ module ts { return result; - function classifySymbol(symbol: Symbol) { + function classifySymbol(symbol: Symbol, isInTypePosition: boolean) { var flags = symbol.getFlags(); if (flags & SymbolFlags.Class) { @@ -4053,14 +4053,16 @@ module ts { else if (flags & SymbolFlags.Enum) { return ClassificationTypeNames.enumName; } - else if (flags & SymbolFlags.Interface) { - return ClassificationTypeNames.interfaceName; - } else if (flags & SymbolFlags.Module) { return ClassificationTypeNames.moduleName; } - else if (flags & SymbolFlags.TypeParameter) { - return ClassificationTypeNames.typeParameterName; + else if (isInTypePosition) { + if (flags & SymbolFlags.Interface) { + return ClassificationTypeNames.interfaceName; + } + else if (flags & SymbolFlags.TypeParameter) { + return ClassificationTypeNames.typeParameterName; + } } } @@ -4070,7 +4072,7 @@ module ts { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { var symbol = typeInfoResolver.getSymbolInfo(node); if (symbol) { - var type = classifySymbol(symbol); + var type = classifySymbol(symbol, isTypeNode(node) || isTypeDeclarationName(node)); if (type) { result.push({ textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()), diff --git a/tests/cases/fourslash/semanticClassification1.ts b/tests/cases/fourslash/semanticClassification1.ts index 0a0cc68250e..ad9e88ea842 100644 --- a/tests/cases/fourslash/semanticClassification1.ts +++ b/tests/cases/fourslash/semanticClassification1.ts @@ -6,7 +6,6 @@ //// } //// interface X extends M.I { } -debugger; var c = classification; verify.semanticClassificationsAre( c.moduleName("M"), c.interfaceName("I"), c.interfaceName("X"), c.moduleName("M"), c.interfaceName("I")); diff --git a/tests/cases/fourslash/semanticClassification2.ts b/tests/cases/fourslash/semanticClassification2.ts new file mode 100644 index 00000000000..810636dba3e --- /dev/null +++ b/tests/cases/fourslash/semanticClassification2.ts @@ -0,0 +1,11 @@ +/// + +//// interface Thing { +//// toExponential(): number; +//// } +//// +//// var Thing = 0; +//// Thing.toExponential(); + +var c = classification; +verify.semanticClassificationsAre(c.interfaceName("Thing")); \ No newline at end of file