public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, AnalyzerManager analyzerManager) { _owner = owner; _sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(analyzerManager); _projectAnalyzersAndStatesMap = new ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates>(); this.Workspace = workspace; }
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, AnalyzerManager analyzerManager) { _owner = owner; _correlationId = correlationId; _memberRangeMap = new MemberRangeMap(); _analyzersAndState = new DiagnosticAnalyzersAndStates(this, workspace, analyzerManager); _executor = new AnalyzerExecutor(this); _diagnosticLogAggregator = new DiagnosticLogAggregator(_owner); }
public IncrementalAnalyzerDelegatee(DiagnosticAnalyzerService owner, Workspace workspace, AnalyzerManager analyzerManager) { _workspace = workspace; _analyzerManager = analyzerManager; _owner = owner; var v1CorrelationId = LogAggregator.GetNextId(); _engineV1 = new EngineV1.DiagnosticIncrementalAnalyzer(_owner, v1CorrelationId, _workspace, _analyzerManager); var v2CorrelationId = LogAggregator.GetNextId(); _engineV2 = new EngineV2.DiagnosticIncrementalAnalyzer(_owner, v2CorrelationId, _workspace, _analyzerManager); }
/// <summary> /// Create an analyzer driver. /// </summary> /// <param name="analyzers">The set of analyzers to include in the analysis</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param> /// <param name="reportDiagnosticsWithSourceSuppression">Flag to indicate if diagnostics with source suppression, i.e. <see cref="Diagnostic.IsSuppressed"/>, should be reported.</param> protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, bool reportDiagnosticsWithSourceSuppression) { this.analyzers = analyzers; this.analyzerManager = analyzerManager; _reportDiagnosticsWithSourceSuppression = reportDiagnosticsWithSourceSuppression; }
/// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// </summary> internal static bool IsDiagnosticAnalyzerSuppressed( DiagnosticAnalyzer analyzer, CompilationOptions options, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { return analyzerManager.IsDiagnosticAnalyzerSuppressed(analyzer, options, IsCompilerAnalyzer, analyzerExecutor); }
/// <summary> /// Create an analyzer driver. /// </summary> /// <param name="analyzers">The set of analyzers to include in the analysis</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param> protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager) { this.analyzers = analyzers; this.analyzerManager = analyzerManager; }
/// <summary> /// Create an <see cref="AnalyzerDriver"/> and attach it to the given compilation. /// </summary> /// <param name="compilation">The compilation to which the new driver should be attached.</param> /// <param name="analyzers">The set of analyzers to include in the analysis.</param> /// <param name="options">Options that are passed to analyzers.</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param> /// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param> /// <param name="reportAnalyzer">Report additional information related to analyzers, such as analyzer execution time.</param> /// <param name="newCompilation">The new compilation with the analyzer driver attached.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param> /// <returns>A newly created analyzer driver</returns> /// <remarks> /// Note that since a compilation is immutable, the act of creating a driver and attaching it produces /// a new compilation. Any further actions on the compilation should use the new compilation. /// </remarks> public static AnalyzerDriver CreateAndAttachToCompilation( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Diagnostic> addExceptionDiagnostic, bool reportAnalyzer, out Compilation newCompilation, CancellationToken cancellationToken) { Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = (ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic); return CreateAndAttachToCompilation(compilation, analyzers, options, analyzerManager, onAnalyzerException, reportAnalyzer, out newCompilation, cancellationToken: cancellationToken); }
// internal for testing purposes. internal DiagnosticAnalyzerService(ImmutableArray<AnalyzerReference> workspaceAnalyzers) : this() { _analyzerManager = new AnalyzerManager(workspaceAnalyzers); }
private DiagnosticAnalyzerService(IEnumerable <string> workspaceAnalyzerAssemblies) : this() { _analyzerManager = new AnalyzerManager(workspaceAnalyzerAssemblies); }
public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, AnalyzerManager analyzerManager) { _owner = owner; _sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(analyzerManager); _projectAnalyzersAndStatesMap = new ConcurrentDictionary <ProjectId, ProjectAnalyzersAndStates>(); this.Workspace = workspace; }
/// <summary> /// Create an analyzer driver. /// </summary> /// <param name="analyzers">The set of analyzers to include in the analysis</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param> /// <param name="cancellationToken">a cancellation token that can be used to abort analysis</param> protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, CancellationToken cancellationToken) { _analyzers = analyzers; this.analyzerManager = analyzerManager; this.CompilationEventQueue = new AsyncQueue<CompilationEvent>(); this.DiagnosticQueue = new AsyncQueue<Diagnostic>(); _queueRegistration = cancellationToken.Register(() => { this.CompilationEventQueue.TryComplete(); this.DiagnosticQueue.TryComplete(); }); }
// internal for testing purposes internal static AnalyzerDriver Create( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, out Compilation newCompilation, CancellationToken cancellationToken) { options = options ?? AnalyzerOptions.Empty; AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager, cancellationToken); newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue); var addDiagnostic = GetDiagnosticSinkWithSuppression(analyzerDriver.DiagnosticQueue.Enqueue, newCompilation); Action<Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException; if (onAnalyzerException != null) { // Wrap onAnalyzerException to pass in filtered diagnostic. var comp = newCompilation; newOnAnalyzerException = (ex, analyzer, diagnostic) => 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()))); var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, singleThreadedAnalyzerToGateMap, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); return analyzerDriver; }
/// <summary> /// Create an <see cref="AnalyzerDriver"/> and attach it to the given compilation. /// </summary> /// <param name="compilation">The compilation to which the new driver should be attached.</param> /// <param name="analyzers">The set of analyzers to include in the analysis.</param> /// <param name="options">Options that are passed to analyzers.</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param> /// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param> /// <param name="newCompilation">The new compilation with the analyzer driver attached.</param> /// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param> /// <returns>A newly created analyzer driver</returns> /// <remarks> /// Note that since a compilation is immutable, the act of creating a driver and attaching it produces /// a new compilation. Any further actions on the compilation should use the new compilation. /// </remarks> public static AnalyzerDriver Create( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Diagnostic> addExceptionDiagnostic, out Compilation newCompilation, CancellationToken cancellationToken) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } if (analyzers.IsDefaultOrEmpty) { throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(analyzers)); } if (analyzers.Any(a => a == null)) { throw new ArgumentException(CodeAnalysisResources.ArgumentElementCannotBeNull, nameof(analyzers)); } Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = (ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic); return Create(compilation, analyzers, options, analyzerManager, onAnalyzerException, out newCompilation, cancellationToken: cancellationToken); }
internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager) { throw new NotImplementedException(); }
private static async Task<ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>> GetAnalyzerGateMapAsync( ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, SemaphoreSlim>(); foreach (var analyzer in analyzers) { var isConcurrent = await analyzerManager.IsConcurrentAnalyzerAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (!isConcurrent) { // Non-concurrent analyzers need their action callbacks from the analyzer driver to be guarded by a gate. var gate = new SemaphoreSlim(initialCount: 1); builder.Add(analyzer, gate); } } return builder.ToImmutable(); }
private DiagnosticAnalyzerService(IEnumerable<string> workspaceAnalyzerAssemblies) : this() { _analyzerManager = new AnalyzerManager(workspaceAnalyzerAssemblies); }
// internal for testing purposes. internal DiagnosticAnalyzerService(ImmutableArray <AnalyzerReference> workspaceAnalyzers) : this() { _analyzerManager = new AnalyzerManager(workspaceAnalyzers); }
internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, CancellationToken cancellationToken) { Debug.Assert(!Arguments.IsScriptRunner); cancellationToken.ThrowIfCancellationRequested(); if (Arguments.DisplayVersion) { PrintVersion(consoleOutput); return Succeeded; } if (Arguments.DisplayLogo) { PrintLogo(consoleOutput); } if (Arguments.DisplayHelp) { PrintHelp(consoleOutput); return Succeeded; } if (ReportErrors(Arguments.Errors, consoleOutput, errorLogger)) { return Failed; } var touchedFilesLogger = (Arguments.TouchedFilesPath != null) ? new TouchedFileLogger() : null; Compilation compilation = CreateCompilation(consoleOutput, touchedFilesLogger, errorLogger); if (compilation == null) { return Failed; } var diagnosticInfos = new List<DiagnosticInfo>(); ImmutableArray<DiagnosticAnalyzer> analyzers = ResolveAnalyzersFromArguments(diagnosticInfos, MessageProvider); var additionalTextFiles = ResolveAdditionalFilesFromArguments(diagnosticInfos, MessageProvider, touchedFilesLogger); if (ReportErrors(diagnosticInfos, consoleOutput, errorLogger)) { return Failed; } var diagnostics = new List<Diagnostic>(); ImmutableArray<EmbeddedText> embeddedTexts = AcquireEmbeddedTexts(compilation, diagnostics); if (ReportErrors(diagnostics, consoleOutput, errorLogger)) { return Failed; } bool reportAnalyzer = false; CancellationTokenSource analyzerCts = null; AnalyzerManager analyzerManager = null; AnalyzerDriver analyzerDriver = null; try { // Print the diagnostics produced during the parsing stage and exit if there were any errors. if (ReportErrors(compilation.GetParseDiagnostics(), consoleOutput, errorLogger)) { return Failed; } ConcurrentSet<Diagnostic> analyzerExceptionDiagnostics = null; if (!analyzers.IsEmpty) { analyzerCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); analyzerManager = new AnalyzerManager(); analyzerExceptionDiagnostics = new ConcurrentSet<Diagnostic>(); Action<Diagnostic> addExceptionDiagnostic = diagnostic => analyzerExceptionDiagnostics.Add(diagnostic); var analyzerOptions = new AnalyzerOptions(ImmutableArray<AdditionalText>.CastUp(additionalTextFiles)); analyzerDriver = AnalyzerDriver.CreateAndAttachToCompilation(compilation, analyzers, analyzerOptions, analyzerManager, addExceptionDiagnostic, Arguments.ReportAnalyzer, out compilation, analyzerCts.Token); reportAnalyzer = Arguments.ReportAnalyzer && !analyzers.IsEmpty; } if (ReportErrors(compilation.GetDeclarationDiagnostics(), consoleOutput, errorLogger)) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); string outputName = GetOutputFileName(compilation, cancellationToken); var finalPeFilePath = Path.Combine(Arguments.OutputDirectory, outputName); var finalPdbFilePath = Arguments.PdbPath ?? Path.ChangeExtension(finalPeFilePath, ".pdb"); var finalXmlFilePath = Arguments.DocumentationPath; var diagnosticBag = DiagnosticBag.GetInstance(); Stream sourceLinkStreamOpt = null; try { // NOTE: Unlike the PDB path, the XML doc path is not embedded in the assembly, so we don't need to pass it to emit. var emitOptions = Arguments.EmitOptions. WithOutputNameOverride(outputName). WithPdbFilePath(finalPdbFilePath); // The PDB path is emitted in it's entirety into the PE. This makes it impossible to have deterministic // builds that occur in different source directories. To enable this we shave all path information from // the PDB when specified by the user. // // This is a temporary work around to allow us to make progress with determinism. The following issue // tracks getting an official solution here. // // https://github.com/dotnet/roslyn/issues/9813 if (Arguments.ParseOptions.Features.ContainsKey("pdb-path-determinism") && !string.IsNullOrEmpty(emitOptions.PdbFilePath)) { emitOptions = emitOptions.WithPdbFilePath(Path.GetFileName(emitOptions.PdbFilePath)); } if (Arguments.SourceLink != null) { sourceLinkStreamOpt = OpenFile(Arguments.SourceLink, consoleOutput, FileMode.Open, FileAccess.Read, FileShare.Read); } var moduleBeingBuilt = compilation.CheckOptionsAndCreateModuleBuilder( diagnosticBag, Arguments.ManifestResources, emitOptions, debugEntryPoint: null, sourceLinkStream: sourceLinkStreamOpt, embeddedTexts: embeddedTexts, testData: null, cancellationToken: cancellationToken); if (moduleBeingBuilt != null) { bool success; try { success = compilation.CompileMethods( moduleBeingBuilt, Arguments.EmitPdb, diagnosticBag, filterOpt: null, cancellationToken: cancellationToken); if (success) { // NOTE: as native compiler does, we generate the documentation file // NOTE: 'in place', replacing the contents of the file if it exists Stream xmlStreamOpt = null; if (finalXmlFilePath != null) { xmlStreamOpt = OpenFile(finalXmlFilePath, consoleOutput, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete); if (xmlStreamOpt == null) { return Failed; } xmlStreamOpt.SetLength(0); } using (xmlStreamOpt) { IEnumerable<DiagnosticInfo> errors; using (var win32ResourceStreamOpt = GetWin32Resources(Arguments, compilation, out errors)) { if (ReportErrors(errors, consoleOutput, errorLogger)) { return Failed; } success = compilation.GenerateResourcesAndDocumentationComments( moduleBeingBuilt, xmlStreamOpt, win32ResourceStreamOpt, diagnosticBag, cancellationToken); } // only report unused usings if we have success. if (success) { compilation.ReportUnusedImports(null, diagnosticBag, cancellationToken); } } } compilation.CompleteTrees(null); if (analyzerDriver != null) { // GetDiagnosticsAsync is called after ReportUnusedImports // since that method calls EventQueue.TryComplete. Without // TryComplete, we may miss diagnostics. var hostDiagnostics = analyzerDriver.GetDiagnosticsAsync(compilation).Result; diagnosticBag.AddRange(hostDiagnostics); if (hostDiagnostics.Any(IsReportedError)) { success = false; } } } finally { moduleBeingBuilt.CompilationFinished(); } if (success) { bool emitPdbFile = Arguments.EmitPdb && emitOptions.DebugInformationFormat != Emit.DebugInformationFormat.Embedded; using (var peStreamProvider = new CompilerEmitStreamProvider(this, finalPeFilePath)) using (var pdbStreamProviderOpt = emitPdbFile ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null) { success = compilation.SerializeToPeStream( moduleBeingBuilt, peStreamProvider, pdbStreamProviderOpt, testSymWriterFactory: null, diagnostics: diagnosticBag, metadataOnly: emitOptions.EmitMetadataOnly, cancellationToken: cancellationToken); if (success && touchedFilesLogger != null) { if (pdbStreamProviderOpt != null) { touchedFilesLogger.AddWritten(finalPdbFilePath); } touchedFilesLogger.AddWritten(finalPeFilePath); } } } } var compileAndEmitDiagnostics = diagnosticBag.ToReadOnly(); if (ReportErrors(compileAndEmitDiagnostics, consoleOutput, errorLogger)) { return Failed; } } finally { diagnosticBag.Free(); sourceLinkStreamOpt?.Dispose(); } cancellationToken.ThrowIfCancellationRequested(); if (analyzerExceptionDiagnostics != null && ReportErrors(analyzerExceptionDiagnostics, consoleOutput, errorLogger)) { return Failed; } bool errorsReadingAdditionalFiles = false; foreach (var additionalFile in additionalTextFiles) { if (ReportErrors(additionalFile.Diagnostics, consoleOutput, errorLogger)) { errorsReadingAdditionalFiles = true; } } if (errorsReadingAdditionalFiles) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); if (Arguments.TouchedFilesPath != null) { Debug.Assert(touchedFilesLogger != null); if (finalXmlFilePath != null) { touchedFilesLogger.AddWritten(finalXmlFilePath); } var readStream = OpenFile(Arguments.TouchedFilesPath + ".read", consoleOutput, mode: FileMode.OpenOrCreate); if (readStream == null) { return Failed; } using (var writer = new StreamWriter(readStream)) { touchedFilesLogger.WriteReadPaths(writer); } var writtenStream = OpenFile(Arguments.TouchedFilesPath + ".write", consoleOutput, mode: FileMode.OpenOrCreate); if (writtenStream == null) { return Failed; } using (var writer = new StreamWriter(writtenStream)) { touchedFilesLogger.WriteWrittenPaths(writer); } } } finally { // At this point analyzers are already complete in which case this is a no-op. Or they are // still running because the compilation failed before all of the compilation events were // raised. In the latter case the driver, and all its associated state, will be waiting around // for events that are never coming. Cancel now and let the clean up process begin. if (analyzerCts != null) { analyzerCts.Cancel(); if (analyzerManager != null) { // Clear cached analyzer descriptors and unregister exception handlers hooked up to the LocalizableString fields of the associated descriptors. analyzerManager.ClearAnalyzerState(analyzers); } if (reportAnalyzer) { ReportAnalyzerExecutionTime(consoleOutput, analyzerDriver, Culture, compilation.Options.ConcurrentBuild); } } } return Succeeded; }
public WorkspaceAnalyzersAndStates(AnalyzerManager analyzerManager) { _analyzerManager = analyzerManager; _perLanguageAnalyzersAndStatesMap = ImmutableDictionary<string, PerLanguageAnalyzersAndStates>.Empty; }
private static ImmutableArray<DiagnosticAnalyzer> GetUnsuppressedAnalyzers( ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); foreach (var analyzer in analyzers) { if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor)) { builder.Add(analyzer); } } return builder.ToImmutable(); }
private static async Task<AnalyzerActions> GetAnalyzerActionsAsync( ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { var allAnalyzerActions = new AnalyzerActions(); foreach (var analyzer in analyzers) { Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor)); var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (analyzerActions != null) { allAnalyzerActions = allAnalyzerActions.Append(analyzerActions); } } return allAnalyzerActions; }
// 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; }
private static async Task<ImmutableDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>> GetGeneratedCodeAnalysisFlagsAsync( ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>(); foreach (var analyzer in analyzers) { Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor)); var generatedCodeAnalysisFlags = await analyzerManager.GetGeneratedCodeAnalysisFlagsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); builder.Add(analyzer, generatedCodeAnalysisFlags); } return builder.ToImmutable(); }
private static Task<AnalyzerActions> GetAnalyzerActionsAsync( ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, AnalyzerExecutor analyzerExecutor) { return Task.Run(async () => { AnalyzerActions allAnalyzerActions = new AnalyzerActions(); foreach (var analyzer in analyzers) { if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor)) { var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false); if (analyzerActions != null) { allAnalyzerActions = allAnalyzerActions.Append(analyzerActions); } } } return allAnalyzerActions; }, analyzerExecutor.CancellationToken); }
/// <summary> /// Create an analyzer driver. /// </summary> /// <param name="analyzers">The set of analyzers to include in the analysis</param> /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param> /// <param name="isComment">Delegate to identify if the given trivia is a comment.</param> protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, Func<SyntaxTrivia, bool> isComment) { this.analyzers = analyzers; this.analyzerManager = analyzerManager; _isGeneratedCode = (tree, ct) => GeneratedCodeUtilities.IsGeneratedCode(tree, isComment, ct); }
private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, CancellationToken cancellationToken) { Debug.Assert(!Arguments.IsScriptRunner); cancellationToken.ThrowIfCancellationRequested(); if (Arguments.DisplayLogo) { PrintLogo(consoleOutput); } if (Arguments.DisplayHelp) { PrintHelp(consoleOutput); return Succeeded; } if (ReportErrors(Arguments.Errors, consoleOutput, errorLogger)) { return Failed; } var touchedFilesLogger = (Arguments.TouchedFilesPath != null) ? new TouchedFileLogger() : null; Compilation compilation = CreateCompilation(consoleOutput, touchedFilesLogger, errorLogger); if (compilation == null) { return Failed; } var diagnostics = new List<DiagnosticInfo>(); var analyzers = ResolveAnalyzersFromArguments(diagnostics, MessageProvider, touchedFilesLogger); var additionalTextFiles = ResolveAdditionalFilesFromArguments(diagnostics, MessageProvider, touchedFilesLogger); if (ReportErrors(diagnostics, consoleOutput, errorLogger)) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); CancellationTokenSource analyzerCts = null; AnalyzerManager analyzerManager = null; AnalyzerDriver analyzerDriver = null; try { Func<ImmutableArray<Diagnostic>> getAnalyzerDiagnostics = null; ConcurrentSet<Diagnostic> analyzerExceptionDiagnostics = null; if (!analyzers.IsDefaultOrEmpty) { analyzerCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); analyzerManager = new AnalyzerManager(); analyzerExceptionDiagnostics = new ConcurrentSet<Diagnostic>(); Action<Diagnostic> addExceptionDiagnostic = diagnostic => analyzerExceptionDiagnostics.Add(diagnostic); var analyzerOptions = new AnalyzerOptions(ImmutableArray<AdditionalText>.CastUp(additionalTextFiles)); analyzerDriver = AnalyzerDriver.CreateAndAttachToCompilation(compilation, analyzers, analyzerOptions, analyzerManager, addExceptionDiagnostic, Arguments.ReportAnalyzer, out compilation, analyzerCts.Token); getAnalyzerDiagnostics = () => analyzerDriver.GetDiagnosticsAsync(compilation).Result; } // Print the diagnostics produced during the parsing stage and exit if there were any errors. if (ReportErrors(compilation.GetParseDiagnostics(), consoleOutput, errorLogger)) { return Failed; } if (ReportErrors(compilation.GetDeclarationDiagnostics(), consoleOutput, errorLogger)) { return Failed; } EmitResult emitResult; // NOTE: as native compiler does, we generate the documentation file // NOTE: 'in place', replacing the contents of the file if it exists string finalPeFilePath; string finalPdbFilePath; string finalXmlFilePath; Stream xmlStreamOpt = null; cancellationToken.ThrowIfCancellationRequested(); finalXmlFilePath = Arguments.DocumentationPath; if (finalXmlFilePath != null) { xmlStreamOpt = OpenFile(finalXmlFilePath, consoleOutput, PortableShim.FileMode.OpenOrCreate, PortableShim.FileAccess.Write, PortableShim.FileShare.ReadWriteBitwiseOrDelete); if (xmlStreamOpt == null) { return Failed; } xmlStreamOpt.SetLength(0); } cancellationToken.ThrowIfCancellationRequested(); IEnumerable<DiagnosticInfo> errors; using (var win32ResourceStreamOpt = GetWin32Resources(Arguments, compilation, out errors)) using (xmlStreamOpt) { if (ReportErrors(errors, consoleOutput, errorLogger)) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); string outputName = GetOutputFileName(compilation, cancellationToken); finalPeFilePath = Path.Combine(Arguments.OutputDirectory, outputName); finalPdbFilePath = Arguments.PdbPath ?? Path.ChangeExtension(finalPeFilePath, ".pdb"); // NOTE: Unlike the PDB path, the XML doc path is not embedded in the assembly, so we don't need to pass it to emit. var emitOptions = Arguments.EmitOptions. WithOutputNameOverride(outputName). WithPdbFilePath(finalPdbFilePath); using (var peStreamProvider = new CompilerEmitStreamProvider(this, finalPeFilePath)) using (var pdbStreamProviderOpt = Arguments.EmitPdb ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null) { emitResult = compilation.Emit( peStreamProvider, pdbStreamProviderOpt, (xmlStreamOpt != null) ? new Compilation.SimpleEmitStreamProvider(xmlStreamOpt) : null, (win32ResourceStreamOpt != null) ? new Compilation.SimpleEmitStreamProvider(win32ResourceStreamOpt) : null, Arguments.ManifestResources, emitOptions, debugEntryPoint: null, getHostDiagnostics: getAnalyzerDiagnostics, cancellationToken: cancellationToken); if (emitResult.Success && touchedFilesLogger != null) { if (pdbStreamProviderOpt != null) { touchedFilesLogger.AddWritten(finalPdbFilePath); } touchedFilesLogger.AddWritten(finalPeFilePath); } } } GenerateSqmData(Arguments.CompilationOptions, emitResult.Diagnostics); if (ReportErrors(emitResult.Diagnostics, consoleOutput, errorLogger)) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); if (analyzerExceptionDiagnostics != null && ReportErrors(analyzerExceptionDiagnostics, consoleOutput, errorLogger)) { return Failed; } bool errorsReadingAdditionalFiles = false; foreach (var additionalFile in additionalTextFiles) { if (ReportErrors(additionalFile.Diagnostics, consoleOutput, errorLogger)) { errorsReadingAdditionalFiles = true; } } if (errorsReadingAdditionalFiles) { return Failed; } cancellationToken.ThrowIfCancellationRequested(); if (Arguments.TouchedFilesPath != null) { Debug.Assert(touchedFilesLogger != null); if (finalXmlFilePath != null) { touchedFilesLogger.AddWritten(finalXmlFilePath); } var readStream = OpenFile(Arguments.TouchedFilesPath + ".read", consoleOutput, mode: PortableShim.FileMode.OpenOrCreate); if (readStream == null) { return Failed; } using (var writer = new StreamWriter(readStream)) { touchedFilesLogger.WriteReadPaths(writer); } var writtenStream = OpenFile(Arguments.TouchedFilesPath + ".write", consoleOutput, mode: PortableShim.FileMode.OpenOrCreate); if (writtenStream == null) { return Failed; } using (var writer = new StreamWriter(writtenStream)) { touchedFilesLogger.WriteWrittenPaths(writer); } } } finally { // At this point analyzers are already complete in which case this is a no-op. Or they are // still running because the compilation failed before all of the compilation events were // raised. In the latter case the driver, and all its associated state, will be waiting around // for events that are never coming. Cancel now and let the clean up process begin. if (analyzerCts != null) { analyzerCts.Cancel(); if (analyzerManager != null) { // Clear cached analyzer descriptors and unregister exception handlers hooked up to the LocalizableString fields of the associated descriptors. analyzerManager.ClearAnalyzerState(analyzers); } if (Arguments.ReportAnalyzer && analyzerDriver != null && compilation != null) { ReportAnalyzerExecutionTime(consoleOutput, analyzerDriver, Culture, compilation.Options.ConcurrentBuild); } } } return Succeeded; }
// internal for testing purposes internal static AnalyzerDriver Create( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, bool reportAnalyzer, out Compilation newCompilation, CancellationToken cancellationToken) { options = options ?? AnalyzerOptions.Empty; AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager, cancellationToken); newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue); var addDiagnostic = GetDiagnosticSinkWithSuppression(analyzerDriver.DiagnosticQueue.Enqueue, newCompilation); Action<Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException; if (onAnalyzerException != null) { // Wrap onAnalyzerException to pass in filtered diagnostic. var comp = newCompilation; newOnAnalyzerException = (ex, analyzer, diagnostic) => 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 (reportAnalyzer) { // 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 = newCompilation.GetTypeByMetadataName("System.Object"); } Func<DiagnosticAnalyzer, object> getAnalyzerGate = analyzer => singleThreadedAnalyzerToGateMap[analyzer]; var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, getAnalyzerGate, reportAnalyzer, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); return analyzerDriver; }