mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 21:36:50 -05:00
Use getAllSuperTypeNodes in more places (#22718)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user