internal static async Task<ImmutableArray<Diagnostic>> GetDiagnostics (Project project, List<DiagnosticAnalyzer> providers, CancellationToken token) { var analyzers = ImmutableArray<DiagnosticAnalyzer>.Empty.AddRange (providers); try { var compilation = await project.GetCompilationAsync (token).ConfigureAwait (false); CompilationWithAnalyzers compilationWithAnalyzer; var options = new CompilationWithAnalyzersOptions ( null, delegate (Exception exception, DiagnosticAnalyzer analyzer, Diagnostic diag) { LoggingService.LogError ("Exception in diagnostic analyzer " + diag.Id + ":" + diag.GetMessage (), exception); }, true, false ); compilationWithAnalyzer = compilation.WithAnalyzers (analyzers, options); if (token.IsCancellationRequested) return ImmutableArray<Diagnostic>.Empty; return await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync ().ConfigureAwait (false); } catch (Exception) { return ImmutableArray<Diagnostic>.Empty; } finally { CompilationWithAnalyzers.ClearAnalyzerState (analyzers); } }
/// <summary> /// Returns a new compilation with attached diagnostic analyzers. /// </summary> /// <param name="compilation">Compilation to which analyzers are to be added.</param> /// <param name="analyzers">The set of analyzers to include in future analyses.</param> /// <param name="analysisOptions">Options to configure analyzer execution within <see cref="CompilationWithAnalyzers"/>.</param> public static CompilationWithAnalyzers WithAnalyzers( this Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions ) { return(new CompilationWithAnalyzers(compilation, analyzers, analysisOptions)); }
public async Task<DiagnosticAnalysisResultMap<string, DiagnosticAnalysisResultBuilder>> GetDiagnosticsAsync( IEnumerable<AnalyzerReference> hostAnalyzers, IEnumerable<string> analyzerIds, bool reportSuppressedDiagnostics, bool logAnalyzerExecutionTime, CancellationToken cancellationToken) { var analyzerMap = CreateAnalyzerMap(hostAnalyzers, _project); var analyzers = GetAnalyzers(analyzerMap, analyzerIds); if (analyzers.Length == 0) { return DiagnosticAnalysisResultMap.Create(ImmutableDictionary<string, DiagnosticAnalysisResultBuilder>.Empty, ImmutableDictionary<string, AnalyzerTelemetryInfo>.Empty); } var compilation = await _project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); // TODO: can we support analyzerExceptionFilter in remote host? // right now, host doesn't support watson, we might try to use new NonFatal watson API? var analyzerOptions = new CompilationWithAnalyzersOptions( options: _project.AnalyzerOptions, onAnalyzerException: OnAnalyzerException, analyzerExceptionFilter: null, concurrentAnalysis: true, logAnalyzerExecutionTime: logAnalyzerExecutionTime, reportSuppressedDiagnostics: reportSuppressedDiagnostics); var analyzerDriver = compilation.WithAnalyzers(analyzers, analyzerOptions); // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); // REVIEW: the design of current analyzer engine is that, information/states in CompilationWithAnalyzer (more specifically AnalyzerManager singleton) // will live forever until analyzer references (analyzers), which is given to CompilationWithAnalyzer, go away. // that is not suitable for OOP since OOP will create new workspace // for each analysis but share all resources including analyzer references. // until, we address this issue, OOP will clear state every time analysis is done. // // * NOTE * this only works for now since we don't run analysis on multiple threads. // // best way to fix this is doing this - https://github.com/dotnet/roslyn/issues/2830 // host should control lifetime of all information related to analyzer reference explicitly CompilationWithAnalyzers.ClearAnalyzerState(analyzers); var builderMap = analysisResult.ToResultBuilderMap(_project, VersionStamp.Default, compilation, analysisResult.Analyzers, cancellationToken); return DiagnosticAnalysisResultMap.Create(builderMap.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), analysisResult.AnalyzerTelemetryInfo.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), _exceptions.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value.ToImmutableArray())); }
private CompilationWithAnalyzers(Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions, CancellationToken cancellationToken) { VerifyArguments(compilation, analyzers, analysisOptions); _compilation = compilation.WithEventQueue(new AsyncQueue <CompilationEvent>()); _analyzers = analyzers; _analysisOptions = analysisOptions; _cancellationToken = cancellationToken; _analysisState = new AnalysisState(analyzers); _analysisResult = new AnalysisResult(analysisOptions.LogAnalyzerExecutionTime, analyzers); _driverPool = new ObjectPool <AnalyzerDriver>(() => compilation.AnalyzerForLanguage(analyzers, AnalyzerManager.Instance)); _executingConcurrentTreeTasksOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary <SyntaxTree, Tuple <Task, CancellationTokenSource> >() : null; _concurrentTreeTaskTokensOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary <Task, int>() : null; _executingCompilationOrNonConcurrentTreeTask = null; }
// internal for testing purposes internal static AnalyzerDriver CreateAndAttachToCompilation( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, bool reportAnalyzer, out Compilation newCompilation, CancellationToken cancellationToken) { AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager); newCompilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>()); var categorizeDiagnostics = false; var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer); analyzerDriver.Initialize(newCompilation, analysisOptions, categorizeDiagnostics, cancellationToken); var analysisScope = new AnalysisScope(newCompilation, analyzers, concurrentAnalysis: newCompilation.Options.ConcurrentBuild, categorizeDiagnostics: categorizeDiagnostics); analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue, analysisScope, cancellationToken: cancellationToken); return analyzerDriver; }
internal void Initialize( Compilation compilation, CompilationWithAnalyzersOptions analysisOptions, bool categorizeDiagnostics, CancellationToken cancellationToken) { Debug.Assert(_initializeTask == null); var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics); var addDiagnostic = GetDiagnosticSinkWithSuppression(diagnosticQueue.Enqueue, compilation); var addLocalDiagnosticOpt = categorizeDiagnostics ? GetDiagnosticSinkWithSuppression(diagnosticQueue.EnqueueLocal, compilation) : null; var addNonLocalDiagnosticOpt = categorizeDiagnostics ? GetDiagnosticSinkWithSuppression(diagnosticQueue.EnqueueNonLocal, compilation) : null; Action<Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException; if (analysisOptions.OnAnalyzerException != null) { // Wrap onAnalyzerException to pass in filtered diagnostic. var comp = compilation; newOnAnalyzerException = (ex, analyzer, diagnostic) => analysisOptions.OnAnalyzerException(ex, analyzer, GetFilteredDiagnostic(diagnostic, comp)); } else { // Add exception diagnostic to regular diagnostic bag. newOnAnalyzerException = (ex, analyzer, diagnostic) => addDiagnostic(diagnostic); } // Assume all analyzers are non-thread safe. var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new object()))); if (analysisOptions.LogAnalyzerExecutionTime) { // If we are reporting detailed analyzer performance numbers, then do a dummy invocation of Compilation.GetTypeByMetadataName API upfront. // This API seems to cause a severe hit for the first analyzer invoking it and hence introduces lot of noise in the computed analyzer execution times. var unused = compilation.GetTypeByMetadataName("System.Object"); } Func<DiagnosticAnalyzer, object> getAnalyzerGate = analyzer => singleThreadedAnalyzerToGateMap[analyzer]; var analyzerExecutor = AnalyzerExecutor.Create(compilation, analysisOptions.AnalyzerOptions ?? AnalyzerOptions.Empty, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, getAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addLocalDiagnosticOpt, addNonLocalDiagnosticOpt, cancellationToken); Initialize(analyzerExecutor, diagnosticQueue, cancellationToken); }
private static void VerifyArguments(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } if (analysisOptions == null) { throw new ArgumentNullException(nameof(analysisOptions)); } VerifyAnalyzersArgumentForStaticApis(analyzers); }
private CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions, CancellationToken cancellationToken) { VerifyArguments(compilation, analyzers, analysisOptions); _compilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>()); _analyzers = analyzers; _analysisOptions = analysisOptions; _cancellationToken = cancellationToken; _analysisState = new AnalysisState(analyzers); _analysisResult = new AnalysisResult(analysisOptions.LogAnalyzerExecutionTime, analyzers); _driverPool = new ObjectPool<AnalyzerDriver>(() => compilation.AnalyzerForLanguage(analyzers, AnalyzerManager.Instance)); _executingConcurrentTreeTasksOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary<SyntaxTree, Tuple<Task, CancellationTokenSource>>() : null; _concurrentTreeTaskTokensOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary<Task, int>() : null; _executingCompilationOrNonConcurrentTreeTask = null; }
/// <summary> /// Creates a new compilation by attaching diagnostic analyzers to an existing compilation. /// </summary> /// <param name="compilation">The original compilation.</param> /// <param name="analyzers">The set of analyzers to include in future analyses.</param> /// <param name="analysisOptions">Options to configure analyzer execution.</param> public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions) : this(compilation, analyzers, analysisOptions, cancellationToken: CancellationToken.None) { }
/// <summary> /// Returns a new compilation with attached diagnostic analyzers. /// </summary> /// <param name="compilation">Compilation to which analyzers are to be added.</param> /// <param name="analyzers">The set of analyzers to include in future analyses.</param> /// <param name="analysisOptions">Options to configure analyzer execution within <see cref="CompilationWithAnalyzers"/>.</param> public static CompilationWithAnalyzers WithAnalyzers(this Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions) { return new CompilationWithAnalyzers(compilation, analyzers, analysisOptions); }
private static void VerifyArguments(Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } if (analysisOptions == null) { throw new ArgumentNullException(nameof(analysisOptions)); } VerifyAnalyzersArgumentForStaticApis(analyzers); }
/// <summary> /// Creates a new compilation by attaching diagnostic analyzers to an existing compilation. /// </summary> /// <param name="compilation">The original compilation.</param> /// <param name="analyzers">The set of analyzers to include in future analyses.</param> /// <param name="analysisOptions">Options to configure analyzer execution.</param> public CompilationWithAnalyzers(Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions) : this(compilation, analyzers, analysisOptions, cancellationToken : CancellationToken.None) { }
public static async Task<IEnumerable<Result>> Check (AnalysisDocument analysisDocument, CancellationToken cancellationToken) { var input = analysisDocument.DocumentContext; if (!AnalysisOptions.EnableFancyFeatures || input.Project == null || !input.IsCompileableInProject || input.AnalysisDocument == null) return Enumerable.Empty<Result> (); try { var model = await analysisDocument.DocumentContext.AnalysisDocument.GetSemanticModelAsync (cancellationToken); if (model == null) return Enumerable.Empty<Result> (); var compilation = model.Compilation; var language = CodeRefactoringService.MimeTypeToLanguage (analysisDocument.Editor.MimeType); var providers = new List<DiagnosticAnalyzer> (); var alreadyAdded = new HashSet<Type>(); if (diagnostics == null) { diagnostics = await CodeRefactoringService.GetCodeDiagnosticsAsync (analysisDocument.DocumentContext, language, cancellationToken); } var diagnosticTable = new Dictionary<string, CodeDiagnosticDescriptor> (); foreach (var diagnostic in diagnostics) { if (alreadyAdded.Contains (diagnostic.DiagnosticAnalyzerType)) continue; if (!diagnostic.IsEnabled) continue; alreadyAdded.Add (diagnostic.DiagnosticAnalyzerType); var provider = diagnostic.GetProvider (); if (provider == null) continue; foreach (var diag in provider.SupportedDiagnostics) diagnosticTable [diag.Id] = diagnostic; providers.Add (provider); } if (providers.Count == 0 || cancellationToken.IsCancellationRequested) return Enumerable.Empty<Result> (); #if DEBUG Debug.Listeners.Add (consoleTraceListener); #endif CompilationWithAnalyzers compilationWithAnalyzer; var analyzers = System.Collections.Immutable.ImmutableArray<DiagnosticAnalyzer>.Empty.AddRange (providers); var diagnosticList = new List<Diagnostic> (); try { var options = new CompilationWithAnalyzersOptions ( null, delegate (Exception exception, DiagnosticAnalyzer analyzer, Diagnostic diag) { LoggingService.LogError ("Exception in diagnostic analyzer " + diag.Id + ":" + diag.GetMessage (), exception); }, null, false, false ); compilationWithAnalyzer = compilation.WithAnalyzers (analyzers, options); if (input.ParsedDocument == null || cancellationToken.IsCancellationRequested) return Enumerable.Empty<Result> (); diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSemanticDiagnosticsAsync (model, null, cancellationToken).ConfigureAwait (false)); diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSyntaxDiagnosticsAsync (model.SyntaxTree, cancellationToken).ConfigureAwait (false)); } catch (Exception) { return Enumerable.Empty<Result> (); } finally { #if DEBUG Debug.Listeners.Remove (consoleTraceListener); #endif CompilationWithAnalyzers.ClearAnalyzerState (analyzers); } return diagnosticList .Where (d => !d.Id.StartsWith("CS", StringComparison.Ordinal)) .Where (d => diagnosticTable[d.Id].GetIsEnabled (d.Descriptor)) .Select (diagnostic => { var res = new DiagnosticResult(diagnostic); // var line = analysisDocument.Editor.GetLineByOffset (res.Region.Start); // Console.WriteLine (diagnostic.Id + "/" + res.Region +"/" + analysisDocument.Editor.GetTextAt (line)); return res; }); } catch (OperationCanceledException) { return Enumerable.Empty<Result> (); } catch (AggregateException ae) { ae.Flatten ().Handle (ix => ix is OperationCanceledException); return Enumerable.Empty<Result> (); } catch (Exception e) { LoggingService.LogError ("Error while running diagnostics.", e); return Enumerable.Empty<Result> (); } }
internal void Initialize( Compilation compilation, CompilationWithAnalyzersOptions analysisOptions, CompilationData compilationData, bool categorizeDiagnostics, CancellationToken cancellationToken) { Debug.Assert(_initializeTask == null); var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics); Action<Diagnostic> addNotCategorizedDiagnosticOpt = null; Action<Diagnostic, DiagnosticAnalyzer, bool> addCategorizedLocalDiagnosticOpt = null; Action<Diagnostic, DiagnosticAnalyzer> addCategorizedNonLocalDiagnosticOpt = null; if (categorizeDiagnostics) { addCategorizedLocalDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation); addCategorizedNonLocalDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation); } else { addNotCategorizedDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation); } // Wrap onAnalyzerException to pass in filtered diagnostic. Action<Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException = (ex, analyzer, diagnostic) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation); if (filteredDiagnostic != null) { if (analysisOptions.OnAnalyzerException != null) { analysisOptions.OnAnalyzerException(ex, analyzer, filteredDiagnostic); } else if (categorizeDiagnostics) { addCategorizedNonLocalDiagnosticOpt(filteredDiagnostic, analyzer); } else { addNotCategorizedDiagnosticOpt(filteredDiagnostic); } } }; if (analysisOptions.LogAnalyzerExecutionTime) { // If we are reporting detailed analyzer performance numbers, then do a dummy invocation of Compilation.GetTypeByMetadataName API upfront. // This API seems to cause a severe hit for the first analyzer invoking it and hence introduces lot of noise in the computed analyzer execution times. var unused = compilation.GetTypeByMetadataName("System.Object"); } var analyzerExecutor = AnalyzerExecutor.Create( compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnosticOpt, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, IsCompilerAnalyzer, analyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, GetAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken); Initialize(analyzerExecutor, diagnosticQueue, compilationData, cancellationToken); }