public async Task <IDocumentAnalysis> GetAnalysisAsync(IPythonModule module, int waitTime, CancellationToken cancellationToken) { var key = new AnalysisModuleKey(module); PythonAnalyzerEntry entry; lock (_syncObj) { if (!_analysisEntries.TryGetValue(key, out entry)) { var emptyAnalysis = new EmptyAnalysis(_services, (IDocument)module); entry = new PythonAnalyzerEntry(emptyAnalysis); _analysisEntries[key] = entry; } } if (waitTime < 0 || Debugger.IsAttached) { return(await GetAnalysisAsync(entry, default, cancellationToken));
private void AnalyzeLoop(IDependencyChainLoopNode <PythonAnalyzerEntry> loopNode, Stopwatch stopWatch) { var version = _walker.Version; var entries = new Dictionary <AnalysisModuleKey, (IPythonModule Module, PythonAnalyzerEntry Entry)>(); var variables = new Dictionary <(AnalysisModuleKey Module, string Name), int>(); var importNames = new List <(AnalysisModuleKey From, int FromPosition, AnalysisModuleKey To, string ToName)>(); var cachedVariables = new Dictionary <AnalysisModuleKey, IVariableCollection>(); var asts = new Dictionary <AnalysisModuleKey, PythonAst>(); var startTime = stopWatch.Elapsed; // Note: loop analysis is not cancellable. The reason is that when smaller loop // appears inside a larger loop gets canceled, it will not be re-walked during // the outer loop analysis. For example, functools <=> _functools loop and // the related CircularDependencyFunctools test. foreach (var entry in loopNode.Values) { ActivityTracker.OnEnqueueModule(entry.Module.FilePath); if (!CanUpdateAnalysis(entry, Version, out var module, out var ast)) { _log?.Log(TraceEventType.Verbose, $"Analysis of loop canceled."); return; } var moduleKey = new AnalysisModuleKey(module); entries[moduleKey] = (module, entry); var analysis = _analyzer.TryRestoreCachedAnalysis(module); if (analysis != null) { AddLoopImportsFromCachedAnalysis(importNames, variables, moduleKey, analysis); cachedVariables.Add(moduleKey, analysis.GlobalScope.Variables); } else { AddLoopImportsFromAst(importNames, variables, moduleKey, ast); asts.Add(moduleKey, ast); } } if (asts.Count == 0) { // Fully cached loop if (_log != null && _log.LogLevel == TraceEventType.Verbose) { var names = string.Join(", ", cachedVariables.Select(v => v.Key.Name)); _log?.Log(TraceEventType.Verbose, $"Fully cached modules cycle: {names}"); } return; } var imports = new List <(AnalysisModuleKey From, int FromPosition, AnalysisModuleKey To, int ToPosition)>(); foreach (var(fromModule, fromPosition, toModule, toName) in importNames) { if (!entries.ContainsKey(toModule)) { continue; } if (toName == null) { imports.Add((fromModule, fromPosition, toModule, 0)); } else if (variables.TryGetValue((toModule, toName), out var toPosition)) { imports.Add((fromModule, fromPosition, toModule, toPosition)); } } var startingKeys = LocationLoopResolver <AnalysisModuleKey> .FindStartingItems(imports); var variableHandler = new LoopImportedVariableHandler(_services, asts, cachedVariables, () => false); foreach (var key in startingKeys) { if (asts.TryGetValue(key, out var startingAst) && entries.TryGetValue(key, out var me)) { variableHandler.WalkModule(me.Module, startingAst); } } foreach (var walker in variableHandler.Walkers) { asts.Remove(new AnalysisModuleKey(walker.Module)); } while (asts.Count > 0) { var(moduleKey, ast) = asts.First(); variableHandler.WalkModule(entries[moduleKey].Module, ast); foreach (var walker in variableHandler.Walkers) { asts.Remove(new AnalysisModuleKey(walker.Module)); } } foreach (var walker in variableHandler.Walkers) { var module = (IDocument)walker.Module; var moduleKey = new AnalysisModuleKey(module); if (entries.TryGetValue(moduleKey, out var e)) { var analysis = CreateAnalysis(null, module, walker.Ast, version, walker); CompleteAnalysis(e.Entry, module, version, analysis); } } loopNode.MarkWalked(); LogCompleted(loopNode, entries.Values.Select(v => v.Module), stopWatch, startTime); }