From 573078c50c20dddf4300d51ac3ece3018ccc59f4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Oct 2014 14:50:10 -0700 Subject: [PATCH 1/7] Use 'getMeaningFromDeclaration' to classify identifiers. --- src/services/services.ts | 224 +++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 15295c0d054..8fa7863eb61 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1621,7 +1621,7 @@ module ts { (node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).externalModuleName === node)); } - enum SearchMeaning { + enum SemanticMeaning { None = 0x0, Value = 0x1, Type = 0x2, @@ -3315,7 +3315,7 @@ module ts { searchSymbol: Symbol, searchText: string, searchLocation: Node, - searchMeaning: SearchMeaning, + searchMeaning: SemanticMeaning, findInStrings: boolean, findInComments: boolean, result: ReferenceEntry[]): void { @@ -3613,114 +3613,6 @@ module ts { return undefined; } - function getMeaningFromDeclaration(node: Declaration): SearchMeaning { - switch (node.kind) { - case SyntaxKind.Parameter: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Property: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.EnumMember: - case SyntaxKind.Method: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.CatchBlock: - return SearchMeaning.Value; - - case SyntaxKind.TypeParameter: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeLiteral: - return SearchMeaning.Type; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - return SearchMeaning.Value | SearchMeaning.Type; - - case SyntaxKind.ModuleDeclaration: - if ((node).name.kind === SyntaxKind.StringLiteral) { - return SearchMeaning.Namespace | SearchMeaning.Value; - } - else if (isInstantiated(node)) { - return SearchMeaning.Namespace | SearchMeaning.Value; - } - else { - return SearchMeaning.Namespace; - } - break; - - case SyntaxKind.ImportDeclaration: - return SearchMeaning.Value | SearchMeaning.Type | SearchMeaning.Namespace; - } - Debug.fail("Unknown declaration type"); - } - - function isTypeReference(node: Node): boolean { - if (node.parent.kind === SyntaxKind.QualifiedName && (node.parent).right === node) - node = node.parent; - - return node.parent.kind === SyntaxKind.TypeReference; - } - - function isNamespaceReference(node: Node): boolean { - var root = node; - var isLastClause = true; - if (root.parent.kind === SyntaxKind.QualifiedName) { - while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) - root = root.parent; - - isLastClause = (root).right === node; - } - - return root.parent.kind === SyntaxKind.TypeReference && !isLastClause; - } - - function isInRightSideOfImport(node: EntityName) { - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - - return node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).entityName === node; - } - - function getMeaningFromRightHandSideOfImport(node: Node) { - Debug.assert(node.kind === SyntaxKind.Identifier); - - // import a = |b|; // Namespace - // import a = |b.c|; // Value, type, namespace - // import a = |b.c|.d; // Namespace - - if (node.parent.kind === SyntaxKind.QualifiedName && - (node.parent).right === node && - node.parent.parent.kind === SyntaxKind.ImportDeclaration) { - return SearchMeaning.Value | SearchMeaning.Type | SearchMeaning.Namespace; - } - return SearchMeaning.Namespace; - } - - function getMeaningFromLocation(node: Node): SearchMeaning { - if (node.parent.kind === SyntaxKind.ExportAssignment) { - return SearchMeaning.Value | SearchMeaning.Type | SearchMeaning.Namespace; - } - else if (isInRightSideOfImport(node)) { - return getMeaningFromRightHandSideOfImport(node); - } - else if (isDeclarationOrFunctionExpressionOrCatchVariableName(node)) { - return getMeaningFromDeclaration(node.parent); - } - else if (isTypeReference(node)) { - return SearchMeaning.Type; - } - else if (isNamespaceReference(node)) { - return SearchMeaning.Namespace; - } - else { - return SearchMeaning.Value; - } - } - /** Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations * of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class * then we need to widen the search to include type positions as well. @@ -3728,7 +3620,7 @@ module ts { * module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module) * do not intersect in any of the three spaces. */ - function getIntersectingMeaningFromDeclarations(meaning: SearchMeaning, declarations: Declaration[]): SearchMeaning { + function getIntersectingMeaningFromDeclarations(meaning: SemanticMeaning, declarations: Declaration[]): SemanticMeaning { if (declarations) { do { // The result is order-sensitive, for instance if initialMeaning === Namespace, and declarations = [class, instantiated module] @@ -3937,6 +3829,114 @@ module ts { return emitOutput; } + function getMeaningFromDeclaration(node: Declaration): SemanticMeaning { + switch (node.kind) { + case SyntaxKind.Parameter: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Property: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.EnumMember: + case SyntaxKind.Method: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.CatchBlock: + return SemanticMeaning.Value; + + case SyntaxKind.TypeParameter: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeLiteral: + return SemanticMeaning.Type; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + return SemanticMeaning.Value | SemanticMeaning.Type; + + case SyntaxKind.ModuleDeclaration: + if ((node).name.kind === SyntaxKind.StringLiteral) { + return SemanticMeaning.Namespace | SemanticMeaning.Value; + } + else if (isInstantiated(node)) { + return SemanticMeaning.Namespace | SemanticMeaning.Value; + } + else { + return SemanticMeaning.Namespace; + } + break; + + case SyntaxKind.ImportDeclaration: + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + Debug.fail("Unknown declaration type"); + } + + function isTypeReference(node: Node): boolean { + if (node.parent.kind === SyntaxKind.QualifiedName && (node.parent).right === node) + node = node.parent; + + return node.parent.kind === SyntaxKind.TypeReference; + } + + function isNamespaceReference(node: Node): boolean { + var root = node; + var isLastClause = true; + if (root.parent.kind === SyntaxKind.QualifiedName) { + while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) + root = root.parent; + + isLastClause = (root).right === node; + } + + return root.parent.kind === SyntaxKind.TypeReference && !isLastClause; + } + + function isInRightSideOfImport(node: EntityName) { + while (node.parent.kind === SyntaxKind.QualifiedName) { + node = node.parent; + } + + return node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).entityName === node; + } + + function getMeaningFromRightHandSideOfImport(node: Node) { + Debug.assert(node.kind === SyntaxKind.Identifier); + + // import a = |b|; // Namespace + // import a = |b.c|; // Value, type, namespace + // import a = |b.c|.d; // Namespace + + if (node.parent.kind === SyntaxKind.QualifiedName && + (node.parent).right === node && + node.parent.parent.kind === SyntaxKind.ImportDeclaration) { + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + return SemanticMeaning.Namespace; + } + + function getMeaningFromLocation(node: Node): SemanticMeaning { + if (node.parent.kind === SyntaxKind.ExportAssignment) { + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + else if (isInRightSideOfImport(node)) { + return getMeaningFromRightHandSideOfImport(node); + } + else if (isDeclarationOrFunctionExpressionOrCatchVariableName(node)) { + return getMeaningFromDeclaration(node.parent); + } + else if (isTypeReference(node)) { + return SemanticMeaning.Type; + } + else if (isNamespaceReference(node)) { + return SemanticMeaning.Namespace; + } + else { + return SemanticMeaning.Value; + } + } + // Signature help /** * This is a semantic operation. @@ -4133,7 +4133,7 @@ module ts { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { var symbol = typeInfoResolver.getSymbolInfo(node); if (symbol) { - var type = classifySymbol(symbol, isTypeNode(node) || isTypeDeclarationName(node)); + var type = classifySymbol(symbol, getMeaningFromLocation(node) === SemanticMeaning.Type); if (type) { result.push({ textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()), From eb5d378d55ca6ceb232e8c29bcd59d2b79aa1ccc Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Oct 2014 15:03:50 -0700 Subject: [PATCH 2/7] Reverted code back to original location since these functions no longer need to be exported. --- src/compiler/checker.ts | 85 +++++++++++++++++++++++++++++++++++++ src/compiler/parser.ts | 94 ----------------------------------------- 2 files changed, 85 insertions(+), 94 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33d4008885d..0158c71f7a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7130,6 +7130,22 @@ module ts { return mapToArray(symbols); } + 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; @@ -7208,6 +7224,75 @@ 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 && (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 422d133d5d0..15f73c4ec3a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -391,100 +391,6 @@ 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 && (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; From 66e1e47d2e261a15284173a4cb748e0f5e4ce8ab Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 7 Oct 2014 14:06:21 -0700 Subject: [PATCH 3/7] Use local meaning for variables in quick info. This means we no longer indicate a variable is an interface if it shares the a name with one. --- src/services/services.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 8fa7863eb61..2d7f2cce00d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2395,11 +2395,6 @@ module ts { totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.Interface) { - totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); - totalParts.push(spacePart()); - totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); - } else if (symbol.flags & SymbolFlags.Enum) { totalParts.push(keywordPart(SyntaxKind.EnumKeyword)); totalParts.push(spacePart()); @@ -2410,12 +2405,19 @@ module ts { totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.TypeParameter) { - totalParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - totalParts.push(new SymbolDisplayPart("type parameter", SymbolDisplayPartKind.text, undefined)); - totalParts.push(punctuationPart(SyntaxKind.CloseParenToken)); - totalParts.push(spacePart()); - totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol)); + else if (getMeaningFromLocation(node) === SemanticMeaning.Type) { + if (symbol.flags & SymbolFlags.Interface) { + totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); + totalParts.push(spacePart()); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); + } + else if (symbol.flags & SymbolFlags.TypeParameter) { + totalParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + totalParts.push(new SymbolDisplayPart("type parameter", SymbolDisplayPartKind.text, undefined)); + totalParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + totalParts.push(spacePart()); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol)); + } } else { totalParts.push(punctuationPart(SyntaxKind.OpenParenToken)); From f68197f52043ce88e7751f4942e50ffb4f37f6a0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 7 Oct 2014 14:36:44 -0700 Subject: [PATCH 4/7] Properly use a bit test instead of equality, due to intersecting meanings. --- src/services/services.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 2d7f2cce00d..246aaad201a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2405,7 +2405,7 @@ module ts { totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (getMeaningFromLocation(node) === SemanticMeaning.Type) { + else if (getMeaningFromLocation(node) & SemanticMeaning.Type) { if (symbol.flags & SymbolFlags.Interface) { totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); totalParts.push(spacePart()); @@ -4107,7 +4107,7 @@ module ts { return result; - function classifySymbol(symbol: Symbol, isInTypePosition: boolean) { + function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning) { var flags = symbol.getFlags(); if (flags & SymbolFlags.Class) { @@ -4119,7 +4119,7 @@ module ts { else if (flags & SymbolFlags.Module) { return ClassificationTypeNames.moduleName; } - else if (isInTypePosition) { + else if (meaningAtPosition & SemanticMeaning.Type) { if (flags & SymbolFlags.Interface) { return ClassificationTypeNames.interfaceName; } @@ -4135,7 +4135,7 @@ module ts { if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) { var symbol = typeInfoResolver.getSymbolInfo(node); if (symbol) { - var type = classifySymbol(symbol, getMeaningFromLocation(node) === SemanticMeaning.Type); + var type = classifySymbol(symbol, getMeaningFromLocation(node)); if (type) { result.push({ textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()), From 712e877762bc9076b54ffceb1232d42d44a3b095 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 8 Oct 2014 12:42:34 -0700 Subject: [PATCH 5/7] Make 'getSymbolKind' semantically sensitive to the location of requests. --- src/services/services.ts | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 246aaad201a..a0d704cba7f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1082,9 +1082,9 @@ module ts { filename: string; // the file where the completion was requested position: number; // position in the file where the completion was requested entries: CompletionEntry[]; // entries for this completion - symbols: Map; // symbols by entry name map - location: Node; // the node where the completion was requested - typeChecker: TypeChecker;// the typeChecker used to generate this completion + symbols: Map; // symbols by entry name map + location: Node; // the node where the completion was requested + typeChecker: TypeChecker; // the typeChecker used to generate this completion } interface FormattingOptions { @@ -1626,6 +1626,7 @@ module ts { Value = 0x1, Type = 0x2, Namespace = 0x4 + All = Value | Type | Namespace } enum BreakContinueSearchType { @@ -1916,16 +1917,20 @@ module ts { return undefined; } + // TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind' + // which is permissible given that it is backwards compatible; but really we should consider + // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. + // We COULD also just do the "lazy" thing that 'getSymbolModifiers' does which is to use the first declaration. return { name: displayName, - kind: getSymbolKind(symbol), + kind: getSymbolKind(symbol, SemanticMeaning.All), kindModifiers: getSymbolModifiers(symbol) }; } function getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) { function getCompletionEntriesFromSymbols(symbols: Symbol[], session: CompletionSession): void { - forEach(symbols, (symbol) => { + forEach(symbols, symbol => { var entry = createCompletionEntry(symbol); if (entry && !lookUp(session.symbols, entry.name)) { session.entries.push(entry); @@ -2303,12 +2308,11 @@ module ts { } } - function getSymbolKind(symbol: Symbol): string { + function getSymbolKind(symbol: Symbol, meaningAtLocation: SemanticMeaning): string { var flags = typeInfoResolver.getRootSymbol(symbol).getFlags(); if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; - if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; if (flags & SymbolFlags.Variable) return ScriptElementKind.variableElement; if (flags & SymbolFlags.Function) return ScriptElementKind.functionElement; @@ -2320,10 +2324,14 @@ module ts { if (flags & SymbolFlags.ConstructSignature) return ScriptElementKind.constructSignatureElement; if (flags & SymbolFlags.CallSignature) return ScriptElementKind.callSignatureElement; if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; - if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; if (flags & SymbolFlags.EnumMember) return ScriptElementKind.variableElement; if (flags & SymbolFlags.Import) return ScriptElementKind.alias; + if (meaningAtLocation & SemanticMeaning.Type) { + if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + } + return ScriptElementKind.unknown; } @@ -2379,6 +2387,8 @@ module ts { return undefined; } + var semanticMeaning = getMeaningFromLocation(node); + var symbol = typeInfoResolver.getSymbolInfo(node); if (!symbol) { return undefined; @@ -2405,7 +2415,7 @@ module ts { totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (getMeaningFromLocation(node) & SemanticMeaning.Type) { + else if (semanticMeaning & SemanticMeaning.Type) { if (symbol.flags & SymbolFlags.Interface) { totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); totalParts.push(spacePart()); @@ -2471,7 +2481,7 @@ module ts { } return { - kind: getSymbolKind(symbol), + kind: getSymbolKind(symbol, semanticMeaning), kindModifiers: getSymbolModifiers(symbol), textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()), displayParts: totalParts, @@ -2496,7 +2506,7 @@ module ts { memberName: new TypeScript.MemberNameString(typeInfoResolver.typeToString(type)), docComment: "", fullSymbolName: typeInfoResolver.symbolToString(symbol, getContainerNode(node)), - kind: getSymbolKind(symbol), + kind: getSymbolKind(symbol, SemanticMeaning.Type), textSpan: TypeScript.TextSpan.fromBounds(node.pos, node.end) }; } @@ -2609,10 +2619,10 @@ module ts { var declarations = symbol.getDeclarations(); var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - var symbolKind = getSymbolKind(symbol); + var symbolKind = getSymbolKind(symbol, getMeaningFromLocation(node)); var containerSymbol = symbol.parent; var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - var containerKind = containerSymbol ? getSymbolKind(symbol) : ""; + var containerKind = containerSymbol ? getSymbolKind(symbol, SemanticMeaning.Namespace) : ""; if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { @@ -4596,7 +4606,7 @@ module ts { // Only allow a symbol to be renamed if it actually has at least one declaration. if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { - var kind = getSymbolKind(symbol); + var kind = getSymbolKind(symbol, getMeaningFromLocation(node)); if (kind) { return getRenameInfo(symbol.name, typeInfoResolver.getFullyQualifiedName(symbol), kind, getSymbolModifiers(symbol), From 3debebe555b3d6e00840bd3c70593fc69714fe08 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 8 Oct 2014 14:23:23 -0700 Subject: [PATCH 6/7] Fixed ordering, syntax, addressed CR feedback, made things work. --- src/services/services.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index a0d704cba7f..40b736a71e9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1625,7 +1625,7 @@ module ts { None = 0x0, Value = 0x1, Type = 0x2, - Namespace = 0x4 + Namespace = 0x4, All = Value | Type | Namespace } @@ -1920,7 +1920,7 @@ module ts { // TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind' // which is permissible given that it is backwards compatible; but really we should consider // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. - // We COULD also just do the "lazy" thing that 'getSymbolModifiers' does which is to use the first declaration. + // We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration. return { name: displayName, kind: getSymbolKind(symbol, SemanticMeaning.All), @@ -2311,9 +2311,17 @@ module ts { function getSymbolKind(symbol: Symbol, meaningAtLocation: SemanticMeaning): string { var flags = typeInfoResolver.getRootSymbol(symbol).getFlags(); - if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; + + // The following should only apply if encountered at a type position, + // and need to have precedence over other meanings if this is the case. + if (meaningAtLocation & SemanticMeaning.Type) { + if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + } + + if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; if (flags & SymbolFlags.Variable) return ScriptElementKind.variableElement; if (flags & SymbolFlags.Function) return ScriptElementKind.functionElement; if (flags & SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement; @@ -2327,11 +2335,6 @@ module ts { if (flags & SymbolFlags.EnumMember) return ScriptElementKind.variableElement; if (flags & SymbolFlags.Import) return ScriptElementKind.alias; - if (meaningAtLocation & SemanticMeaning.Type) { - if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; - if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; - } - return ScriptElementKind.unknown; } @@ -2622,7 +2625,6 @@ module ts { var symbolKind = getSymbolKind(symbol, getMeaningFromLocation(node)); var containerSymbol = symbol.parent; var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - var containerKind = containerSymbol ? getSymbolKind(symbol, SemanticMeaning.Namespace) : ""; if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { @@ -4126,9 +4128,6 @@ module ts { else if (flags & SymbolFlags.Enum) { return ClassificationTypeNames.enumName; } - else if (flags & SymbolFlags.Module) { - return ClassificationTypeNames.moduleName; - } else if (meaningAtPosition & SemanticMeaning.Type) { if (flags & SymbolFlags.Interface) { return ClassificationTypeNames.interfaceName; @@ -4137,6 +4136,9 @@ module ts { return ClassificationTypeNames.typeParameterName; } } + else if (flags & SymbolFlags.Module) { + return ClassificationTypeNames.moduleName; + } } function processNode(node: Node) { From eb3106d2b75a56c8ce2eba22f4fddb49b50adea5 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 8 Oct 2014 15:44:49 -0700 Subject: [PATCH 7/7] CR feedback on the checker --- src/compiler/checker.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0158c71f7a3..2a69f16ccc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7225,7 +7225,7 @@ module ts { } function isTypeNode(node: Node): boolean { - if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) { + if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { return true; } @@ -7240,6 +7240,7 @@ module ts { 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: @@ -7247,9 +7248,11 @@ module ts { if (node.parent.kind === SyntaxKind.QualifiedName) { node = node.parent; } - // Fall through + // fall through case SyntaxKind.QualifiedName: // At this point, node is either a qualified name or an identifier + Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName, "'node' was expected to be a qualified name or identifier in 'isTypeNode'."); + var parent = node.parent; if (parent.kind === SyntaxKind.TypeQuery) { return false; @@ -7260,7 +7263,7 @@ module ts { // // 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) { + if (SyntaxKind.FirstTypeNode <= parent.kind && parent.kind <= SyntaxKind.LastTypeNode) { return true; } switch (parent.kind) {