mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-16 15:51:35 -05:00
Filter symbols based on the meaning at the location
This commit is contained in:
@@ -14554,12 +14554,31 @@ namespace ts {
|
||||
? (<PropertyAccessExpression>node).expression
|
||||
: (<QualifiedName>node).left;
|
||||
|
||||
const type = checkExpression(left);
|
||||
return isValidPropertyAccessWithType(node, left, propertyName, getWidenedType(checkExpression(left)));
|
||||
}
|
||||
|
||||
function isValidPropertyAccessWithType(
|
||||
node: PropertyAccessExpression | QualifiedName,
|
||||
left: LeftHandSideExpression | QualifiedName,
|
||||
propertyName: string,
|
||||
type: Type): boolean {
|
||||
|
||||
if (type !== unknownType && !isTypeAny(type)) {
|
||||
const prop = getPropertyOfType(getWidenedType(type), propertyName);
|
||||
const prop = getPropertyOfType(type, propertyName);
|
||||
if (prop) {
|
||||
return checkPropertyAccessibility(node, left, type, prop);
|
||||
}
|
||||
|
||||
// In js files properties of unions are allowed in completion
|
||||
if (isInJavaScriptFile(left) && (type.flags & TypeFlags.Union)) {
|
||||
for (const elementType of (<UnionType>type).types) {
|
||||
if (isValidPropertyAccessWithType(node, left, propertyName, elementType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -559,6 +559,9 @@ namespace ts.Completions {
|
||||
isMemberCompletion = true;
|
||||
isNewIdentifierLocation = false;
|
||||
|
||||
// Since this is qualified name check its a type node location
|
||||
const isTypeLocation = isPartOfTypeNode(node.parent);
|
||||
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
|
||||
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
let symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
@@ -570,16 +573,24 @@ namespace ts.Completions {
|
||||
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.name);
|
||||
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 (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.name)) {
|
||||
if (isValidAccess(symbol)) {
|
||||
symbols.push(symbol);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const type = typeChecker.getTypeAtLocation(node);
|
||||
addTypeProperties(type);
|
||||
if (!isTypeLocation) {
|
||||
const type = typeChecker.getTypeAtLocation(node);
|
||||
addTypeProperties(type);
|
||||
}
|
||||
}
|
||||
|
||||
function addTypeProperties(type: Type) {
|
||||
@@ -687,13 +698,88 @@ namespace ts.Completions {
|
||||
isStatement(scopeNode);
|
||||
}
|
||||
|
||||
/// TODO filter meaning based on the current context
|
||||
const symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings);
|
||||
symbols = filterGlobalCompletion(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function filterGlobalCompletion(symbols: Symbol[]) {
|
||||
return filter(symbols, symbol => {
|
||||
if (!isSourceFile(location)) {
|
||||
// export = /**/ here we want to get all meanings, so any symbol is ok
|
||||
if (isExportAssignment(location.parent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is an alias, follow what it aliases
|
||||
if (symbol && symbol.flags & SymbolFlags.Alias) {
|
||||
symbol = typeChecker.getAliasedSymbol(symbol);
|
||||
}
|
||||
|
||||
// import m = /**/ <-- It can only access namespace (if typing import = x. this would get member symbols and not namespace)
|
||||
if (isInRightSideOfInternalImportEqualsDeclaration(location)) {
|
||||
return !!(symbol.flags & SymbolFlags.Namespace);
|
||||
}
|
||||
|
||||
if (!isContextTokenValueLocation(contextToken) &&
|
||||
(isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken))) {
|
||||
// Its a type, but you can reach it by namespace.type as well
|
||||
return symbolCanbeReferencedAtTypeLocation(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// expressions are value space (which includes the value namespaces)
|
||||
return !!(symbol.flags & SymbolFlags.Value);
|
||||
});
|
||||
}
|
||||
|
||||
function isContextTokenValueLocation(contextToken: Node) {
|
||||
if (contextToken) {
|
||||
const parentKind = contextToken.parent.kind;
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.TypeOfKeyword:
|
||||
return parentKind === SyntaxKind.TypeQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isContextTokenTypeLocation(contextToken: Node) {
|
||||
if (contextToken) {
|
||||
const parentKind = contextToken.parent.kind;
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.ColonToken:
|
||||
return parentKind === SyntaxKind.PropertyDeclaration ||
|
||||
parentKind === SyntaxKind.PropertySignature ||
|
||||
parentKind === SyntaxKind.Parameter ||
|
||||
parentKind === SyntaxKind.VariableDeclaration ||
|
||||
isFunctionLikeKind(parentKind);
|
||||
|
||||
case SyntaxKind.EqualsToken:
|
||||
return parentKind === SyntaxKind.TypeAliasDeclaration;
|
||||
|
||||
case SyntaxKind.AsKeyword:
|
||||
return parentKind === SyntaxKind.AsExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function symbolCanbeReferencedAtTypeLocation(symbol: Symbol): boolean {
|
||||
// This is an alias, follow what it aliases
|
||||
if (symbol && symbol.flags & SymbolFlags.Alias) {
|
||||
symbol = typeChecker.getAliasedSymbol(symbol);
|
||||
}
|
||||
|
||||
if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) {
|
||||
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
|
||||
// If the exported symbols contains type,
|
||||
// symbol can be referenced at locations where type is allowed
|
||||
return forEach(exportedSymbols, symbolCanbeReferencedAtTypeLocation);
|
||||
}
|
||||
|
||||
return !!(symbol.flags & (SymbolFlags.NamespaceModule | SymbolFlags.Type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first node that "embraces" the position, so that one may
|
||||
* accurately aggregate locals from the closest containing scope.
|
||||
@@ -1126,21 +1212,6 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isFunction(kind: SyntaxKind): boolean {
|
||||
if (!isFunctionLikeKind(kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.FunctionType:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if we are certain that the currently edited location must define a new location; false otherwise.
|
||||
*/
|
||||
@@ -1152,7 +1223,7 @@ namespace ts.Completions {
|
||||
containingNodeKind === SyntaxKind.VariableDeclarationList ||
|
||||
containingNodeKind === SyntaxKind.VariableStatement ||
|
||||
containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { foo, |
|
||||
isFunction(containingNodeKind) ||
|
||||
isFunctionLikeButNotConstructor(containingNodeKind) ||
|
||||
containingNodeKind === SyntaxKind.ClassDeclaration || // class A<T, |
|
||||
containingNodeKind === SyntaxKind.ClassExpression || // var C = class D<T, |
|
||||
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface A<T, |
|
||||
@@ -1170,7 +1241,7 @@ namespace ts.Completions {
|
||||
|
||||
case SyntaxKind.OpenParenToken:
|
||||
return containingNodeKind === SyntaxKind.CatchClause ||
|
||||
isFunction(containingNodeKind);
|
||||
isFunctionLikeButNotConstructor(containingNodeKind);
|
||||
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { |
|
||||
@@ -1188,7 +1259,7 @@ namespace ts.Completions {
|
||||
containingNodeKind === SyntaxKind.ClassExpression || // var C = class D< |
|
||||
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface A< |
|
||||
containingNodeKind === SyntaxKind.TypeAliasDeclaration || // type List< |
|
||||
isFunction(containingNodeKind);
|
||||
isFunctionLikeKind(containingNodeKind);
|
||||
|
||||
case SyntaxKind.StaticKeyword:
|
||||
return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(contextToken.parent.parent);
|
||||
@@ -1257,6 +1328,10 @@ namespace ts.Completions {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isFunctionLikeButNotConstructor(kind: SyntaxKind) {
|
||||
return isFunctionLikeKind(kind) && kind !== SyntaxKind.Constructor;
|
||||
}
|
||||
|
||||
function isDotOfNumericLiteral(contextToken: Node): boolean {
|
||||
if (contextToken.kind === SyntaxKind.NumericLiteral) {
|
||||
const text = contextToken.getFullText();
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace ts {
|
||||
else if (node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
return SemanticMeaning.All;
|
||||
}
|
||||
else if (isInRightSideOfImport(node)) {
|
||||
else if (isInRightSideOfInternalImportEqualsDeclaration(node)) {
|
||||
return getMeaningFromRightHandSideOfImportEquals(node);
|
||||
}
|
||||
else if (isDeclarationName(node)) {
|
||||
@@ -114,7 +114,7 @@ namespace ts {
|
||||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
function isInRightSideOfImport(node: Node) {
|
||||
export function isInRightSideOfInternalImportEqualsDeclaration(node: Node) {
|
||||
while (node.parent.kind === SyntaxKind.QualifiedName) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user