Use getAllSuperTypeNodes in more places (#22718)

This commit is contained in:
Andy
2018-03-28 13:33:20 -07:00
committed by GitHub
parent cb9f436b54
commit 6ec13bb945
5 changed files with 62 additions and 148 deletions

View File

@@ -2111,6 +2111,13 @@ namespace ts {
return heritageClause ? heritageClause.types : undefined;
}
/** Returns the node in an `extends` or `implements` clause of a class or interface. */
export function getAllSuperTypeNodes(node: Node): ReadonlyArray<TypeNode> {
return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray
: isClassLike(node) ? concatenate(singleElementArray(getClassExtendsHeritageClauseElement(node)), getClassImplementsHeritageClauseElements(node)) || emptyArray
: emptyArray;
}
export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) {
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
return heritageClause ? heritageClause.types : undefined;

View File

@@ -1563,43 +1563,34 @@ namespace ts.Completions {
completionKind = CompletionKind.MemberLike;
// Declaring new property/method/accessor
isNewIdentifierLocation = true;
// Has keywords for class elements
keywordFilters = isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
// If you're in an interface you don't want to repeat things from super-interface. So just stop here.
if (!isClassLike(decl)) return GlobalsSearch.Success;
const baseTypeNode = getClassExtendsHeritageClauseElement(decl);
const implementsTypeNodes = getClassImplementsHeritageClauseElements(decl);
if (!baseTypeNode && !implementsTypeNodes) return GlobalsSearch.Success;
const classElement = contextToken.parent;
const classElementModifierFlags = (isClassElement(classElement) ? getModifierFlags(classElement) : ModifierFlags.None)
// If this context token is not something we are editing now, consider if this would lead to be modifier
| (!isCurrentlyEditingNode(contextToken) ? modifierToFlag(keywordForNode(contextToken)) : ModifierFlags.None);
// No member list for private methods
if (classElementModifierFlags & ModifierFlags.Private) return GlobalsSearch.Success;
let baseClassTypeToGetPropertiesFrom: Type | undefined;
if (baseTypeNode) {
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
if (classElementModifierFlags & ModifierFlags.Static) {
// Use static class to get property symbols from
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(baseClassTypeToGetPropertiesFrom.symbol, decl);
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
// If this is context token is not something we are editing now, consider if this would lead to be modifier
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
switch (contextToken.getText()) {
case "private":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
break;
case "static":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
break;
}
}
const implementedInterfaceTypePropertySymbols = !implementsTypeNodes || (classElementModifierFlags & ModifierFlags.Static)
? emptyArray
: flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
// List of property symbols of base type that are not private and already implemented
symbols = filterClassMembersList(
baseClassTypeToGetPropertiesFrom ? typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : emptyArray,
implementedInterfaceTypePropertySymbols,
decl.members,
classElementModifierFlags);
// No member list for private methods
if (!(classElementModifierFlags & ModifierFlags.Private)) {
// List of property symbols of base type that are not private and already implemented
const baseSymbols = flatMap(getAllSuperTypeNodes(decl), baseTypeNode => {
const type = typeChecker.getTypeAtLocation(baseTypeNode);
return typeChecker.getPropertiesOfType(classElementModifierFlags & ModifierFlags.Static ? typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl) : type);
});
symbols = filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags);
}
return GlobalsSearch.Success;
}
@@ -1967,12 +1958,8 @@ namespace ts.Completions {
*
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
*/
function filterClassMembersList(
baseSymbols: ReadonlyArray<Symbol>,
implementingTypeSymbols: ReadonlyArray<Symbol>,
existingMembers: ReadonlyArray<ClassElement>,
currentClassElementModifierFlags: ModifierFlags): Symbol[] {
const existingMemberNames = createUnderscoreEscapedMap<boolean>();
function filterClassMembersList(baseSymbols: ReadonlyArray<Symbol>, existingMembers: ReadonlyArray<ClassElement>, currentClassElementModifierFlags: ModifierFlags): Symbol[] {
const existingMemberNames = createUnderscoreEscapedMap<true>();
for (const m of existingMembers) {
// Ignore omitted expressions for missing members
if (m.kind !== SyntaxKind.PropertyDeclaration &&
@@ -1993,10 +1980,7 @@ namespace ts.Completions {
}
// do not filter it out if the static presence doesnt match
const mIsStatic = hasModifier(m, ModifierFlags.Static);
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
if ((mIsStatic && !currentElementIsStatic) ||
(!mIsStatic && currentElementIsStatic)) {
if (hasModifier(m, ModifierFlags.Static) !== !!(currentClassElementModifierFlags & ModifierFlags.Static)) {
continue;
}
@@ -2006,24 +1990,10 @@ namespace ts.Completions {
}
}
const result: Symbol[] = [];
addPropertySymbols(baseSymbols, ModifierFlags.Private);
addPropertySymbols(implementingTypeSymbols, ModifierFlags.NonPublicAccessibilityModifier);
return result;
function addPropertySymbols(properties: ReadonlyArray<Symbol>, inValidModifierFlags: ModifierFlags) {
for (const property of properties) {
if (isValidProperty(property, inValidModifierFlags)) {
result.push(property);
}
}
}
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
return !existingMemberNames.get(propertySymbol.escapedName) &&
propertySymbol.getDeclarations() &&
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
}
return baseSymbols.filter(propertySymbol =>
!existingMemberNames.has(propertySymbol.escapedName) &&
!!propertySymbol.declarations &&
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private));
}
/**

View File

@@ -1200,63 +1200,31 @@ namespace ts.FindAllReferences.Core {
* distinction between structurally compatible implementations and explicit implementations, so we
* must use the AST.
*
* @param child A class or interface Symbol
* @param symbol A class or interface Symbol
* @param parent Another class or interface Symbol
* @param cachedResults A map of symbol id pairs (i.e. "child,parent") to booleans indicating previous results
*/
function explicitlyInheritsFrom(child: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
const parentIsInterface = parent.getFlags() & SymbolFlags.Interface;
return searchHierarchy(child);
function searchHierarchy(symbol: Symbol): boolean {
if (symbol === parent) {
return true;
}
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
const cached = cachedResults.get(key);
if (cached !== undefined) {
return cached;
}
// Set the key so that we don't infinitely recurse
cachedResults.set(key, false);
const inherits = forEach(symbol.getDeclarations(), declaration => {
if (isClassLike(declaration)) {
if (parentIsInterface) {
const interfaceReferences = getClassImplementsHeritageClauseElements(declaration);
if (interfaceReferences) {
for (const typeReference of interfaceReferences) {
if (searchTypeReference(typeReference)) {
return true;
}
}
}
}
return searchTypeReference(getClassExtendsHeritageClauseElement(declaration));
}
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
if (parentIsInterface) {
return forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), searchTypeReference);
}
}
return false;
});
cachedResults.set(key, inherits);
return inherits;
function explicitlyInheritsFrom(symbol: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
if (symbol === parent) {
return true;
}
function searchTypeReference(typeReference: ExpressionWithTypeArguments): boolean {
if (typeReference) {
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
const cached = cachedResults.get(key);
if (cached !== undefined) {
return cached;
}
// Set the key so that we don't infinitely recurse
cachedResults.set(key, false);
const inherits = symbol.declarations.some(declaration =>
getAllSuperTypeNodes(declaration).some(typeReference => {
const type = checker.getTypeAtLocation(typeReference);
if (type && type.symbol) {
return searchHierarchy(type.symbol);
}
}
return false;
}
return !!type && !!type.symbol && explicitlyInheritsFrom(type.symbol, parent, cachedResults, checker);
}));
cachedResults.set(key, inherits);
return inherits;
}
function getReferencesForSuperKeyword(superKeyword: Node): SymbolAndEntries[] {
@@ -1491,10 +1459,6 @@ namespace ts.FindAllReferences.Core {
* The value of previousIterationSymbol is undefined when the function is first called.
*/
function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Push<Symbol>, previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void {
if (!symbol) {
return;
}
// If the current symbol is the same as the previous-iteration symbol, we can just return the symbol that has already been visited
// This is particularly important for the following cases, so that we do not infinitely visit the same symbol.
// For example:
@@ -1506,27 +1470,16 @@ namespace ts.FindAllReferences.Core {
// the function will add any found symbol of the property-name, then its sub-routine will call
// getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already
// visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol.
if (previousIterationSymbolsCache.has(symbol.escapedName)) {
if (!symbol || previousIterationSymbolsCache.has(symbol.escapedName)) {
return;
}
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
forEach(symbol.getDeclarations(), declaration => {
if (isClassLike(declaration)) {
getPropertySymbolFromTypeReference(getClassExtendsHeritageClauseElement(<ClassDeclaration>declaration));
forEach(getClassImplementsHeritageClauseElements(<ClassDeclaration>declaration), getPropertySymbolFromTypeReference);
}
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), getPropertySymbolFromTypeReference);
}
});
}
return;
for (const declaration of symbol.declarations) {
for (const typeReference of getAllSuperTypeNodes(declaration)) {
const type = checker.getTypeAtLocation(typeReference);
if (!type) continue;
function getPropertySymbolFromTypeReference(typeReference: ExpressionWithTypeArguments): void {
if (typeReference) {
const type = checker.getTypeAtLocation(typeReference);
if (type) {
const propertySymbol = checker.getPropertyOfType(type, propertyName);
if (propertySymbol) {
result.push(...checker.getRootSymbols(propertySymbol));

View File

@@ -576,7 +576,7 @@ namespace ts {
*/
function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] {
let foundDocs = false;
return flatMap(getAllSuperTypeNodes(declaration), superTypeNode => {
return flatMap(declaration.parent ? getAllSuperTypeNodes(declaration.parent) : emptyArray, superTypeNode => {
if (foundDocs) {
return emptyArray;
}
@@ -594,21 +594,6 @@ namespace ts {
});
}
/**
* Finds and returns the `TypeNode` for all super classes and implemented interfaces given a declaration.
* @param declaration The possibly-inherited declaration.
* @returns A filled array of `TypeNode`s containing all super classes and implemented interfaces if any exist, otherwise an empty array.
*/
function getAllSuperTypeNodes(declaration: Declaration): ReadonlyArray<TypeNode> {
const container = declaration.parent;
if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) {
return emptyArray;
}
const extended = getClassExtendsHeritageClauseElement(container);
const types = extended ? [extended] : emptyArray;
return isClassLike(container) ? concatenate(types, getClassImplementsHeritageClauseElements(container)) : types;
}
class SourceFileObject extends NodeObject implements SourceFile {
public kind: SyntaxKind.SourceFile;
public _declarationBrand: any;