When adding completions for a module, don't get the type of the module if not necessary. (#16768)

* When adding completions for a module, don't get the type of the module if not necessary.

* Use SymbolFlags.Module alias
This commit is contained in:
Andy 2017-07-13 09:20:40 -07:00 committed by GitHub
parent 6880ee33a3
commit 69d3ca774a
5 changed files with 74 additions and 36 deletions

View file

@ -3604,6 +3604,10 @@ namespace ts {
return previous[previous.length - 1];
}
export function skipAlias(symbol: Symbol, checker: TypeChecker) {
return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
}
/** See comment on `declareModuleMember` in `binder.ts`. */
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;

View file

@ -222,10 +222,7 @@ namespace ts.codefix {
}
function getUniqueSymbolId(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
}
return getSymbolId(symbol);
return getSymbolId(skipAlias(symbol, checker));
}
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {

View file

@ -596,46 +596,48 @@ namespace ts.Completions {
isNewIdentifierLocation = false;
// Since this is qualified name check its a type node location
const isTypeLocation = isPartOfTypeNode(node.parent) || insideJsDocTagTypeExpression;
const isTypeLocation = insideJsDocTagTypeExpression || isPartOfTypeNode(node.parent);
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) {
if (isEntityName(node)) {
let symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
symbol = skipAlias(symbol, typeChecker);
// This is an alias, follow what it aliases
if (symbol && symbol.flags & SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
if (symbol && symbol.flags & SymbolFlags.HasExports) {
// Extract module or enum members
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
const isValidAccess = isRhsOfImportDeclaration ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
forEach(exportedSymbols, symbol => {
if (isValidAccess(symbol)) {
symbols.push(symbol);
if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
// Extract module or enum members
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
const isValidAccess = isRhsOfImportDeclaration ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
for (const symbol of exportedSymbols) {
if (isValidAccess(symbol)) {
symbols.push(symbol);
}
}
});
// If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods).
if (!isTypeLocation && symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node));
}
return;
}
}
}
if (!isTypeLocation) {
const type = typeChecker.getTypeAtLocation(node);
if (type) addTypeProperties(type);
addTypeProperties(typeChecker.getTypeAtLocation(node));
}
}
function addTypeProperties(type: Type) {
if (type) {
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
symbols.push(symbol);
}
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
symbols.push(symbol);
}
}
@ -811,15 +813,13 @@ namespace ts.Completions {
symbol = symbol.exportSymbol || symbol;
// This is an alias, follow what it aliases
if (symbol && symbol.flags & SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
symbol = skipAlias(symbol, typeChecker);
if (symbol.flags & SymbolFlags.Type) {
return true;
}
if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) {
if (symbol.flags & SymbolFlags.Module) {
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
// If the exported symbols contains type,
// symbol can be referenced at locations where type is allowed

View file

@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
////class C {
//// static m() { }
////}
////
////class D extends C {}
////namespace D {
//// export type T = number;
////}
////
////let x: D./*type*/;
////D./*value*/
goTo.marker("type");
verify.completionListContains("T");
verify.not.completionListContains("m");
goTo.marker("value");
verify.not.completionListContains("T");
verify.completionListContains("m");

View file

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
////namespace N {
//// export type T = number;
////}
////const N = { m() {} };
////let x: N./*type*/;
////N./*value*/;
goTo.marker("type");
verify.completionListContains("T");
verify.not.completionListContains("m");
goTo.marker("value");
verify.not.completionListContains("T");
verify.completionListContains("m");