diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.AnalyzerAndOptions.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.AnalyzerAndOptions.cs index e0e93ded70efa89cab16398031cd2d362837c362..d82bc31492092d847777f2bee4988b139741ff51 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.AnalyzerAndOptions.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.AnalyzerAndOptions.cs @@ -9,7 +9,7 @@ internal partial class AnalyzerManager { private sealed class AnalyzerAndOptions { - private readonly DiagnosticAnalyzer _analyzer; + public readonly DiagnosticAnalyzer Analyzer; private readonly AnalyzerOptions _analyzerOptions; public AnalyzerAndOptions(DiagnosticAnalyzer analyzer, AnalyzerOptions analyzerOptions) @@ -17,7 +17,7 @@ public AnalyzerAndOptions(DiagnosticAnalyzer analyzer, AnalyzerOptions analyzerO Debug.Assert(analyzer != null); Debug.Assert(analyzerOptions != null); - _analyzer = analyzer; + Analyzer = analyzer; _analyzerOptions = analyzerOptions; } @@ -29,7 +29,7 @@ public bool Equals(AnalyzerAndOptions other) } return other != null && - _analyzer.Equals(other._analyzer) && + Analyzer.Equals(other.Analyzer) && _analyzerOptions.Equals(other._analyzerOptions); } @@ -40,7 +40,7 @@ public override bool Equals(object other) public override int GetHashCode() { - return Hash.Combine(_analyzer.GetHashCode(), _analyzerOptions.GetHashCode()); + return Hash.Combine(Analyzer.GetHashCode(), _analyzerOptions.GetHashCode()); } } } diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs index 79a5883e8154b2ecb85e204041226f63446d6f94..5f08d135bf75ce5f13159328764494d40ba152de 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -31,31 +32,28 @@ internal partial class AnalyzerManager // This map stores the tasks to compute HostSessionStartAnalysisScope for session wide analyzer actions, i.e. AnalyzerActions registered by analyzer's Initialize method. // These are run only once per every analyzer. - private readonly ConditionalWeakTable> _sessionScopeMap = - new ConditionalWeakTable>(); + private readonly ConcurrentDictionary> _sessionScopeMap = + new ConcurrentDictionary>(concurrencyLevel: 2, capacity: 5); // This map stores the tasks to compute HostCompilationStartAnalysisScope for per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions. // Compilation start actions will get executed once per-each AnalyzerAndOptions as user might want to return different set of custom actions for each compilation/analyzer options. - private readonly ConditionalWeakTable>> _compilationScopeMap = - new ConditionalWeakTable>>(); - - private readonly ConditionalWeakTable>>.CreateValueCallback _compilationScopeMapCallback = - comp => new ConcurrentDictionary>(concurrencyLevel: 2, capacity: 5); + private readonly ConcurrentDictionary>> _compilationScopeMap = + new ConcurrentDictionary>>(concurrencyLevel: 2, capacity: 5); /// /// Cache descriptors for each diagnostic analyzer. We do this since is /// a property rather than metadata. We expect it to be cheap and immutable, but we can't force them to be so, we cache them /// and ask only once. /// - private readonly ConditionalWeakTable, EventHandler>> _descriptorCache = - new ConditionalWeakTable, EventHandler>>(); + private readonly ConcurrentDictionary, EventHandler>> _descriptorCache = + new ConcurrentDictionary, EventHandler>>(concurrencyLevel: 2, capacity: 5); private Task GetCompilationAnalysisScopeCoreAsync( AnalyzerAndOptions analyzerAndOptions, HostSessionStartAnalysisScope sessionScope, AnalyzerExecutor analyzerExecutor) { - Func> getTask = a => + Func> getTask = comp => { return Task.Run(() => { @@ -65,8 +63,9 @@ internal partial class AnalyzerManager }, analyzerExecutor.CancellationToken); }; - var compilationActionsMap = _compilationScopeMap.GetValue(analyzerExecutor.Compilation, _compilationScopeMapCallback); - return compilationActionsMap.GetOrAdd(analyzerAndOptions, getTask); + var callback = new ConditionalWeakTable>.CreateValueCallback(getTask); + var compilationActionsMap = _compilationScopeMap.GetOrAdd(analyzerAndOptions, new ConditionalWeakTable>()); + return compilationActionsMap.GetValue(analyzerExecutor.Compilation, callback); } private async Task GetCompilationAnalysisScopeAsync( @@ -84,9 +83,11 @@ internal partial class AnalyzerManager { // Task to compute the scope was cancelled. // Clear the entry in scope map for analyzer, so we can attempt a retry. - var compilationActionsMap = _compilationScopeMap.GetOrCreateValue(analyzerExecutor.Compilation); - Task cancelledTask; - compilationActionsMap.TryRemove(analyzerAndOptions, out cancelledTask); + ConditionalWeakTable> compilationActionsMap; + if (_compilationScopeMap.TryGetValue(analyzerAndOptions, out compilationActionsMap)) + { + compilationActionsMap.Remove(analyzerExecutor.Compilation); + } analyzerExecutor.CancellationToken.ThrowIfCancellationRequested(); return await GetCompilationAnalysisScopeAsync(analyzer, sessionScope, analyzerExecutor).ConfigureAwait(false); @@ -107,8 +108,7 @@ internal partial class AnalyzerManager }, analyzerExecutor.CancellationToken); }; - var callback = new ConditionalWeakTable>.CreateValueCallback(getTask); - return _sessionScopeMap.GetValue(analyzer, callback); + return _sessionScopeMap.GetOrAdd(analyzer, getTask); } private async Task GetSessionAnalysisScopeAsync( @@ -123,7 +123,8 @@ internal partial class AnalyzerManager { // Task to compute the scope was cancelled. // Clear the entry in scope map for analyzer, so we can attempt a retry. - _sessionScopeMap.Remove(analyzer); + Task cancelledTask; + _sessionScopeMap.TryRemove(analyzer, out cancelledTask); analyzerExecutor.CancellationToken.ThrowIfCancellationRequested(); return await GetSessionAnalysisScopeAsync(analyzer, analyzerExecutor).ConfigureAwait(false); @@ -178,25 +179,30 @@ public async Task GetAnalyzerHasDependentCompilationEndAsync(DiagnosticAna DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor) { - var descriptors = _descriptorCache.GetValue(analyzer, key => + var descriptors = _descriptorCache.GetOrAdd(analyzer, key => { var supportedDiagnostics = ImmutableArray.Empty; // Catch Exception from analyzer.SupportedDiagnostics analyzerExecutor.ExecuteAndCatchIfThrows(analyzer, () => { supportedDiagnostics = analyzer.SupportedDiagnostics; }); - var handler = new EventHandler((sender, ex) => + EventHandler handler = null; + Action onAnalyzerException = analyzerExecutor.OnAnalyzerException; + if (onAnalyzerException != null) + { + handler = new EventHandler((sender, ex) => { var diagnostic = AnalyzerExecutor.GetAnalyzerExceptionDiagnostic(analyzer, ex); - analyzerExecutor.OnAnalyzerException?.Invoke(ex, analyzer, diagnostic); + onAnalyzerException(ex, analyzer, diagnostic); }); - // Subscribe for exceptions from lazily evaluated localizable strings in the descriptors. - foreach (var descriptor in supportedDiagnostics) - { - descriptor.Title.OnException += handler; - descriptor.MessageFormat.OnException += handler; - descriptor.Description.OnException += handler; + // Subscribe for exceptions from lazily evaluated localizable strings in the descriptors. + foreach (var descriptor in supportedDiagnostics) + { + descriptor.Title.OnException += handler; + descriptor.MessageFormat.OnException += handler; + descriptor.Description.OnException += handler; + } } return Tuple.Create(supportedDiagnostics, handler); @@ -205,23 +211,54 @@ public async Task GetAnalyzerHasDependentCompilationEndAsync(DiagnosticAna return descriptors.Item1; } - internal void ClearAnalyzerExceptionHandlers(DiagnosticAnalyzer analyzer) + /// + /// This method should be invoked when the analyzer host is disposing off the analyzers. + /// It unregisters the exception handler hooked up to the descriptors' LocalizableString fields and subsequently removes the cached descriptors for the analyzers. + /// + internal void ClearAnalyzerState(ImmutableArray analyzers) + { + foreach (var analyzer in analyzers) + { + ClearDescriptorState(analyzer); + ClearAnalysisScopeState(analyzer); + } + } + + private void ClearDescriptorState(DiagnosticAnalyzer analyzer) { // Host is disposing the analyzer instance, unsubscribe analyzer exception handlers. Tuple, EventHandler> value; - if (_descriptorCache.TryGetValue(analyzer, out value)) + if (_descriptorCache.TryRemove(analyzer, out value)) { var descriptors = value.Item1; var handler = value.Item2; - foreach (var descriptor in descriptors) + if (handler != null) { - descriptor.Title.OnException -= handler; - descriptor.MessageFormat.OnException -= handler; - descriptor.Description.OnException -= handler; + foreach (var descriptor in descriptors) + { + descriptor.Title.OnException -= handler; + descriptor.MessageFormat.OnException -= handler; + descriptor.Description.OnException -= handler; + } } } } + private void ClearAnalysisScopeState(DiagnosticAnalyzer analyzer) + { + // Clear session scope. + Task canceledTask; + _sessionScopeMap.TryRemove(analyzer, out canceledTask); + + // Clear compilation scope. + var keysToRemove = _compilationScopeMap.Keys.Where(analyzerAndOptions => analyzerAndOptions.Analyzer.Equals(analyzer)).ToImmutableArray(); + foreach (var analyzerAndOptions in keysToRemove) + { + ConditionalWeakTable> map; + _compilationScopeMap.TryRemove(analyzerAndOptions, out map); + } + } + internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Func isCompilerAnalyzer, AnalyzerExecutor analyzerExecutor) { // Avoid realizing all the descriptors for all compiler diagnostics by assuming that compiler analyzer doesn't report unsupported diagnostics. diff --git a/src/Compilers/Core/Desktop/AnalyzerFileReference.AssemblyPathHelper.cs b/src/Compilers/Core/Desktop/AnalyzerFileReference.AssemblyPathHelper.cs index 654da5cfbedc6ff1c34919fbbdd48afb897d083f..8e8eb7545249dd428312075f68c33d815bc896d9 100644 --- a/src/Compilers/Core/Desktop/AnalyzerFileReference.AssemblyPathHelper.cs +++ b/src/Compilers/Core/Desktop/AnalyzerFileReference.AssemblyPathHelper.cs @@ -1,13 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { diff --git a/src/Compilers/Core/Desktop/AnalyzerFileReference.cs b/src/Compilers/Core/Desktop/AnalyzerFileReference.cs index b50c92b40741bcc6bd09d3ae60c60c5987c195d8..026b207267eecee31bca5391f96114e3823c1e37 100644 --- a/src/Compilers/Core/Desktop/AnalyzerFileReference.cs +++ b/src/Compilers/Core/Desktop/AnalyzerFileReference.cs @@ -85,13 +85,27 @@ public override ImmutableArray GetAnalyzersForAllLanguages() { if (_lazyAllAnalyzers.IsDefault) { - var allAnalyzers = MetadataCache.GetOrCreateAnalyzersFromFile(this); - ImmutableInterlocked.InterlockedInitialize(ref _lazyAllAnalyzers, allAnalyzers); + ImmutableInterlocked.InterlockedInitialize(ref _lazyAllAnalyzers, CreateAnalyzersForAllLanguages(this)); } return _lazyAllAnalyzers; } + private static ImmutableArray CreateAnalyzersForAllLanguages(AnalyzerFileReference reference) + { + // Get all analyzers in the assembly. + var map = ImmutableDictionary.CreateBuilder>(); + reference.AddAnalyzers(map); + + var builder = ImmutableArray.CreateBuilder(); + foreach (var analyzers in map.Values) + { + builder.AddRange(analyzers); + } + + return builder.ToImmutable(); + } + public override ImmutableArray GetAnalyzers(string language) { if (string.IsNullOrEmpty(language)) @@ -102,9 +116,12 @@ public override ImmutableArray GetAnalyzers(string language) return ImmutableInterlocked.GetOrAdd(ref _lazyAnalyzersPerLanguage, language, CreateLanguageSpecificAnalyzers, this); } - private static ImmutableArray CreateLanguageSpecificAnalyzers(string language, AnalyzerFileReference @this) + private static ImmutableArray CreateLanguageSpecificAnalyzers(string langauge, AnalyzerFileReference reference) { - return MetadataCache.GetOrCreateAnalyzersFromFile(@this, language); + // Get all analyzers in the assembly for the given language. + var builder = ImmutableArray.CreateBuilder(); + reference.AddAnalyzers(builder, langauge); + return builder.ToImmutable(); } public override string FullPath diff --git a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs index 983cfdce143ee1e72ea93c907c1eecb6e31702aa..904bafdf9f31350a66c13da68cce6d9d769dfb7a 100644 --- a/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Desktop/CommandLine/CommonCompiler.cs @@ -360,13 +360,14 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat cancellationToken.ThrowIfCancellationRequested(); CancellationTokenSource analyzerCts = null; + AnalyzerManager analyzerManager = null; try { Func> getAnalyzerDiagnostics = null; if (!analyzers.IsDefaultOrEmpty) { analyzerCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var analyzerManager = new AnalyzerManager(); + analyzerManager = new AnalyzerManager(); var analyzerExceptionDiagnostics = new ConcurrentSet(); Action addExceptionDiagnostic = diagnostic => analyzerExceptionDiagnostics.Add(diagnostic); var analyzerOptions = new AnalyzerOptions(ImmutableArray.Create(additionalTextFiles)); @@ -527,6 +528,9 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat if (analyzerCts != null) { analyzerCts.Cancel(); + + // Clear cached analyzer descriptors and unregister exception handlers hooked up to the LocalizableString fields of the associated descriptors. + analyzerManager.ClearAnalyzerState(analyzers); } } diff --git a/src/Compilers/Core/Desktop/MetadataCache.cs b/src/Compilers/Core/Desktop/MetadataCache.cs index 6e0f86ba2553943a59e99d164fbd502cd0d21d80..b904a2a3835d4b1bf216a69b73d2aa8c188ced34 100644 --- a/src/Compilers/Core/Desktop/MetadataCache.cs +++ b/src/Compilers/Core/Desktop/MetadataCache.cs @@ -2,12 +2,9 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; -using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -20,8 +17,6 @@ namespace Microsoft.CodeAnalysis /// 2) A list of weak references to instances of VB/CS AssemblySymbols based on the PEAssembly object. /// /// For modules - a map from file name and timestamp to a weak reference to the corresponding PEModule object - /// - /// For analyzer assemblies - a map from file name and timestamp to a weak reference to the diagnostic analyzers defined in the assembly. /// [Obsolete("To be removed", error: false)] internal static class MetadataCache @@ -42,14 +37,6 @@ internal static class MetadataCache private static List s_moduleKeys = new List(); - /// - /// Global cache for diagnostic analyzers imported from analyzer assembly files. - /// - private static Dictionary s_analyzersFromFiles = - new Dictionary(); - - private static List s_analyzerAssemblyKeys = new List(); - /// /// Timer triggering compact operation for metadata cache. /// @@ -113,22 +100,6 @@ public CachedModule(ModuleMetadata metadata) } } - internal struct CachedAnalyzers - { - // Save a reference to the cached analyzers so that they don't get collected - // if the metadata object gets collected. - public readonly WeakReference Analyzers; - public readonly string Language; - - public CachedAnalyzers(object analyzers, string language) - { - Debug.Assert(analyzers != null); - - this.Analyzers = new WeakReference(analyzers); - this.Language = language; - } - } - /// /// Lock that must be acquired for the duration of read/write operations on MetadataCache. /// @@ -182,13 +153,12 @@ private static void CompactCache(Object state) CompactCacheOfAssemblies(); CompactCacheOfModules(); - CompactCacheOfAnalyzers(); s_compactCollectionCount = currentCollectionCount; lock (Guard) { - if (!(AnyAssembliesCached() || AnyModulesCached() || AnyAnalyzerAssembliesCached())) + if (!(AnyAssembliesCached() || AnyModulesCached())) { // Stop the timer s_compactTimerIsOn = no; @@ -392,71 +362,6 @@ private static bool AnyModulesCached() return s_moduleKeys.Count > 0; } - /// - /// Called by compactTimer. - /// - private static void CompactCacheOfAnalyzers() - { - // Do one pass through the analyzerAssemblyKeys list - int originalCount = -1; - - for (int current = 0; ; current++) - { - // Compact analyzer assemblies, one assembly per lock - - // Lock our cache - lock (Guard) - { - if (originalCount == -1) - { - originalCount = s_analyzerAssemblyKeys.Count; - } - - if (s_analyzerAssemblyKeys.Count > current) - { - CachedAnalyzers cahedAnalyzers; - FileKey key = s_analyzerAssemblyKeys[current]; - - if (s_analyzersFromFiles.TryGetValue(key, out cahedAnalyzers)) - { - if (!cahedAnalyzers.Analyzers.IsAlive) - { - // Analyzers has been collected - s_analyzersFromFiles.Remove(key); - s_analyzerAssemblyKeys.RemoveAt(current); - current--; - } - } - else - { - // Key is not found. Shouldn't ever get here! - System.Diagnostics.Debug.Assert(false); - s_analyzerAssemblyKeys.RemoveAt(current); - current--; - } - } - - if (s_analyzerAssemblyKeys.Count <= current + 1) - { - // no more assemblies to process - if (originalCount > s_analyzerAssemblyKeys.Count) - { - s_analyzerAssemblyKeys.TrimExcess(); - } - - return; - } - } - - Thread.Yield(); - } - } - - private static bool AnyAnalyzerAssembliesCached() - { - return s_analyzerAssemblyKeys.Count > 0; - } - /// /// Global cache for assemblies imported from files. /// Internal accessibility is for test purpose only. @@ -504,29 +409,6 @@ internal static List ModuleKeys } } - /// - /// Global cache for analyzers imported from files. - /// Internal accessibility is for test purpose only. - /// - internal static Dictionary AnalyzersFromFiles - { - get - { - return s_analyzersFromFiles; - } - } - - /// - /// For test purposes only. - /// - internal static List AnalyzerAssemblyKeys - { - get - { - return s_analyzerAssemblyKeys; - } - } - // for testing internal static CleaningCacheLock LockAndClean() { @@ -545,8 +427,6 @@ internal class CleaningCacheLock : IDisposable private List _saveAssemblyKeys; private Dictionary _saveModulesFromFiles; private List _saveModuleKeys; - private Dictionary _saveAnalyzersFromFiles; - private List _saveAnalyzerAssemblyKeys; private bool _cacheIsLocked; @@ -571,22 +451,16 @@ public static CleaningCacheLock LockAndCleanCaches() result._saveAssemblyKeys = s_assemblyKeys; result._saveModulesFromFiles = s_modulesFromFiles; result._saveModuleKeys = s_moduleKeys; - result._saveAnalyzersFromFiles = s_analyzersFromFiles; - result._saveAnalyzerAssemblyKeys = s_analyzerAssemblyKeys; var newAssembliesFromFiles = new Dictionary(); var newAssemblyKeys = new List(); var newModulesFromFiles = new Dictionary(); var newModuleKeys = new List(); - var newAnalyzersFromFiles = new Dictionary(); - var newAnalyzerAssemblyKeys = new List(); s_assembliesFromFiles = newAssembliesFromFiles; s_assemblyKeys = newAssemblyKeys; s_modulesFromFiles = newModulesFromFiles; s_moduleKeys = newModuleKeys; - s_analyzersFromFiles = newAnalyzersFromFiles; - s_analyzerAssemblyKeys = newAnalyzerAssemblyKeys; result._threadId = Thread.CurrentThread.ManagedThreadId; result._stackTrace = Environment.StackTrace; @@ -632,8 +506,6 @@ public void CleanCaches() s_assemblyKeys.Clear(); s_modulesFromFiles.Clear(); s_moduleKeys.Clear(); - s_analyzersFromFiles.Clear(); - s_analyzerAssemblyKeys.Clear(); } public void Dispose() @@ -646,8 +518,6 @@ public void Dispose() s_assemblyKeys = _saveAssemblyKeys; s_modulesFromFiles = _saveModulesFromFiles; s_moduleKeys = _saveModuleKeys; - s_analyzersFromFiles = _saveAnalyzersFromFiles; - s_analyzerAssemblyKeys = _saveAnalyzerAssemblyKeys; Debug.Assert(ReferenceEquals(s_last, this)); Debug.Assert(_threadId == Thread.CurrentThread.ManagedThreadId); @@ -698,97 +568,6 @@ internal static Metadata GetOrCreateFromFile(string fullPath, MetadataImageKind } } - internal static ImmutableArray GetOrCreateAnalyzersFromFile(AnalyzerFileReference analyzerReference, string langauge = null) - { - string fullPath = analyzerReference.FullPath; - Debug.Assert(PathUtilities.IsAbsolute(fullPath)); - - lock (Guard) - { - // may throw: - FileKey key = FileKey.Create(fullPath); - - CachedAnalyzers cachedAnalyzers; - if (s_analyzersFromFiles.TryGetValue(key, out cachedAnalyzers)) - { - if (cachedAnalyzers.Analyzers.IsAlive && cachedAnalyzers.Language == langauge) - { - return (ImmutableArray)cachedAnalyzers.Analyzers.Target; - } - else - { - s_analyzersFromFiles.Remove(key); - var removed = s_analyzerAssemblyKeys.Remove(key); - Debug.Assert(removed); - Debug.Assert(!s_analyzerAssemblyKeys.Contains(key)); - } - } - - if (langauge == null) - { - return CreateAnalyzersFromFile(analyzerReference); - } - - return CreateAnalyzersFromFile(analyzerReference, langauge); - } - } - - private static ImmutableArray CreateAnalyzersFromFile(AnalyzerFileReference reference) - { - Debug.Assert(PathUtilities.IsAbsolute(reference.FullPath)); - - // get all analyzers in the assembly; - var map = ImmutableDictionary.CreateBuilder>(); - reference.AddAnalyzers(map); - - // TODO: fix the cache mechanism. I don't understand how the cache is supposed to work - // so I am leaving it as it is for now. (does weak reference things currently actually work?) - // also, current one looks like assume a file can have analzyers for only one language. - // is this assumption right? - // - // foreach (var kv in mapBuilder) - // { - // CacheAnalyzers(kv.Key, fullPath, kv.Value); - // } - // - // EnableCompactTimer(); - - var array = ImmutableArray.CreateBuilder(); - foreach (var analyzers in map.Values) - { - array.AddRange(analyzers); - } - - return array.ToImmutable(); - } - - private static ImmutableArray CreateAnalyzersFromFile(AnalyzerFileReference reference, string langauge) - { - Debug.Assert(PathUtilities.IsAbsolute(reference.FullPath)); - - // get all analyzers in the assembly for the given language; - var builder = ImmutableArray.CreateBuilder(); - reference.AddAnalyzers(builder, langauge); - var analyzers = builder.ToImmutable(); - - CacheAnalyzers(langauge, reference.FullPath, analyzers); - EnableCompactTimer(); - - return analyzers; - } - - private static void CacheAnalyzers(string langauge, string fullPath, ImmutableArray analyzers) - { - // refresh the timestamp (the file may have changed just before we memory-mapped it): - var key = FileKey.Create(fullPath); - - s_analyzersFromFiles[key] = new CachedAnalyzers(analyzers, langauge); - Debug.Assert(!s_analyzerAssemblyKeys.Contains(key)); - - s_analyzerAssemblyKeys.Add(key); - } - - /// private static AssemblyMetadata GetOrCreateAssemblyFromFile(string fullPath) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 0534dc3aed602d33d96e98530a4f1741b66e97bf..c64261e4b85ede78c02921f396833778953eef60 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -948,10 +948,24 @@ yield return var nodesToAnalyze = descendantDeclsToSkip == null ? declaredNode.DescendantNodesAndSelf(descendIntoTrivia: true) : - declaredNode.DescendantNodesAndSelf(n => !descendantDeclsToSkip.Contains(n), descendIntoTrivia: true).Except(descendantDeclsToSkip); + GetSyntaxNodesToAnalyze(declaredNode, descendantDeclsToSkip); analyzerExecutor.ExecuteSyntaxNodeActions(nodesToAnalyze, actionsByKind, semanticModel, getKind); } + + private static IEnumerable GetSyntaxNodesToAnalyze(SyntaxNode declaredNode, HashSet descendantDeclsToSkip) + { + Debug.Assert(declaredNode != null); + Debug.Assert(descendantDeclsToSkip != null); + + foreach (var node in declaredNode.DescendantNodesAndSelf(n => !descendantDeclsToSkip.Contains(n), descendIntoTrivia: true)) + { + if (!descendantDeclsToSkip.Contains(node)) + { + yield return node; + } + } + } } internal static class AnalyzerDriverResources diff --git a/src/EditorFeatures/Test/Diagnostics/TestDiagnosticAnalyzerService.cs b/src/EditorFeatures/Test/Diagnostics/TestDiagnosticAnalyzerService.cs index 1f9516bc92cd279ad61c484c82fef0070424de17..9118c5f918e3376643adf68a82da422455ee3977 100644 --- a/src/EditorFeatures/Test/Diagnostics/TestDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/Test/Diagnostics/TestDiagnosticAnalyzerService.cs @@ -41,14 +41,14 @@ internal TestDiagnosticAnalyzerService(ImmutableArray workspa _onAnalyzerException = onAnalyzerException; } - internal override Action GetOnAnalyzerException(Project project, DiagnosticLogAggregator diagnosticLogAggregator) + internal override Action GetOnAnalyzerException(ProjectId projectId, DiagnosticLogAggregator diagnosticLogAggregator) { - return _onAnalyzerException ?? base.GetOnAnalyzerException(project, diagnosticLogAggregator); + return _onAnalyzerException ?? base.GetOnAnalyzerException(projectId, diagnosticLogAggregator); } - internal override Action GetOnAnalyzerException_NoTelemetryLogging(Project project) + internal override Action GetOnAnalyzerException_NoTelemetryLogging(ProjectId projectId) { - return _onAnalyzerException ?? base.GetOnAnalyzerException_NoTelemetryLogging(project); + return _onAnalyzerException ?? base.GetOnAnalyzerException_NoTelemetryLogging(projectId); } private class TestAnalyzerReferenceByLanguage : AnalyzerReference diff --git a/src/Features/Core/Diagnostics/AbstractHostDiagnosticUpdateSource.cs b/src/Features/Core/Diagnostics/AbstractHostDiagnosticUpdateSource.cs index 32d29c1d28cb787d34c2aff550f8844e6b7f3e0e..3116b6cc87ccf08cd1e4a75db1992a6ece0c3cbf 100644 --- a/src/Features/Core/Diagnostics/AbstractHostDiagnosticUpdateSource.cs +++ b/src/Features/Core/Diagnostics/AbstractHostDiagnosticUpdateSource.cs @@ -44,13 +44,15 @@ protected void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args) } } - internal void ReportAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace, Project project) + internal void ReportAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace, ProjectId projectId) { if (workspace != this.Workspace) { return; } + var project = workspace.CurrentSolution.GetProject(projectId); + bool raiseDiagnosticsUpdated = true; var diagnosticData = project != null ? DiagnosticData.Create(project, diagnostic) : @@ -74,10 +76,16 @@ internal void ReportAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic d public void ClearAnalyzerReferenceDiagnostics(AnalyzerFileReference analyzerReference, string language, ProjectId projectId) { - foreach (var analyzer in analyzerReference.GetAnalyzers(language)) + var analyzers = analyzerReference.GetAnalyzers(language); + ClearAnalyzerDiagnostics(analyzers, projectId); + AnalyzerManager.Instance.ClearAnalyzerState(analyzers); + } + + private void ClearAnalyzerDiagnostics(ImmutableArray analyzers, ProjectId projectId) + { + foreach (var analyzer in analyzers) { ClearAnalyzerDiagnostics(analyzer, projectId); - AnalyzerManager.Instance.ClearAnalyzerExceptionHandlers(analyzer); } } diff --git a/src/Features/Core/Diagnostics/AnalyzerHelper.cs b/src/Features/Core/Diagnostics/AnalyzerHelper.cs index 94bd472910b96bb7a031946f2a0357c9e8ee4614..ab45e3d589a420e2b3d1aaaa5b22b59e92b0ea6d 100644 --- a/src/Features/Core/Diagnostics/AnalyzerHelper.cs +++ b/src/Features/Core/Diagnostics/AnalyzerHelper.cs @@ -62,7 +62,7 @@ public static bool IsCompilerAnalyzer(this DiagnosticAnalyzer analyzer) DiagnosticAnalyzer analyzer, Diagnostic diagnostic, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, - Project projectOpt = null) + ProjectId projectOpt = null) { if (diagnostic != null) { diff --git a/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs index 323475f7f262d04e3035b4b8dd8c9b1ccfa9b854..9b2e0f70898ecdfef641afa61bebb44161bd5c95 100644 --- a/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs @@ -187,15 +187,15 @@ public virtual void LogAnalyzerCountSummary() } // internal for testing purposes. - internal Action GetOnAnalyzerException(Project project) + internal Action GetOnAnalyzerException(ProjectId projectId) { - return Owner?.GetOnAnalyzerException(project, DiagnosticLogAggregator); + return Owner?.GetOnAnalyzerException(projectId, DiagnosticLogAggregator); } // internal for testing purposes. - internal Action GetOnAnalyzerException_NoTelemetryLogging(Project project) + internal Action GetOnAnalyzerException_NoTelemetryLogging(ProjectId projectId) { - return Owner?.GetOnAnalyzerException_NoTelemetryLogging(project); + return Owner?.GetOnAnalyzerException_NoTelemetryLogging(projectId); } } } diff --git a/src/Features/Core/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/Core/Diagnostics/DiagnosticAnalyzerService.cs index 17d899b4744c8fa6baaafe5219e4a6f309dda40b..9fc579677139afdc519a4a82b60226e3d965d646 100644 --- a/src/Features/Core/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/Core/Diagnostics/DiagnosticAnalyzerService.cs @@ -170,22 +170,22 @@ public Task> GetSpecificDiagnosticsAsync(Solution } // virtual for testing purposes. - internal virtual Action GetOnAnalyzerException(Project project, DiagnosticLogAggregator diagnosticLogAggregator) + internal virtual Action GetOnAnalyzerException(ProjectId projectId, DiagnosticLogAggregator diagnosticLogAggregator) { return (ex, analyzer, diagnostic) => { // Log telemetry, if analyzer supports telemetry. DiagnosticAnalyzerLogger.LogAnalyzerCrashCount(analyzer, ex, diagnosticLogAggregator); - AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(ex, analyzer, diagnostic, _hostDiagnosticUpdateSource, project); + AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(ex, analyzer, diagnostic, _hostDiagnosticUpdateSource, projectId); }; } // virtual for testing purposes. - internal virtual Action GetOnAnalyzerException_NoTelemetryLogging(Project project) + internal virtual Action GetOnAnalyzerException_NoTelemetryLogging(ProjectId projectId) { return (ex, analyzer, diagnostic) => - AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(ex, analyzer, diagnostic, _hostDiagnosticUpdateSource, project); + AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(ex, analyzer, diagnostic, _hostDiagnosticUpdateSource, projectId); } } } diff --git a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs index 521cfc28d7bf0dcfec48ba1d0a38bc985f8070c0..fb5b168e7287a84bb48d54013f068f9b282543f9 100644 --- a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs +++ b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs @@ -64,8 +64,8 @@ internal class DiagnosticAnalyzerDriver _generatedCodeService = project.Solution.Workspace.Services.GetService(); _analyzerDriverService = project.LanguageServices.GetService(); _analyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Workspace); - _onAnalyzerException = owner.GetOnAnalyzerException(project); - _onAnalyzerException_NoTelemetryLogging = owner.GetOnAnalyzerException_NoTelemetryLogging(project); + _onAnalyzerException = owner.GetOnAnalyzerException(project.Id); + _onAnalyzerException_NoTelemetryLogging = owner.GetOnAnalyzerException_NoTelemetryLogging(project.Id); } public Document Document