import { CancellationToken , CompletionItem , DocumentSymbol } from 'vscode-languageserver' ; import { TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument' ; import { CallHierarchyIncomingCall , CallHierarchyItem , CallHierarchyOutgoingCall , DocumentHighlight , MarkupKind , } from 'vscode-languageserver-types' ; import { OperationCanceledException , throwIfCancellationRequested } from '../common/cancellationUtils' ; import { removeArrayElements } from '../common/collectionUtils' ; import { ConfigOptions , ExecutionEnvironment } from '../common/configOptions' ; import { ConsoleInterface , StandardConsole } from '../common/console' ; import { assert } from '../common/debug' ; import { Diagnostic } from '../common/diagnostic' ; import { FileDiagnostics } from '../common/diagnosticSink' ; import { FileEditAction , FileEditActions , TextEditAction } from '../common/editAction' ; import { LanguageServiceExtension } from '../common/extensibility' ; import { LogTracker } from '../common/logTracker' ; import { combinePaths , getDirectoryPath , getFileName , getRelativePath , isFile , makeDirectories , normalizePath , normalizePathCase , stripFileExtension , } from '../common/pathUtils' ; import { convertPositionToOffset , convertRangeToTextRange } from '../common/positionUtils' ; import { computeCompletionSimilarity } from '../common/stringUtils' ; import { DocumentRange , doesRangeContain , doRangesIntersect , Position , Range } from '../common/textRange' ; import { Duration , timingStats } from '../common/timing' ; import { AutoImporter , AutoImportResult , buildModuleSymbolsMap , ModuleSymbolMap , } from '../languageService/autoImporter' ; import { CallHierarchyProvider } from '../languageService/callHierarchyProvider' ; import { AbbreviationMap , CompletionOptions , CompletionResults } from '../languageService/completionProvider' ; import { DefinitionFilter } from '../languageService/definitionProvider' ; import { DocumentSymbolCollector } from '../languageService/documentSymbolCollector' ; import { IndexOptions , IndexResults , WorkspaceSymbolCallback } from '../languageService/documentSymbolProvider' ; import { HoverResults } from '../languageService/hoverProvider' ; import { ReferenceCallback , ReferencesResult } from '../languageService/referencesProvider' ; import { RenameModuleProvider } from '../languageService/renameModuleProvider' ; import { SignatureHelpResults } from '../languageService/signatureHelpProvider' ; import { ParseNodeType } from '../parser/parseNodes' ; import { ParseResults } from '../parser/parser' ; import { ImportLookupResult } from './analyzerFileInfo' ; import * as AnalyzerNodeInfo from './analyzerNodeInfo' ; import { CircularDependency } from './circularDependency' ; import { isAliasDeclaration } from './declaration' ; import { ImportResolver } from './importResolver' ; import { ImportResult , ImportType } from './importResult' ; import { findNodeByOffset , getDocString } from './parseTreeUtils' ; import { Scope } from './scope' ; import { getScopeForNode } from './scopeUtils' ; import { SourceFile } from './sourceFile' ; import { SourceMapper } from './sourceMapper' ; import { Symbol } from './symbol' ; import { isPrivateOrProtectedName } from './symbolNameUtils' ; import { createTracePrinter } from './tracePrinter' ; import { TypeEvaluator } from './typeEvaluatorTypes' ; import { createTypeEvaluatorWithTracker } from './typeEvaluatorWithTracker' ; import { PrintTypeFlags } from './typePrinter' ; import { Type } from './types' ; import { TypeStubWriter } from './typeStubWriter' ; const _maxImportDepth = 256 ; export const MaxWorkspaceIndexFileCount = 2000 ; export interface SourceFileInfo { sourceFile : SourceFile ; isTypeshedFile : boolean ; isThirdPartyImport : boolean ; isThirdPartyPyTypedPresent : boolean ; diagnosticsVersion ? : number | undefined ; builtinsImport ? : SourceFileInfo | undefined ; isTracked : boolean ; isOpenByClient : boolean ; imports : SourceFileInfo [ ] ; importedBy : SourceFileInfo [ ] ; shadows : SourceFileInfo [ ] ; shadowedBy : SourceFileInfo [ ] ; } export interface MaxAnalysisTime { openFilesTimeInMs : number ; noOpenFilesTimeInMs : number ; } export interface Indices { setWorkspaceIndex ( path , indexResults ) : void ; getIndex ( execEnv ) : Map < string , IndexResults > | undefined ; setIndex ( execEnv , path , indexResults ) : void ; reset ( ) : void ; } interface UpdateImportInfo { path : string ; isTypeshedFile : boolean ; isThirdPartyImport : boolean ; isPyTypedPresent : boolean ; } export type PreCheckCallback = ( parseResults : ParseResults , evaluator : TypeEvaluator ) => void ; export class Program { private _console : ConsoleInterface ; private _sourceFileList : SourceFileInfo [ ] = [ ] ; private _sourceFileMap = new Map < string , SourceFileInfo > ( ) ; private _allowedThirdPartyImports : string [ ] | undefined ; private _evaluator : TypeEvaluator | undefined ; private _configOptions : ConfigOptions ; private _importResolver : ImportResolver ; private _logTracker : LogTracker ; private _parsedFileCount = 0 ; private _preCheckCallback : PreCheckCallback | undefined ; constructor ( initialImportResolver , initialConfigOptions , console ? , private _extension ? , logTracker ? , private _disableChecker ? ) { this . _console = console || new StandardConsole ( ) ; this . _logTracker = logTracker ?? new LogTracker ( console , 'FG' ) ; this . _importResolver = initialImportResolver ; this . _configOptions = initialConfigOptions ; this . _createNewEvaluator ( ) ; } get evaluator ( ) : TypeEvaluator | undefined { return this . _evaluator ; } setConfigOptions ( configOptions ) { this . _configOptions = configOptions ; this . _createNewEvaluator ( ) ; } setImportResolver ( importResolver ) { this . _importResolver = importResolver ; this . _createNewEvaluator ( ) ; } setTrackedFiles ( filePaths ) { if ( this . _sourceFileList . length > 0 ) { const newFileMap = new Map < string , string > ( ) ; filePaths . forEach ( ( path ) => { newFileMap . set ( normalizePathCase ( this . _fs , path ) , path ) ; } ) ; this . _sourceFileList . forEach ( ( oldFile ) => { const filePath = normalizePathCase ( this . _fs , oldFile . sourceFile . getFilePath ( ) ) ; if ( ! newFileMap . has ( filePath ) ) { oldFile . isTracked = false ; } } ) ; } this . addTrackedFiles ( filePaths ) ; return this . _removeUnneededFiles ( ) ; } setPreCheckCallback ( preCheckCallback ) { this . _preCheckCallback = preCheckCallback ; } setAllowedThirdPartyImports ( importNames ) { this . _allowedThirdPartyImports = importNames ; } addTrackedFiles ( filePaths , isThirdPartyImport = false , isInPyTypedPackage = false ) { filePaths . forEach ( ( filePath ) => { this . addTrackedFile ( filePath , isThirdPartyImport , isInPyTypedPackage ) ; } ) ; } addTrackedFile ( filePath , isThirdPartyImport = false , isInPyTypedPackage = false ) { let sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( sourceFileInfo ) { sourceFileInfo . isTracked = true ; return sourceFileInfo . sourceFile ; } const importName = this . _getImportNameForFile ( filePath ) ; const sourceFile = new SourceFile ( this . _fs , filePath , importName , isThirdPartyImport , isInPyTypedPackage , this . _console , this . _logTracker ) ; sourceFileInfo = { sourceFile , isTracked : true , isOpenByClient : false , isTypeshedFile : false , isThirdPartyImport , isThirdPartyPyTypedPresent : isInPyTypedPackage , diagnosticsVersion : undefined , imports : [ ] , importedBy : [ ] , shadows : [ ] , shadowedBy : [ ] , } ; this . _addToSourceFileListAndMap ( sourceFileInfo ) ; return sourceFile ; } setFileOpened ( filePath , version , contents , isTracked = false ) { let sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { const importName = this . _getImportNameForFile ( filePath ) ; const sourceFile = new SourceFile ( this . _fs , filePath , importName , false , false , this . _console , this . _logTracker ) ; sourceFileInfo = { sourceFile , isTracked : isTracked , isOpenByClient : true , isTypeshedFile : false , isThirdPartyImport : false , isThirdPartyPyTypedPresent : false , diagnosticsVersion : undefined , imports : [ ] , importedBy : [ ] , shadows : [ ] , shadowedBy : [ ] , } ; this . _addToSourceFileListAndMap ( sourceFileInfo ) ; } else { sourceFileInfo . isOpenByClient = true ; sourceFileInfo . diagnosticsVersion = 0 ; } sourceFileInfo . sourceFile . setClientVersion ( version , contents ) ; } setFileClosed ( filePath ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( sourceFileInfo ) { sourceFileInfo . isOpenByClient = false ; sourceFileInfo . sourceFile . setClientVersion ( null , [ ] ) ; } return this . _removeUnneededFiles ( ) ; } markAllFilesDirty ( evenIfContentsAreSame ) { const markDirtyMap = new Map < string , boolean > ( ) ; this . _sourceFileList . forEach ( ( sourceFileInfo ) => { if ( evenIfContentsAreSame ) { sourceFileInfo . sourceFile . markDirty ( ) ; } else if ( sourceFileInfo . sourceFile . didContentsChangeOnDisk ( ) ) { sourceFileInfo . sourceFile . markDirty ( ) ; this . _markFileDirtyRecursive ( sourceFileInfo , markDirtyMap ) ; } } ) ; if ( markDirtyMap . size > 0 ) { this . _createNewEvaluator ( ) ; } } markFilesDirty ( filePaths , evenIfContentsAreSame ) { const markDirtyMap = new Map < string , boolean > ( ) ; filePaths . forEach ( ( filePath ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( sourceFileInfo ) { if ( evenIfContentsAreSame || ( ! sourceFileInfo . isOpenByClient && sourceFileInfo . sourceFile . didContentsChangeOnDisk ( ) ) ) { sourceFileInfo . sourceFile . markDirty ( ) ; this . _markFileDirtyRecursive ( sourceFileInfo , markDirtyMap ) ; } } } ) ; if ( markDirtyMap . size > 0 ) { this . _createNewEvaluator ( ) ; } } getFileCount ( ) { return this . _sourceFileList . length ; } getTracked ( ) { return this . _sourceFileList . filter ( ( s ) => s . isTracked ) ; } getOpened ( ) { return this . _sourceFileList . filter ( ( s ) => s . isOpenByClient ) ; } getFilesToAnalyzeCount ( ) { let sourceFileCount = 0 ; if ( this . _disableChecker ) { return sourceFileCount ; } this . _sourceFileList . forEach ( ( fileInfo ) => { if ( fileInfo . sourceFile . isCheckingRequired ( ) ) { if ( this . _shouldCheckFile ( fileInfo ) ) { sourceFileCount ++ ; } } } ) ; return sourceFileCount ; } isCheckingOnlyOpenFiles ( ) { return this . _configOptions . checkOnlyOpenFiles || false ; } getSourceFile ( filePath ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } return sourceFileInfo . sourceFile ; } getBoundSourceFile ( filePath ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; return this . getSourceFile ( filePath ) ; } analyze ( maxTime ? , token = CancellationToken . None ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const elapsedTime = new Duration ( ) ; const openFiles = this . _sourceFileList . filter ( ( sf ) => sf . isOpenByClient && sf . sourceFile . isCheckingRequired ( ) ) ; if ( openFiles . length > 0 ) { const effectiveMaxTime = maxTime ? maxTime . openFilesTimeInMs : Number . MAX_VALUE ; for ( const sourceFileInfo of openFiles ) { if ( this . _checkTypes ( sourceFileInfo ) ) { if ( elapsedTime . getDurationInMilliseconds ( ) > effectiveMaxTime ) { return true ; } } } if ( maxTime !== undefined ) { return true ; } } if ( ! this . _configOptions . checkOnlyOpenFiles ) { const effectiveMaxTime = maxTime ? maxTime . noOpenFilesTimeInMs : Number . MAX_VALUE ; for ( const sourceFileInfo of this . _sourceFileList ) { if ( ! this . _isUserCode ( sourceFileInfo ) ) { continue ; } if ( this . _checkTypes ( sourceFileInfo ) ) { if ( elapsedTime . getDurationInMilliseconds ( ) > effectiveMaxTime ) { return true ; } } } } return false ; } ) ; } indexWorkspace ( callback , token ) { if ( ! this . _configOptions . indexing ) { return 0 ; } return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const initiallyParsedSet = new Set < SourceFileInfo > ( ) ; for ( const sourceFileInfo of this . _sourceFileList ) { if ( ! sourceFileInfo . sourceFile . isParseRequired ( ) ) { initiallyParsedSet . add ( sourceFileInfo ) ; } } let count = 0 ; for ( const sourceFileInfo of this . _sourceFileList ) { if ( ! this . _isUserCode ( sourceFileInfo ) || ! sourceFileInfo . sourceFile . isIndexingRequired ( ) ) { continue ; } this . _bindFile ( sourceFileInfo ) ; const results = sourceFileInfo . sourceFile . index ( { indexingForAutoImportMode : false } , token ) ; if ( results ) { if ( ++ count > MaxWorkspaceIndexFileCount ) { this . _console . warn ( `Workspace indexing has hit its upper limit: 2000 files` ) ; dropParseAndBindInfoCreatedForIndexing ( this . _sourceFileList , initiallyParsedSet ) ; return count ; } callback ( sourceFileInfo . sourceFile . getFilePath ( ) , results ) ; } this . _handleMemoryHighUsage ( ) ; } dropParseAndBindInfoCreatedForIndexing ( this . _sourceFileList , initiallyParsedSet ) ; return count ; } ) ; function dropParseAndBindInfoCreatedForIndexing ( sourceFiles , initiallyParsedSet ) { for ( const sourceFileInfo of sourceFiles ) { if ( sourceFileInfo . sourceFile . isParseRequired ( ) || initiallyParsedSet . has ( sourceFileInfo ) ) { continue ; } sourceFileInfo . sourceFile . dropParseAndBindInfo ( ) ; } } } printDependencies ( projectRootDir , verbose ) { const sortedFiles = this . _sourceFileList . filter ( ( s ) => ! s . isTypeshedFile ) . sort ( ( a , b ) => { return a . sourceFile . getFilePath ( ) < b . sourceFile . getFilePath ( ) ? 1 : - 1 ; } ) ; const zeroImportFiles = [ ] ; sortedFiles . forEach ( ( sfInfo ) => { this . _console . info ( '' ) ; let filePath = sfInfo . sourceFile . getFilePath ( ) ; const relPath = getRelativePath ( filePath , projectRootDir ) ; if ( relPath ) { filePath = relPath ; } this . _console . info ( `${ filePath }` ) ; this . _console . info ( ` Imports     ${ sfInfo . imports . length } ` + `file${ sfInfo . imports . length === 1 ? '' : 's' }` ) ; if ( verbose ) { sfInfo . imports . forEach ( ( importInfo ) => { this . _console . info ( `    ${ importInfo . sourceFile . getFilePath ( ) }` ) ; } ) ; } this . _console . info ( ` Imported by ${ sfInfo . importedBy . length } ` + `file${ sfInfo . importedBy . length === 1 ? '' : 's' }` ) ; if ( verbose ) { sfInfo . importedBy . forEach ( ( importInfo ) => { this . _console . info ( `    ${ importInfo . sourceFile . getFilePath ( ) }` ) ; } ) ; } if ( sfInfo . importedBy . length === 0 ) { zeroImportFiles . push ( sfInfo . sourceFile ) ; } } ) ; if ( zeroImportFiles . length > 0 ) { this . _console . info ( '' ) ; this . _console . info ( `${ zeroImportFiles . length } file${ zeroImportFiles . length === 1 ? '' : 's' }` + ` not explicitly imported` ) ; zeroImportFiles . forEach ( ( importFile ) => { this . _console . info ( `    ${ importFile . getFilePath ( ) }` ) ; } ) ; } } writeTypeStub ( targetImportPath , targetIsSingleFile , stubPath , token ) { for ( const sourceFileInfo of this . _sourceFileList ) { throwIfCancellationRequested ( token ) ; const filePath = sourceFileInfo . sourceFile . getFilePath ( ) ; const relativePath = getRelativePath ( filePath , targetImportPath ) ; if ( relativePath !== undefined ) { let typeStubPath = normalizePath ( combinePaths ( stubPath , relativePath ) ) ; if ( targetIsSingleFile ) { typeStubPath = combinePaths ( getDirectoryPath ( typeStubPath ) , '__init__.pyi' ) ; } else { typeStubPath = stripFileExtension ( typeStubPath ) + '.pyi' ; } const typeStubDir = getDirectoryPath ( typeStubPath ) ; try { makeDirectories ( this . _fs , typeStubDir , stubPath ) ; } catch ( e ) { const errMsg = `Could not create directory for '${ typeStubDir }'` ; throw new Error ( errMsg ) ; } this . _bindFile ( sourceFileInfo ) ; this . _runEvaluatorWithCancellationToken ( token , ( ) => { const writer = new TypeStubWriter ( typeStubPath , sourceFileInfo . sourceFile , this . _evaluator ! ) ; writer . write ( ) ; } ) ; this . _handleMemoryHighUsage ( ) ; } } } getTypeForSymbol ( symbol ) { this . _handleMemoryHighUsage ( ) ; const evaluator = this . _evaluator || this . _createNewEvaluator ( ) ; return evaluator . getEffectiveTypeOfSymbol ( symbol ) ; } printType ( type , expandTypeAlias ) { this . _handleMemoryHighUsage ( ) ; const evaluator = this . _evaluator || this . _createNewEvaluator ( ) ; return evaluator . printType ( type , expandTypeAlias ) ; } private static _getPrintTypeFlags ( configOptions ) { let flags = PrintTypeFlags . None ; if ( configOptions . diagnosticRuleSet . printUnknownAsAny ) { flags |= PrintTypeFlags . PrintUnknownWithAny ; } if ( configOptions . diagnosticRuleSet . omitConditionalConstraint ) { flags |= PrintTypeFlags . OmitConditionalConstraint ; } if ( configOptions . diagnosticRuleSet . omitTypeArgsIfAny ) { flags |= PrintTypeFlags . OmitTypeArgumentsIfAny ; } if ( configOptions . diagnosticRuleSet . omitUnannotatedParamType ) { flags |= PrintTypeFlags . OmitUnannotatedParamType ; } if ( configOptions . diagnosticRuleSet . pep604Printing ) { flags |= PrintTypeFlags . PEP604 ; } return flags ; } private get _fs ( ) { return this . _importResolver . fileSystem ; } private _getImportNameForFile ( filePath ) { const moduleNameAndType = this . _importResolver . getModuleNameForImport ( filePath , this . _configOptions . getDefaultExecEnvironment ( ) ) ; return moduleNameAndType . moduleName ; } private _addShadowedFile ( stubFile , shadowImplPath ) { let shadowFileInfo = this . _getSourceFileInfoFromPath ( shadowImplPath ) ; if ( ! shadowFileInfo ) { const importName = this . _getImportNameForFile ( shadowImplPath ) ; const sourceFile = new SourceFile ( this . _fs , shadowImplPath , importName , false , false , this . _console , this . _logTracker ) ; shadowFileInfo = { sourceFile , isTracked : false , isOpenByClient : false , isTypeshedFile : false , isThirdPartyImport : false , isThirdPartyPyTypedPresent : false , diagnosticsVersion : undefined , imports : [ ] , importedBy : [ ] , shadows : [ ] , shadowedBy : [ ] , } ; this . _addToSourceFileListAndMap ( shadowFileInfo ) ; } if ( ! shadowFileInfo . shadows . includes ( stubFile ) ) { shadowFileInfo . shadows . push ( stubFile ) ; } if ( ! stubFile . shadowedBy . includes ( shadowFileInfo ) ) { stubFile . shadowedBy . push ( shadowFileInfo ) ; } return shadowFileInfo . sourceFile ; } private _createNewEvaluator ( ) { this . _evaluator = createTypeEvaluatorWithTracker ( this . _lookUpImport , { disableInferenceForPyTypedSources : this . _configOptions . disableInferenceForPyTypedSources , printTypeFlags : Program . _getPrintTypeFlags ( this . _configOptions ) , logCalls : this . _configOptions . logTypeEvaluationTime , minimumLoggingThreshold : this . _configOptions . typeEvaluationTimeThreshold , analyzeUnannotatedFunctions : this . _configOptions . analyzeUnannotatedFunctions , evaluateUnknownImportsAsAny : ! ! this . _configOptions . evaluateUnknownImportsAsAny , verifyTypeCacheEvaluatorFlags : ! ! this . _configOptions . internalTestMode , } , this . _logTracker , this . _configOptions . logTypeEvaluationTime ? createTracePrinter ( this . _importResolver . getImportRoots ( this . _configOptions . findExecEnvironment ( this . _configOptions . projectRoot ) ) ) : undefined ) ; return this . _evaluator ; } private _parseFile ( fileToParse , content ? ) { if ( ! this . _isFileNeeded ( fileToParse ) || ! fileToParse . sourceFile . isParseRequired ( ) ) { return ; } if ( fileToParse . sourceFile . parse ( this . _configOptions , this . _importResolver , content ) ) { this . _parsedFileCount ++ ; this . _updateSourceFileImports ( fileToParse , this . _configOptions ) ; } if ( fileToParse . sourceFile . isFileDeleted ( ) ) { fileToParse . isTracked = false ; const markDirtyMap = new Map < string , boolean > ( ) ; this . _markFileDirtyRecursive ( fileToParse , markDirtyMap ) ; this . _importResolver . invalidateCache ( ) ; } } private _bindFile ( fileToAnalyze , content ? ) { if ( ! this . _isFileNeeded ( fileToAnalyze ) || ! fileToAnalyze . sourceFile . isBindingRequired ( ) ) { return ; } this . _parseFile ( fileToAnalyze , content ) ; let builtinsScope ; if ( fileToAnalyze . builtinsImport && fileToAnalyze . builtinsImport !== fileToAnalyze ) { this . _bindFile ( fileToAnalyze . builtinsImport ) ; const parseResults = fileToAnalyze . builtinsImport . sourceFile . getParseResults ( ) ; if ( parseResults ) { builtinsScope = AnalyzerNodeInfo . getScope ( parseResults . parseTree ) ; assert ( builtinsScope !== undefined ) ; } } fileToAnalyze . sourceFile . bind ( this . _configOptions , this . _lookUpImport , builtinsScope ) ; } private _lookUpImport = ( filePath ) : ImportLookupResult | undefined => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } if ( sourceFileInfo . sourceFile . isBindingRequired ( ) ) { timingStats . typeCheckerTime . subtractFromTime ( ( ) => { this . _bindFile ( sourceFileInfo ) ; } ) ; } const symbolTable = sourceFileInfo . sourceFile . getModuleSymbolTable ( ) ; if ( ! symbolTable ) { return undefined ; } const parseResults = sourceFileInfo . sourceFile . getParseResults ( ) ; const moduleNode = parseResults ! . parseTree ; const dunderAllInfo = AnalyzerNodeInfo . getDunderAllInfo ( parseResults ! . parseTree ) ; return { symbolTable , dunderAllNames : dunderAllInfo ?. names , usesUnsupportedDunderAllForm : dunderAllInfo ?. usesUnsupportedDunderAllForm ?? false , get docString ( ) { return getDocString ( moduleNode . statements ) ; } , } ; } ; private _buildModuleSymbolsMap ( sourceFileToExclude , userFileOnly , includeIndexUserSymbols , token ) { return buildModuleSymbolsMap ( this . _sourceFileList . filter ( ( s ) => s !== sourceFileToExclude && ( userFileOnly ? this . _isUserCode ( s ) : true ) ) , includeIndexUserSymbols , token ) ; } private _shouldCheckFile ( fileInfo ) { if ( fileInfo . isOpenByClient ) { return true ; } if ( ! this . _configOptions . checkOnlyOpenFiles && fileInfo . isTracked ) { return true ; } return false ; } private _checkTypes ( fileToCheck ) { return this . _logTracker . log ( `analyzing: ${ fileToCheck . sourceFile . getFilePath ( ) }` , ( logState ) => { if ( ! this . _isFileNeeded ( fileToCheck ) ) { logState . suppress ( ) ; return false ; } if ( ! fileToCheck . sourceFile . isCheckingRequired ( ) ) { logState . suppress ( ) ; return false ; } if ( ! this . _shouldCheckFile ( fileToCheck ) ) { logState . suppress ( ) ; return false ; } this . _bindFile ( fileToCheck ) ; if ( this . _preCheckCallback ) { const parseResults = fileToCheck . sourceFile . getParseResults ( ) ; if ( parseResults ) { this . _preCheckCallback ( parseResults , this . _evaluator ! ) ; } } if ( ! this . _disableChecker ) { fileToCheck . sourceFile . check ( this . _evaluator ! ) ; } this . _handleMemoryHighUsage ( ) ; if ( this . _configOptions . diagnosticRuleSet . reportImportCycles !== 'none' ) { if ( ! this . _allowedThirdPartyImports ) { const closureMap = new Map < string , SourceFileInfo > ( ) ; this . _getImportsRecursive ( fileToCheck , closureMap , 0 ) ; closureMap . forEach ( ( file ) => { timingStats . cycleDetectionTime . timeOperation ( ( ) => { this . _detectAndReportImportCycles ( file ) ; } ) ; } ) ; } } return true ; } ) ; } private _getImportsRecursive ( file , closureMap , recursionCount ) { const filePath = normalizePathCase ( this . _fs , file . sourceFile . getFilePath ( ) ) ; if ( closureMap . has ( filePath ) ) { return ; } if ( recursionCount > _maxImportDepth ) { file . sourceFile . setHitMaxImportDepth ( _maxImportDepth ) ; return ; } closureMap . set ( filePath , file ) ; for ( const importedFileInfo of file . imports ) { this . _getImportsRecursive ( importedFileInfo , closureMap , recursionCount + 1 ) ; } } private _detectAndReportImportCycles ( sourceFileInfo , dependencyChain = [ ] , dependencyMap = new Map < string , boolean > ( ) ) { if ( sourceFileInfo . sourceFile . isStubFile ( ) || sourceFileInfo . isThirdPartyImport ) { return ; } const filePath = normalizePathCase ( this . _fs , sourceFileInfo . sourceFile . getFilePath ( ) ) ; if ( dependencyMap . has ( filePath ) ) { if ( dependencyChain . length > 1 && sourceFileInfo === dependencyChain [ 0 ] ) { this . _logImportCycle ( dependencyChain ) ; } } else { if ( dependencyMap . has ( filePath ) ) { return ; } dependencyMap . set ( filePath , true ) ; dependencyChain . push ( sourceFileInfo ) ; for ( const imp of sourceFileInfo . imports ) { this . _detectAndReportImportCycles ( imp , dependencyChain , dependencyMap ) ; } dependencyMap . set ( filePath , false ) ; dependencyChain . pop ( ) ; } } private _logImportCycle ( dependencyChain ) { const circDep = new CircularDependency ( ) ; dependencyChain . forEach ( ( sourceFileInfo ) => { circDep . appendPath ( sourceFileInfo . sourceFile . getFilePath ( ) ) ; } ) ; circDep . normalizeOrder ( ) ; const firstFilePath = circDep . getPaths ( ) [ 0 ] ; const firstSourceFile = this . _getSourceFileInfoFromPath ( firstFilePath ) ! ; assert ( firstSourceFile !== undefined ) ; firstSourceFile . sourceFile . addCircularDependency ( circDep ) ; } private _markFileDirtyRecursive ( sourceFileInfo , markMap ) { const filePath = normalizePathCase ( this . _fs , sourceFileInfo . sourceFile . getFilePath ( ) ) ; if ( ! markMap . has ( filePath ) ) { sourceFileInfo . sourceFile . markReanalysisRequired ( ) ; markMap . set ( filePath , true ) ; sourceFileInfo . importedBy . forEach ( ( dep ) => { this . _markFileDirtyRecursive ( dep , markMap ) ; } ) ; } } getTextOnRange ( filePath , range , token ) { const sourceFileInfo = this . _sourceFileMap . get ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } const sourceFile = sourceFileInfo . sourceFile ; const fileContents = sourceFile . getOpenFileContents ( ) ; if ( fileContents === undefined ) { return undefined ; } return this . _runEvaluatorWithCancellationToken ( token , ( ) => { this . _parseFile ( sourceFileInfo ) ; const parseTree = sourceFile . getParseResults ( ) ! ; const textRange = convertRangeToTextRange ( range , parseTree . tokenizerOutput . lines ) ; if ( ! textRange ) { return undefined ; } return fileContents . substr ( textRange . start , textRange . length ) ; } ) ; } getAutoImports ( filePath , range , similarityLimit , nameMap , libraryMap , lazyEdit , allowVariableInAll , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return [ ] ; } const sourceFile = sourceFileInfo . sourceFile ; const fileContents = sourceFile . getOpenFileContents ( ) ; if ( fileContents === undefined ) { return [ ] ; } return this . _runEvaluatorWithCancellationToken ( token , ( ) => { this . _bindFile ( sourceFileInfo ) ; const parseTree = sourceFile . getParseResults ( ) ! ; const textRange = convertRangeToTextRange ( range , parseTree . tokenizerOutput . lines ) ; if ( ! textRange ) { return [ ] ; } const currentNode = findNodeByOffset ( parseTree . parseTree , textRange . start ) ; if ( ! currentNode ) { return [ ] ; } const writtenWord = fileContents . substr ( textRange . start , textRange . length ) ; const map = this . _buildModuleSymbolsMap ( sourceFileInfo , ! ! libraryMap , true , token ) ; const autoImporter = new AutoImporter ( this . _configOptions . findExecEnvironment ( filePath ) , this . _importResolver , parseTree , range . start , new Set ( ) , map , { lazyEdit , allowVariableInAll , libraryMap , patternMatcher : ( p , t ) => computeCompletionSimilarity ( p , t ) > similarityLimit , } ) ; const results = [ ] ; const currentScope = getScopeForNode ( currentNode ) ; if ( currentScope ) { const info = nameMap ?. get ( writtenWord ) ; if ( info ) { results . push ( ... autoImporter . getAutoImportCandidatesForAbbr ( writtenWord , info , token ) ) ; } results . push ( ... autoImporter . getAutoImportCandidates ( writtenWord , similarityLimit , undefined , token ) . filter ( ( r ) => ! currentScope . lookUpSymbolRecursive ( r . name ) ) ) ; } return results ; } ) ; } getDiagnostics ( options ) { const fileDiagnostics = this . _removeUnneededFiles ( ) ; this . _sourceFileList . forEach ( ( sourceFileInfo ) => { if ( this . _shouldCheckFile ( sourceFileInfo ) ) { const diagnostics = sourceFileInfo . sourceFile . getDiagnostics ( options , sourceFileInfo . diagnosticsVersion ) ; if ( diagnostics !== undefined ) { fileDiagnostics . push ( { filePath : sourceFileInfo . sourceFile . getFilePath ( ) , version : sourceFileInfo . sourceFile . getClientVersion ( ) , diagnostics , } ) ; sourceFileInfo . diagnosticsVersion = sourceFileInfo . sourceFile . getDiagnosticVersion ( ) ; } } else if ( ! sourceFileInfo . isOpenByClient && options . checkOnlyOpenFiles && sourceFileInfo . diagnosticsVersion !== undefined ) { fileDiagnostics . push ( { filePath : sourceFileInfo . sourceFile . getFilePath ( ) , version : sourceFileInfo . sourceFile . getClientVersion ( ) , diagnostics : [ ] , } ) ; sourceFileInfo . diagnosticsVersion = undefined ; } } ) ; return fileDiagnostics ; } getDiagnosticsForRange ( filePath , range ) { const sourceFile = this . getSourceFile ( filePath ) ; if ( ! sourceFile ) { return [ ] ; } const unfilteredDiagnostics = sourceFile . getDiagnostics ( this . _configOptions ) ; if ( ! unfilteredDiagnostics ) { return [ ] ; } return unfilteredDiagnostics . filter ( ( diag ) => { return doRangesIntersect ( diag . range , range ) ; } ) ; } getDefinitionsForPosition ( filePath , position , filter , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getDefinitionsForPosition ( this . _createSourceMapper ( execEnv ) , position , filter , this . _evaluator ! , token ) ; } ) ; } getTypeDefinitionsForPosition ( filePath , position , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getTypeDefinitionsForPosition ( this . _createSourceMapper ( execEnv , false , true ) , position , this . _evaluator ! , filePath , token ) ; } ) ; } reportReferencesForPosition ( filePath , position , includeDeclaration , reporter , token ) { this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return ; } const invokedFromUserFile = this . _isUserCode ( sourceFileInfo ) ; this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const referencesResult = sourceFileInfo . sourceFile . getDeclarationForPosition ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , reporter , token ) ; if ( ! referencesResult ) { return ; } if ( referencesResult . requiresGlobalSearch ) { for ( const curSourceFileInfo of this . _sourceFileList ) { throwIfCancellationRequested ( token ) ; if ( curSourceFileInfo . isOpenByClient || ! invokedFromUserFile || this . _isUserCode ( curSourceFileInfo ) ) { const fileContents = curSourceFileInfo . sourceFile . getFileContent ( ) ; if ( ! fileContents || fileContents . search ( referencesResult . symbolName ) >= 0 ) { this . _bindFile ( curSourceFileInfo ) ; curSourceFileInfo . sourceFile . addReferences ( referencesResult , includeDeclaration , this . _evaluator ! , token ) ; } this . _handleMemoryHighUsage ( ) ; } } if ( includeDeclaration ) { for ( const decl of referencesResult . declarations ) { throwIfCancellationRequested ( token ) ; if ( referencesResult . locations . some ( ( l ) => l . path === decl . path ) ) { continue ; } const declFileInfo = this . _getSourceFileInfoFromPath ( decl . path ) ; if ( ! declFileInfo ) { continue ; } const tempResult = new ReferencesResult ( referencesResult . requiresGlobalSearch , referencesResult . nodeAtOffset , referencesResult . symbolName , referencesResult . declarations ) ; declFileInfo . sourceFile . addReferences ( tempResult , includeDeclaration , this . _evaluator ! , token ) ; for ( const loc of tempResult . locations ) { if ( loc . path === decl . path && doesRangeContain ( decl . range , loc . range ) ) { referencesResult . addLocations ( loc ) ; } } } } } else { sourceFileInfo . sourceFile . addReferences ( referencesResult , includeDeclaration , this . _evaluator ! , token ) ; } } ) ; } getFileIndex ( filePath , options , token ) { if ( options . indexingForAutoImportMode ) { const name = stripFileExtension ( getFileName ( filePath ) ) ; if ( isPrivateOrProtectedName ( name ) ) { return undefined ; } } this . _handleMemoryHighUsage ( ) ; return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } const content = sourceFileInfo . sourceFile . getFileContent ( ) ?? '' ; if ( options . indexingForAutoImportMode && ! sourceFileInfo . sourceFile . isStubFile ( ) && ! sourceFileInfo . sourceFile . isThirdPartyPyTypedPresent ( ) ) { if ( content . indexOf ( '__all__' ) < 0 ) { return undefined ; } } this . _bindFile ( sourceFileInfo , content ) ; return sourceFileInfo . sourceFile . index ( options , token ) ; } ) ; } addSymbolsForDocument ( filePath , symbolList , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( sourceFileInfo ) { if ( ! sourceFileInfo . sourceFile . getCachedIndexResults ( ) ) { this . _bindFile ( sourceFileInfo ) ; } sourceFileInfo . sourceFile . addHierarchicalSymbolsForDocument ( symbolList , token ) ; } } ) ; } reportSymbolsForWorkspace ( query , reporter , token ) { this . _runEvaluatorWithCancellationToken ( token , ( ) => { if ( ! query ) { return ; } for ( const sourceFileInfo of this . _sourceFileList ) { if ( ! this . _isUserCode ( sourceFileInfo ) ) { continue ; } if ( ! sourceFileInfo . sourceFile . getCachedIndexResults ( ) ) { this . _bindFile ( sourceFileInfo ) ; } const symbolList = sourceFileInfo . sourceFile . getSymbolsForDocument ( query , token ) ; if ( symbolList . length > 0 ) { reporter ( symbolList ) ; } this . _handleMemoryHighUsage ( ) ; } } ) ; } getHoverForPosition ( filePath , position , format , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getHoverForPosition ( this . _createSourceMapper ( execEnv , true ) , position , format , this . _evaluator ! , token ) ; } ) ; } getDocumentHighlight ( filePath , position , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getDocumentHighlight ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , token ) ; } ) ; } getSignatureHelpForPosition ( filePath , position , format , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getSignatureHelpForPosition ( position , this . _createSourceMapper ( execEnv , true ) , this . _evaluator ! , format , token ) ; } ) ; } async getCompletionsForPosition ( filePath , position , workspacePath , options , nameMap , libraryMap , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } const completionResult = this . _logTracker . log ( `completion at ${ filePath }:${ position . line }:${ position . character }` , ( ls ) => { const result = this . _runEvaluatorWithCancellationToken ( token , ( ) => { this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; return sourceFileInfo . sourceFile . getCompletionsForPosition ( position , workspacePath , this . _configOptions , this . _importResolver , this . _lookUpImport , this . _evaluator ! , options , this . _createSourceMapper ( execEnv , true ) , nameMap , libraryMap , ( ) => this . _buildModuleSymbolsMap ( sourceFileInfo , ! ! libraryMap , false , token ) , token ) ; } ) ; ls . add ( `found ${ result ?. completionList ?. items . length ?? 'null' } items` ) ; return result ; } ) ; if ( ! completionResult ?. completionList || ! this . _extension ?. completionListExtension ) { return completionResult ; } const parseResults = sourceFileInfo . sourceFile . getParseResults ( ) ; if ( parseResults ?. parseTree && parseResults ?. text ) { const offset = convertPositionToOffset ( position , parseResults . tokenizerOutput . lines ) ; if ( offset !== undefined ) { await this . _extension . completionListExtension . updateCompletionResults ( completionResult , parseResults , offset , token ) ; } } return completionResult ; } resolveCompletionItem ( filePath , completionItem , options , nameMap , libraryMap , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; sourceFileInfo . sourceFile . resolveCompletionItem ( this . _configOptions , this . _importResolver , this . _lookUpImport , this . _evaluator ! , options , this . _createSourceMapper ( execEnv , true ) , nameMap , libraryMap , ( ) => this . _buildModuleSymbolsMap ( sourceFileInfo , ! ! libraryMap , false , token ) , completionItem , token ) ; } ) ; } renameModule ( path , newPath , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { if ( isFile ( this . _fs , path ) ) { const fileInfo = this . _getSourceFileInfoFromPath ( path ) ; if ( ! fileInfo ) { return undefined ; } } const renameModuleProvider = RenameModuleProvider . createForModule ( this . _importResolver , this . _configOptions , this . _evaluator ! , path , newPath , token ) ; if ( ! renameModuleProvider ) { return undefined ; } this . _processModuleReferences ( renameModuleProvider , renameModuleProvider . lastModuleName , path ) ; return renameModuleProvider . getEdits ( ) ; } ) ; } moveSymbolAtPosition ( filePath , newFilePath , position , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const fileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! fileInfo ) { return undefined ; } this . _bindFile ( fileInfo ) ; const parseResults = fileInfo . sourceFile . getParseResults ( ) ; if ( ! parseResults ) { return undefined ; } const offset = convertPositionToOffset ( position , parseResults . tokenizerOutput . lines ) ; if ( offset === undefined ) { return undefined ; } const node = findNodeByOffset ( parseResults . parseTree , offset ) ; if ( node === undefined ) { return undefined ; } if ( node . nodeType !== ParseNodeType . Name ) { return undefined ; } const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const declarations = DocumentSymbolCollector . getDeclarationsForNode ( node , this . _evaluator ! , false , token , this . _createSourceMapper ( execEnv ) ) ; const renameModuleProvider = RenameModuleProvider . createForSymbol ( this . _importResolver , this . _configOptions , this . _evaluator ! , filePath , newFilePath , declarations , token ) ; if ( ! renameModuleProvider ) { return undefined ; } this . _processModuleReferences ( renameModuleProvider , node . value , filePath ) ; return { edits : renameModuleProvider . getEdits ( ) , fileOperations : [ ] } ; } ) ; } renameSymbolAtPosition ( filePath , position , newName , isDefaultWorkspace , token ) { return this . _runEvaluatorWithCancellationToken ( token , ( ) => { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const referencesResult = sourceFileInfo . sourceFile . getDeclarationForPosition ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , undefined , token ) ; if ( ! referencesResult ) { return undefined ; } removeArrayElements ( referencesResult . declarations , ( d ) => { if ( ! isAliasDeclaration ( d ) ) { return false ; } if ( ! d . usesLocalName || ! d . node ) { return true ; } if ( d . node . nodeType === ParseNodeType . ImportFrom ) { return true ; } if ( d . node . alias ?. value !== referencesResult . symbolName ) { return true ; } return false ; } ) ; if ( referencesResult . declarations . length === 0 ) { return undefined ; } if ( ! isDefaultWorkspace && referencesResult . declarations . some ( ( d ) => ! this . _isUserCode ( this . _getSourceFileInfoFromPath ( d . path ) ) ) ) { return undefined ; } if ( referencesResult . requiresGlobalSearch && ! isDefaultWorkspace ) { for ( const curSourceFileInfo of this . _sourceFileList ) { if ( this . _isUserCode ( curSourceFileInfo ) ) { this . _bindFile ( curSourceFileInfo ) ; curSourceFileInfo . sourceFile . addReferences ( referencesResult , true , this . _evaluator ! , token ) ; } this . _handleMemoryHighUsage ( ) ; } } else if ( isDefaultWorkspace || this . _isUserCode ( sourceFileInfo ) ) { sourceFileInfo . sourceFile . addReferences ( referencesResult , true , this . _evaluator ! , token ) ; } const editActions = [ ] ; referencesResult . locations . forEach ( ( loc ) => { editActions . push ( { filePath : loc . path , range : loc . range , replacementText : newName , } ) ; } ) ; return editActions ; } ) ; } getCallForPosition ( filePath , position , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const referencesResult = sourceFileInfo . sourceFile . getDeclarationForPosition ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , undefined , token ) ; if ( ! referencesResult || referencesResult . declarations . length === 0 ) { return undefined ; } const targetDecl = CallHierarchyProvider . getTargetDeclaration ( referencesResult . declarations , referencesResult . nodeAtOffset ) ; return CallHierarchyProvider . getCallForDeclaration ( referencesResult . symbolName , targetDecl , this . _evaluator ! , token ) ; } getIncomingCallsForPosition ( filePath , position , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const referencesResult = sourceFileInfo . sourceFile . getDeclarationForPosition ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , undefined , token ) ; if ( ! referencesResult || referencesResult . declarations . length === 0 ) { return undefined ; } const targetDecl = CallHierarchyProvider . getTargetDeclaration ( referencesResult . declarations , referencesResult . nodeAtOffset ) ; let items = [ ] ; for ( const curSourceFileInfo of this . _sourceFileList ) { if ( this . _isUserCode ( curSourceFileInfo ) || curSourceFileInfo . isOpenByClient ) { this . _bindFile ( curSourceFileInfo ) ; const itemsToAdd = CallHierarchyProvider . getIncomingCallsForDeclaration ( curSourceFileInfo . sourceFile . getFilePath ( ) , referencesResult . symbolName , targetDecl , curSourceFileInfo . sourceFile . getParseResults ( ) ! , this . _evaluator ! , token ) ; if ( itemsToAdd ) { items = items . concat ( ... itemsToAdd ) ; } this . _handleMemoryHighUsage ( ) ; } } return items ; } getOutgoingCallsForPosition ( filePath , position , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; const execEnv = this . _configOptions . findExecEnvironment ( filePath ) ; const referencesResult = sourceFileInfo . sourceFile . getDeclarationForPosition ( this . _createSourceMapper ( execEnv ) , position , this . _evaluator ! , undefined , token ) ; if ( ! referencesResult || referencesResult . declarations . length === 0 ) { return undefined ; } const targetDecl = CallHierarchyProvider . getTargetDeclaration ( referencesResult . declarations , referencesResult . nodeAtOffset ) ; return CallHierarchyProvider . getOutgoingCallsForDeclaration ( targetDecl , sourceFileInfo . sourceFile . getParseResults ( ) ! , this . _evaluator ! , token ) ; } performQuickAction ( filePath , command , args , token ) { const sourceFileInfo = this . _getSourceFileInfoFromPath ( filePath ) ; if ( ! sourceFileInfo ) { return undefined ; } this . _bindFile ( sourceFileInfo ) ; return sourceFileInfo . sourceFile . performQuickAction ( command , args , token ) ; } test_createSourceMapper ( execEnv ) { return this . _createSourceMapper ( execEnv , false ) ; } private _processModuleReferences ( renameModuleProvider , filteringText , currentFilePath ) { for ( const currentFileInfo of this . _sourceFileList ) { if ( ! this . _isUserCode ( currentFileInfo ) ) { continue ; } const filePath = currentFileInfo . sourceFile . getFilePath ( ) ; const content = currentFileInfo . sourceFile . getFileContent ( ) ?? '' ; if ( filePath !== currentFilePath && content . indexOf ( filteringText ) < 0 ) { continue ; } this . _bindFile ( currentFileInfo , content ) ; const parseResult = currentFileInfo . sourceFile . getParseResults ( ) ; if ( ! parseResult ) { continue ; } renameModuleProvider . renameReferences ( filePath , parseResult ) ; this . _handleMemoryHighUsage ( ) ; } } private _handleMemoryHighUsage ( ) { const typeCacheSize = this . _evaluator ! . getTypeCacheSize ( ) ; if ( typeCacheSize > 750000 || this . _parsedFileCount > 1000 ) { const heapSizeInMb = Math . round ( process . memoryUsage ( ) . heapUsed / ( 1024 * 1024 ) ) ; if ( heapSizeInMb > 1536 ) { this . _console . info ( `Emptying type cache to avoid heap overflow. Heap size used: ${ heapSizeInMb }MB` ) ; this . _createNewEvaluator ( ) ; this . _discardCachedParseResults ( ) ; this . _parsedFileCount = 0 ; } } } private _discardCachedParseResults ( ) { for ( const sourceFileInfo of this . _sourceFileList ) { sourceFileInfo . sourceFile . dropParseAndBindInfo ( ) ; } } private _isUserCode ( fileInfo ) { return fileInfo && fileInfo . isTracked && ! fileInfo . isThirdPartyImport && ! fileInfo . isTypeshedFile ; } private _runEvaluatorWithCancellationToken < T > ( token , callback ) { try { if ( token ) { return this . _evaluator ! . runWithCancellationToken ( token , callback ) ; } else { return callback ( ) ; } } catch ( e ) { if ( ! ( e instanceof OperationCanceledException ) ) { this . _createNewEvaluator ( ) ; } throw e ; } } private _removeUnneededFiles ( ) { const fileDiagnostics = [ ] ; for ( let i = 0 ; i < this . _sourceFileList . length ; ) { const fileInfo = this . _sourceFileList [ i ] ; if ( ! this . _isFileNeeded ( fileInfo ) ) { fileDiagnostics . push ( { filePath : fileInfo . sourceFile . getFilePath ( ) , version : fileInfo . sourceFile . getClientVersion ( ) , diagnostics : [ ] , } ) ; fileInfo . sourceFile . prepareForClose ( ) ; this . _removeSourceFileFromListAndMap ( fileInfo . sourceFile . getFilePath ( ) , i ) ; fileInfo . imports . forEach ( ( importedFile ) => { const indexToRemove = importedFile . importedBy . findIndex ( ( fi ) => fi === fileInfo ) ; assert ( indexToRemove >= 0 ) ; importedFile . importedBy . splice ( indexToRemove , 1 ) ; if ( ! this . _isFileNeeded ( importedFile ) ) { const indexToRemove = this . _sourceFileList . findIndex ( ( fi ) => fi === importedFile ) ; if ( indexToRemove >= 0 && indexToRemove < i ) { fileDiagnostics . push ( { filePath : importedFile . sourceFile . getFilePath ( ) , version : importedFile . sourceFile . getClientVersion ( ) , diagnostics : [ ] , } ) ; importedFile . sourceFile . prepareForClose ( ) ; this . _removeSourceFileFromListAndMap ( importedFile . sourceFile . getFilePath ( ) , indexToRemove ) ; i -- ; } } } ) ; fileInfo . shadowedBy . forEach ( ( shadowedFile ) => { shadowedFile . shadows = shadowedFile . shadows . filter ( ( f ) => f !== fileInfo ) ; } ) ; fileInfo . shadowedBy = [ ] ; } else { if ( ! this . _shouldCheckFile ( fileInfo ) && fileInfo . diagnosticsVersion !== undefined ) { fileDiagnostics . push ( { filePath : fileInfo . sourceFile . getFilePath ( ) , version : fileInfo . sourceFile . getClientVersion ( ) , diagnostics : [ ] , } ) ; fileInfo . diagnosticsVersion = undefined ; } i ++ ; } } return fileDiagnostics ; } private _isFileNeeded ( fileInfo ) { if ( fileInfo . sourceFile . isFileDeleted ( ) ) { return false ; } if ( fileInfo . isTracked || fileInfo . isOpenByClient ) { return true ; } if ( fileInfo . shadows . length > 0 ) { return true ; } if ( fileInfo . importedBy . length === 0 ) { return false ; } return this . _isImportNeededRecursive ( fileInfo , new Map < string , boolean > ( ) ) ; } private _isImportNeededRecursive ( fileInfo , recursionMap ) { if ( fileInfo . isTracked || fileInfo . isOpenByClient || fileInfo . shadows . length > 0 ) { return true ; } const filePath = normalizePathCase ( this . _fs , fileInfo . sourceFile . getFilePath ( ) ) ; if ( recursionMap . has ( filePath ) ) { return false ; } recursionMap . set ( filePath , true ) ; for ( const importerInfo of fileInfo . importedBy ) { if ( this . _isImportNeededRecursive ( importerInfo , recursionMap ) ) { return true ; } } return false ; } private _createSourceMapper ( execEnv , mapCompiled ? , preferStubs ? ) { const sourceMapper = new SourceMapper ( this . _importResolver , execEnv , this . _evaluator ! , ( stubFilePath , implFilePath ) => { const stubFileInfo = this . _getSourceFileInfoFromPath ( stubFilePath ) ; if ( ! stubFileInfo ) { return undefined ; } this . _addShadowedFile ( stubFileInfo , implFilePath ) ; return this . getBoundSourceFile ( implFilePath ) ; } , ( f ) => this . getBoundSourceFile ( f ) , mapCompiled ?? false , preferStubs ?? false ) ; return sourceMapper ; } private _isImportAllowed ( importer , importResult , isImportStubFile ) { if ( importResult . isNativeLib ) { return false ; } let thirdPartyImportAllowed = this . _configOptions . useLibraryCodeForTypes || ( importResult . importType === ImportType . ThirdParty && ! ! importResult . pyTypedInfo ) || ( importResult . importType === ImportType . Local && importer . isThirdPartyPyTypedPresent ) ; if ( importResult . importType === ImportType . ThirdParty || ( importer . isThirdPartyImport && importResult . importType === ImportType . Local ) ) { if ( this . _allowedThirdPartyImports ) { if ( importResult . isRelative ) { thirdPartyImportAllowed = true ; } else if ( this . _allowedThirdPartyImports . some ( ( importName ) => { if ( importResult . importName === importName ) { return true ; } if ( importResult . importName . startsWith ( importName + '.' ) ) { return true ; } return false ; } ) ) { thirdPartyImportAllowed = true ; } } if ( ! isImportStubFile ) { return thirdPartyImportAllowed ; } } return true ; } private _updateSourceFileImports ( sourceFileInfo , options ) { const filesAdded = [ ] ; const imports = sourceFileInfo . sourceFile . getImports ( ) ; const getThirdPartyImportInfo = ( importResult ) => { let isThirdPartyImport = false ; let isPyTypedPresent = false ; if ( importResult . importType === ImportType . ThirdParty ) { isThirdPartyImport = true ; if ( importResult . pyTypedInfo ) { isPyTypedPresent = true ; } } else if ( sourceFileInfo . isThirdPartyImport && importResult . importType === ImportType . Local ) { isThirdPartyImport = true ; if ( sourceFileInfo . isThirdPartyPyTypedPresent ) { isPyTypedPresent = true ; } } return { isThirdPartyImport , isPyTypedPresent , } ; } ; const newImportPathMap = new Map < string , UpdateImportInfo > ( ) ; imports . forEach ( ( importResult ) => { if ( importResult . isImportFound ) { if ( this . _isImportAllowed ( sourceFileInfo , importResult , importResult . isStubFile ) ) { if ( importResult . resolvedPaths . length > 0 ) { const filePath = importResult . resolvedPaths [ importResult . resolvedPaths . length - 1 ] ; if ( filePath ) { const thirdPartyTypeInfo = getThirdPartyImportInfo ( importResult ) ; newImportPathMap . set ( normalizePathCase ( this . _fs , filePath ) , { path : filePath , isTypeshedFile : ! ! importResult . isTypeshedFile , isThirdPartyImport : thirdPartyTypeInfo . isThirdPartyImport , isPyTypedPresent : thirdPartyTypeInfo . isPyTypedPresent , } ) ; } } } importResult . filteredImplicitImports . forEach ( ( implicitImport ) => { if ( this . _isImportAllowed ( sourceFileInfo , importResult , implicitImport . isStubFile ) ) { if ( ! implicitImport . isNativeLib ) { const thirdPartyTypeInfo = getThirdPartyImportInfo ( importResult ) ; newImportPathMap . set ( normalizePathCase ( this . _fs , implicitImport . path ) , { path : implicitImport . path , isTypeshedFile : ! ! importResult . isTypeshedFile , isThirdPartyImport : thirdPartyTypeInfo . isThirdPartyImport , isPyTypedPresent : thirdPartyTypeInfo . isPyTypedPresent , } ) ; } } } ) ; } else if ( options . verboseOutput ) { this . _console . info ( `Could not import '${ importResult . importName }' ` + `in file '${ sourceFileInfo . sourceFile . getFilePath ( ) }'` ) ; if ( importResult . importFailureInfo ) { importResult . importFailureInfo . forEach ( ( diag ) => { this . _console . info ( `  ${ diag }` ) ; } ) ; } } } ) ; const updatedImportMap = new Map < string , SourceFileInfo > ( ) ; sourceFileInfo . imports . forEach ( ( importInfo ) => { const oldFilePath = normalizePathCase ( this . _fs , importInfo . sourceFile . getFilePath ( ) ) ; if ( ! newImportPathMap . has ( oldFilePath ) ) { importInfo . importedBy = importInfo . importedBy . filter ( ( fi ) => normalizePathCase ( this . _fs , fi . sourceFile . getFilePath ( ) ) !== normalizePathCase ( this . _fs , sourceFileInfo . sourceFile . getFilePath ( ) ) ) ; } else { updatedImportMap . set ( oldFilePath , importInfo ) ; } } ) ; newImportPathMap . forEach ( ( importInfo , normalizedImportPath ) => { if ( ! updatedImportMap . has ( normalizedImportPath ) ) { let importedFileInfo ; if ( this . _getSourceFileInfoFromPath ( importInfo . path ) ) { importedFileInfo = this . _getSourceFileInfoFromPath ( importInfo . path ) ! ; } else { const importName = this . _getImportNameForFile ( importInfo . path ) ; const sourceFile = new SourceFile ( this . _fs , importInfo . path , importName , importInfo . isThirdPartyImport , importInfo . isPyTypedPresent , this . _console , this . _logTracker ) ; importedFileInfo = { sourceFile , isTracked : false , isOpenByClient : false , isTypeshedFile : importInfo . isTypeshedFile , isThirdPartyImport : importInfo . isThirdPartyImport , isThirdPartyPyTypedPresent : importInfo . isPyTypedPresent , diagnosticsVersion : undefined , imports : [ ] , importedBy : [ ] , shadows : [ ] , shadowedBy : [ ] , } ; this . _addToSourceFileListAndMap ( importedFileInfo ) ; filesAdded . push ( importedFileInfo ) ; } importedFileInfo . importedBy . push ( sourceFileInfo ) ; updatedImportMap . set ( normalizedImportPath , importedFileInfo ) ; } } ) ; sourceFileInfo . imports = [ ] ; newImportPathMap . forEach ( ( _ , path ) => { if ( this . _getSourceFileInfoFromPath ( path ) ) { sourceFileInfo . imports . push ( this . _getSourceFileInfoFromPath ( path ) ! ) ; } } ) ; sourceFileInfo . builtinsImport = undefined ; const builtinsImport = sourceFileInfo . sourceFile . getBuiltinsImport ( ) ; if ( builtinsImport && builtinsImport . isImportFound ) { const resolvedBuiltinsPath = builtinsImport . resolvedPaths [ builtinsImport . resolvedPaths . length - 1 ] ; sourceFileInfo . builtinsImport = this . _getSourceFileInfoFromPath ( resolvedBuiltinsPath ) ; } return filesAdded ; } private _getSourceFileInfoFromPath ( filePath ) { return this . _sourceFileMap . get ( normalizePathCase ( this . _fs , filePath ) ) ; } private _removeSourceFileFromListAndMap ( filePath , indexToRemove ) { this . _sourceFileMap . delete ( normalizePathCase ( this . _fs , filePath ) ) ; this . _sourceFileList . splice ( indexToRemove , 1 ) ; } private _addToSourceFileListAndMap ( fileInfo ) { const filePath = normalizePathCase ( this . _fs , fileInfo . sourceFile . getFilePath ( ) ) ; assert ( ! this . _sourceFileMap . has ( filePath ) ) ; this . _sourceFileList . push ( fileInfo ) ; this . _sourceFileMap . set ( filePath , fileInfo ) ; } }