13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ciimport * as ts from 'typescript'; 173af6ab5fSopenharmony_ciimport { LinterConfig } from '../TypeScriptLinterConfig'; 183af6ab5fSopenharmony_ciimport { forEachNodeInSubtree } from '../utils/functions/ForEachNodeInSubtree'; 193af6ab5fSopenharmony_ciimport { isStructDeclaration } from '../utils/functions/IsStruct'; 203af6ab5fSopenharmony_ciimport type { TsUtils } from '../utils/TsUtils'; 213af6ab5fSopenharmony_ci 223af6ab5fSopenharmony_ciexport class SymbolCache { 233af6ab5fSopenharmony_ci constructor( 243af6ab5fSopenharmony_ci private readonly typeChecker: ts.TypeChecker, 253af6ab5fSopenharmony_ci private readonly utils: TsUtils, 263af6ab5fSopenharmony_ci readonly sourceFile: ts.SourceFile, 273af6ab5fSopenharmony_ci readonly cancellationToken?: ts.CancellationToken 283af6ab5fSopenharmony_ci ) { 293af6ab5fSopenharmony_ci const callback = (node: ts.Node): void => { 303af6ab5fSopenharmony_ci if (isStructDeclaration(node)) { 313af6ab5fSopenharmony_ci // early exit via exception if cancellation was requested 323af6ab5fSopenharmony_ci this.cancellationToken?.throwIfCancellationRequested(); 333af6ab5fSopenharmony_ci } 343af6ab5fSopenharmony_ci 353af6ab5fSopenharmony_ci const symbol = this.handlersMap.get(node.kind)?.call(this, node); 363af6ab5fSopenharmony_ci if (symbol !== undefined) { 373af6ab5fSopenharmony_ci this.addReference(symbol, node); 383af6ab5fSopenharmony_ci } 393af6ab5fSopenharmony_ci }; 403af6ab5fSopenharmony_ci 413af6ab5fSopenharmony_ci const stopCondition = (node: ts.Node): boolean => { 423af6ab5fSopenharmony_ci return !node || LinterConfig.terminalTokens.has(node.kind); 433af6ab5fSopenharmony_ci }; 443af6ab5fSopenharmony_ci 453af6ab5fSopenharmony_ci forEachNodeInSubtree(sourceFile, callback, stopCondition); 463af6ab5fSopenharmony_ci } 473af6ab5fSopenharmony_ci 483af6ab5fSopenharmony_ci getReferences(symbol: ts.Symbol): ts.Node[] { 493af6ab5fSopenharmony_ci return this.cache.get(symbol) ?? []; 503af6ab5fSopenharmony_ci } 513af6ab5fSopenharmony_ci 523af6ab5fSopenharmony_ci private handleElementAccessExpression(node: ts.Node): ts.Symbol | undefined { 533af6ab5fSopenharmony_ci const elementAccessExpr = node as ts.ElementAccessExpression; 543af6ab5fSopenharmony_ci return this.typeChecker.getSymbolAtLocation(elementAccessExpr.argumentExpression); 553af6ab5fSopenharmony_ci } 563af6ab5fSopenharmony_ci 573af6ab5fSopenharmony_ci private handleEnumDeclaration(node: ts.Node): ts.Symbol | undefined { 583af6ab5fSopenharmony_ci const enumDeclaration = node as ts.EnumDeclaration; 593af6ab5fSopenharmony_ci return this.utils.trueSymbolAtLocation(enumDeclaration.name); 603af6ab5fSopenharmony_ci } 613af6ab5fSopenharmony_ci 623af6ab5fSopenharmony_ci private handlePrivateIdentifier(node: ts.Node): ts.Symbol | undefined { 633af6ab5fSopenharmony_ci const privateIdentifier = node as ts.PrivateIdentifier; 643af6ab5fSopenharmony_ci return this.typeChecker.getSymbolAtLocation(privateIdentifier); 653af6ab5fSopenharmony_ci } 663af6ab5fSopenharmony_ci 673af6ab5fSopenharmony_ci private handlePropertyAssignment(node: ts.Node): ts.Symbol | undefined { 683af6ab5fSopenharmony_ci const propertyAssignment = node as ts.PropertyAssignment; 693af6ab5fSopenharmony_ci const contextualType = this.typeChecker.getContextualType(propertyAssignment.parent); 703af6ab5fSopenharmony_ci return contextualType === undefined ? undefined : this.utils.getPropertySymbol(contextualType, propertyAssignment); 713af6ab5fSopenharmony_ci } 723af6ab5fSopenharmony_ci 733af6ab5fSopenharmony_ci private handlePropertyDeclaration(node: ts.Node): ts.Symbol | undefined { 743af6ab5fSopenharmony_ci const propertyDeclaration = node as ts.PropertyDeclaration; 753af6ab5fSopenharmony_ci return this.typeChecker.getSymbolAtLocation(propertyDeclaration.name); 763af6ab5fSopenharmony_ci } 773af6ab5fSopenharmony_ci 783af6ab5fSopenharmony_ci private handlePropertySignature(node: ts.Node): ts.Symbol | undefined { 793af6ab5fSopenharmony_ci const propertySignature = node as ts.PropertySignature; 803af6ab5fSopenharmony_ci return this.typeChecker.getSymbolAtLocation(propertySignature.name); 813af6ab5fSopenharmony_ci } 823af6ab5fSopenharmony_ci 833af6ab5fSopenharmony_ci private handleFunctionDeclaration(node: ts.Node): ts.Symbol | undefined { 843af6ab5fSopenharmony_ci const functionDeclaration = node as ts.FunctionDeclaration; 853af6ab5fSopenharmony_ci return functionDeclaration.name ? this.typeChecker.getSymbolAtLocation(functionDeclaration.name) : undefined; 863af6ab5fSopenharmony_ci } 873af6ab5fSopenharmony_ci 883af6ab5fSopenharmony_ci private handleCallExpression(node: ts.Node): ts.Symbol | undefined { 893af6ab5fSopenharmony_ci const callExpression = node as ts.CallExpression; 903af6ab5fSopenharmony_ci return this.typeChecker.getSymbolAtLocation(callExpression.expression); 913af6ab5fSopenharmony_ci } 923af6ab5fSopenharmony_ci 933af6ab5fSopenharmony_ci private handleIdentifier(node: ts.Node): ts.Symbol | undefined { 943af6ab5fSopenharmony_ci const identifier = node as ts.Identifier; 953af6ab5fSopenharmony_ci const symbol = this.typeChecker.getSymbolAtLocation(identifier); 963af6ab5fSopenharmony_ci if (symbol?.flags) { 973af6ab5fSopenharmony_ci return (symbol.flags & ts.SymbolFlags.Variable) !== 0 ? symbol : undefined; 983af6ab5fSopenharmony_ci } 993af6ab5fSopenharmony_ci return undefined; 1003af6ab5fSopenharmony_ci } 1013af6ab5fSopenharmony_ci 1023af6ab5fSopenharmony_ci private addReference(symbol: ts.Symbol, node: ts.Node): void { 1033af6ab5fSopenharmony_ci let nodes = this.cache.get(symbol); 1043af6ab5fSopenharmony_ci if (nodes === undefined) { 1053af6ab5fSopenharmony_ci nodes = []; 1063af6ab5fSopenharmony_ci this.cache.set(symbol, nodes); 1073af6ab5fSopenharmony_ci } 1083af6ab5fSopenharmony_ci nodes.push(node); 1093af6ab5fSopenharmony_ci } 1103af6ab5fSopenharmony_ci 1113af6ab5fSopenharmony_ci private readonly handlersMap = new Map([ 1123af6ab5fSopenharmony_ci [ts.SyntaxKind.ElementAccessExpression, this.handleElementAccessExpression], 1133af6ab5fSopenharmony_ci [ts.SyntaxKind.EnumDeclaration, this.handleEnumDeclaration], 1143af6ab5fSopenharmony_ci [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier], 1153af6ab5fSopenharmony_ci [ts.SyntaxKind.PropertyAssignment, this.handlePropertyAssignment], 1163af6ab5fSopenharmony_ci [ts.SyntaxKind.PropertyDeclaration, this.handlePropertyDeclaration], 1173af6ab5fSopenharmony_ci [ts.SyntaxKind.PropertySignature, this.handlePropertySignature], 1183af6ab5fSopenharmony_ci [ts.SyntaxKind.FunctionDeclaration, this.handleFunctionDeclaration], 1193af6ab5fSopenharmony_ci [ts.SyntaxKind.CallExpression, this.handleCallExpression], 1203af6ab5fSopenharmony_ci [ts.SyntaxKind.Identifier, this.handleIdentifier] 1213af6ab5fSopenharmony_ci ]); 1223af6ab5fSopenharmony_ci 1233af6ab5fSopenharmony_ci private readonly cache: Map<ts.Symbol, ts.Node[]> = new Map<ts.Symbol, ts.Node[]>(); 1243af6ab5fSopenharmony_ci} 125