From 7ae5d3599e7d362c0fe10f3d3c855a0ac2daea94 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Fri, 6 May 2016 18:08:51 -0700 Subject: [PATCH] expose a way to enable compiler diagnostics to custom workspace --- .../TodoCommentIncrementalAnalyzerProvider.cs | 6 +- .../DefaultDiagnosticAnalyzerService.cs} | 115 ++++++++++-------- ...sticAnalyzerService_IncrementalAnalyzer.cs | 4 +- src/Features/Core/Portable/Features.csproj | 6 +- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 2 +- ...ntaxTreeInfoIncrementalAnalyzerProvider.cs | 2 +- .../SemanticChangeNotificationService.cs | 40 +++--- .../SolutionCrawlerRegistrationService.cs | 104 +++++++++++++++- ...oordinator.IncrementalAnalyzerProcessor.cs | 8 +- ...lationErrorTelemetryIncrementalAnalyzer.cs | 4 +- ...nerAttributeIncrementalAnalyzerProvider.cs | 2 +- .../MiscellaneousFilesWorkspace.cs | 31 +---- .../SolutionSize/SolutionSizeTracker.cs | 2 +- .../Core/Def/ServicesVisualStudio.csproj | 1 - .../CodeModel/CodeModelIncrementalAnalyzer.cs | 2 +- ... => DefaultDiagnosticUpdateSourceTests.vb} | 8 +- .../Core/Test/ServicesVisualStudioTest.vbproj | 2 +- .../Diagnostics/DiagnosticProvider.cs | 49 ++++++++ .../InternalRuntimeDiagnosticOptions.cs | 14 +++ .../Core/Portable/PublicAPI.Unshipped.txt | 8 +- ...ortIncrementalAnalyzerProviderAttribute.cs | 12 +- .../SolutionCrawler/IIncrementalAnalyzer.cs | 0 .../IIncrementalAnalyzerProvider.cs | 0 .../ISolutionCrawlerRegistrationService.cs | 0 .../IncrementalAnalyzerProviderMetadata.cs | 2 + ...NullSolutionCrawlerRegisterationService.cs | 25 ++++ .../WellKnownSolutionCrawlerAnalyzers.cs | 9 ++ .../Core/Portable/Workspaces.csproj | 11 +- 28 files changed, 337 insertions(+), 132 deletions(-) rename src/{VisualStudio/Core/Def/Implementation/Diagnostics/MiscellaneousDiagnosticAnalyzerService.cs => Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs} (58%) rename src/VisualStudio/Core/Test/Diagnostics/{MiscDiagnosticUpdateSourceTests.vb => DefaultDiagnosticUpdateSourceTests.vb} (94%) create mode 100644 src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs create mode 100644 src/Workspaces/Core/Portable/Diagnostics/InternalRuntimeDiagnosticOptions.cs rename src/{Features/Core/Portable/SolutionCrawler/Extensibility => Workspaces/Core/Portable/SolutionCrawler}/ExportIncrementalAnalyzerProviderAttribute.cs (71%) rename src/{Features => Workspaces}/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs (100%) rename src/{Features/Core/Portable/SolutionCrawler/Extensibility => Workspaces/Core/Portable/SolutionCrawler}/IIncrementalAnalyzerProvider.cs (100%) rename src/{Features => Workspaces}/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs (100%) rename src/{Features/Core/Portable/SolutionCrawler/Extensibility => Workspaces/Core/Portable/SolutionCrawler}/IncrementalAnalyzerProviderMetadata.cs (88%) create mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs create mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs diff --git a/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzerProvider.cs b/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzerProvider.cs index c0699f5ec0d..70e992251c4 100644 --- a/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzerProvider.cs @@ -12,9 +12,11 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.TodoComments { - [Export(typeof(ITodoListProvider))] [Shared] - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles)] + [Export(typeof(ITodoListProvider))] + [ExportIncrementalAnalyzerProvider( + name: nameof(TodoCommentIncrementalAnalyzerProvider), + workspaceKinds: new[] { WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles })] internal class TodoCommentIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider, ITodoListProvider { private static readonly ConditionalWeakTable s_analyzers = new ConditionalWeakTable(); diff --git a/src/VisualStudio/Core/Def/Implementation/Diagnostics/MiscellaneousDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs similarity index 58% rename from src/VisualStudio/Core/Def/Implementation/Diagnostics/MiscellaneousDiagnosticAnalyzerService.cs rename to src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs index b8811b5938f..9ac94c4ba80 100644 --- a/src/VisualStudio/Core/Def/Implementation/Diagnostics/MiscellaneousDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs @@ -9,24 +9,26 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics { - [ExportIncrementalAnalyzerProvider(WorkspaceKind.MiscellaneousFiles)] [Shared] - internal partial class MiscellaneousDiagnosticAnalyzerService : IIncrementalAnalyzerProvider, IDiagnosticUpdateSource + [ExportIncrementalAnalyzerProvider(WellKnownSolutionCrawlerAnalyzers.Diagnostic, workspaceKinds: null)] + internal partial class DefaultDiagnosticAnalyzerService : IIncrementalAnalyzerProvider, IDiagnosticUpdateSource { + private const int Syntax = 1; + private const int Semantic = 2; + private readonly IDiagnosticAnalyzerService _analyzerService; [ImportingConstructor] - public MiscellaneousDiagnosticAnalyzerService(IDiagnosticAnalyzerService analyzerService, IDiagnosticUpdateSourceRegistrationService registrationService) + public DefaultDiagnosticAnalyzerService( + IDiagnosticAnalyzerService analyzerService, IDiagnosticUpdateSourceRegistrationService registrationService) { _analyzerService = analyzerService; - registrationService.Register(this); } @@ -37,7 +39,7 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) return null; } - return new SyntaxOnlyDiagnosticAnalyzer(this, workspace); + return new CompilerDiagnosticAnalyzer(this, workspace); } public event EventHandler DiagnosticsUpdated; @@ -62,21 +64,33 @@ internal void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs state) this.DiagnosticsUpdated?.Invoke(this, state); } - private class SyntaxOnlyDiagnosticAnalyzer : IIncrementalAnalyzer + private class CompilerDiagnosticAnalyzer : IIncrementalAnalyzer { - private readonly MiscellaneousDiagnosticAnalyzerService _service; + private readonly DefaultDiagnosticAnalyzerService _service; private readonly Workspace _workspace; - public SyntaxOnlyDiagnosticAnalyzer(MiscellaneousDiagnosticAnalyzerService service, Workspace workspace) + public CompilerDiagnosticAnalyzer(DefaultDiagnosticAnalyzerService service, Workspace workspace) { _service = service; _workspace = workspace; } + public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) + { + if (e.Option == InternalRuntimeDiagnosticOptions.Syntax || + e.Option == InternalRuntimeDiagnosticOptions.Semantic) + { + return true; + } + + return false; + } + public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken) { - // if closed file diagnostic is off and document is not opened, then don't do anything - if (!CheckOptions(document)) + // right now, there is no way to observe diagnostics for closed file. + if (!_workspace.IsDocumentOpen(document.Id) || + !_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Syntax)) { return; } @@ -87,25 +101,45 @@ public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancel Contract.Requires(document.Project.Solution.Workspace == _workspace); var diagnosticData = diagnostics == null ? ImmutableArray.Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); + + _service.RaiseDiagnosticsUpdated( + DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Syntax, document.Id), + _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); + } + + public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) + { + // right now, there is no way to observe diagnostics for closed file. + if (!_workspace.IsDocumentOpen(document.Id) || + !_workspace.Options.GetOption(InternalRuntimeDiagnosticOptions.Semantic)) + { + return; + } + + var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var diagnostics = model.GetMethodBodyDiagnostics(span: null, cancellationToken: cancellationToken).Concat( + model.GetDeclarationDiagnostics(span: null, cancellationToken: cancellationToken)); + + Contract.Requires(document.Project.Solution.Workspace == _workspace); + + var diagnosticData = diagnostics == null ? ImmutableArray.Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty(); + _service.RaiseDiagnosticsUpdated( - DiagnosticsUpdatedArgs.DiagnosticsCreated(new MiscUpdateArgsId(document.Id), + DiagnosticsUpdatedArgs.DiagnosticsCreated(new DefaultUpdateArgsId(_workspace.Kind, Semantic, document.Id), _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData)); } public void RemoveDocument(DocumentId documentId) { // a file is removed from misc project - RaiseEmptyDiagnosticUpdated(documentId); + RaiseEmptyDiagnosticUpdated(Syntax, documentId); + RaiseEmptyDiagnosticUpdated(Semantic, documentId); } public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) { // no closed file diagnostic and file is not opened, remove any existing diagnostics - if (!CheckOptions(document)) - { - RaiseEmptyDiagnosticUpdated(document.Id); - } - + RemoveDocument(document.Id); return SpecializedTasks.EmptyTask; } @@ -114,16 +148,10 @@ public Task DocumentCloseAsync(Document document, CancellationToken cancellation return DocumentResetAsync(document, cancellationToken); } - private void RaiseEmptyDiagnosticUpdated(DocumentId documentId) + private void RaiseEmptyDiagnosticUpdated(int kind, DocumentId documentId) { _service.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs.DiagnosticsRemoved( - ValueTuple.Create(this, documentId), _workspace, null, documentId.ProjectId, documentId)); - } - - // method we don't care. misc project only supports syntax errors - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; + new DefaultUpdateArgsId(_workspace.Kind, kind, documentId), _workspace, null, documentId.ProjectId, documentId)); } public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken) @@ -136,11 +164,6 @@ public Task DocumentOpenAsync(Document document, CancellationToken cancellationT return SpecializedTasks.EmptyTask; } - public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) - { - return false; - } - public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; @@ -150,45 +173,31 @@ public void RemoveProject(ProjectId projectId) { } - private bool CheckOptions(Document document) + private class DefaultUpdateArgsId : BuildToolId.Base, ISupportLiveUpdate { - if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(_workspace, document.Project.Language) && - _workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis)) - { - return true; - } - - return document.IsOpen(); - } + private readonly string _workspaceKind; - private class MiscUpdateArgsId : BuildToolId.Base, ISupportLiveUpdate - { - public MiscUpdateArgsId(DocumentId documentId) : base(documentId) + public DefaultUpdateArgsId(string workspaceKind, int type, DocumentId documentId) : base(type, documentId) { + _workspaceKind = workspaceKind; } - public override string BuildTool - { - get - { - return PredefinedBuildTools.Live; - } - } + public override string BuildTool => PredefinedBuildTools.Live; public override bool Equals(object obj) { - var other = obj as MiscUpdateArgsId; + var other = obj as DefaultUpdateArgsId; if (other == null) { return false; } - return base.Equals(obj); + return _workspaceKind == other._workspaceKind && base.Equals(obj); } public override int GetHashCode() { - return base.GetHashCode(); + return Hash.Combine(_workspaceKind.GetHashCode(), base.GetHashCode()); } } } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index eceed50ab72..fe0d09e46a2 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -15,7 +15,9 @@ namespace Microsoft.CodeAnalysis.Diagnostics { - [ExportIncrementalAnalyzerProvider(highPriorityForActiveFile: true, workspaceKinds: new string[] { WorkspaceKind.Host, WorkspaceKind.Interactive })] + [ExportIncrementalAnalyzerProvider( + highPriorityForActiveFile: true, name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, + workspaceKinds: new string[] { WorkspaceKind.Host, WorkspaceKind.Interactive })] internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { private readonly ConditionalWeakTable _map; diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index a56fd009c12..3ee5c09728d 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -221,6 +221,7 @@ + @@ -529,17 +530,13 @@ - - - - @@ -548,7 +545,6 @@ - diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index be988a16b81..3dd4ad9a43b 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -33,7 +33,7 @@ namespace Microsoft.CodeAnalysis.IncrementalCaches /// once it is fully indexed, then total results will be returned. /// [Shared] - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host)] + [ExportIncrementalAnalyzerProvider(nameof(SymbolTreeInfoIncrementalAnalyzerProvider), new[] { WorkspaceKind.Host })] [ExportWorkspaceServiceFactory(typeof(ISymbolTreeInfoCacheService))] internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider, IWorkspaceServiceFactory { diff --git a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs index e36924bfe12..79a23ff9a31 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.IncrementalCaches { - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] + [ExportIncrementalAnalyzerProvider(nameof(SyntaxTreeInfoIncrementalAnalyzerProvider), new[] { WorkspaceKind.Host }), Shared] internal class SyntaxTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider { public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) diff --git a/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs b/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs index 94183cb831c..b854538cb75 100644 --- a/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs +++ b/src/Features/Core/Portable/Notification/SemanticChangeNotificationService.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Notification { [Export(typeof(ISemanticChangeNotificationService)), Shared] - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.MiscellaneousFiles)] + [ExportIncrementalAnalyzerProvider(nameof(SemanticChangeNotificationService), workspaceKinds: null)] internal class SemanticChangeNotificationService : ISemanticChangeNotificationService, IIncrementalAnalyzerProvider { public event EventHandler OpenedDocumentSemanticChanged; @@ -38,17 +39,36 @@ public NotificationService(SemanticChangeNotificationService owner) _owner = owner; } - public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + public void RemoveDocument(DocumentId documentId) { + // now it runs for all workspace, make sure we get rid of entry from the map + // as soon as it is not needed. + // this whole thing will go away when workspace disable itself from solution crawler. VersionStamp unused; - _map.TryRemove(document.Id, out unused); + _map.TryRemove(documentId, out unused); + } + + public void RemoveProject(ProjectId projectId) + { + foreach (var documentId in _map.Keys.Where(id => id.ProjectId == projectId).ToArray()) + { + RemoveDocument(documentId); + } + } + public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) + { + return DocumentResetAsync(document, cancellationToken); + } + + public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + { + RemoveDocument(document.Id); return SpecializedTasks.EmptyTask; } public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) { - // TODO: Is this correct? return false; } @@ -81,11 +101,6 @@ public Task DocumentOpenAsync(Document document, CancellationToken cancellationT return SpecializedTasks.EmptyTask; } - public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } - public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) { return SpecializedTasks.EmptyTask; @@ -101,13 +116,6 @@ public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, Cancella return SpecializedTasks.EmptyTask; } - public void RemoveDocument(DocumentId documentId) - { - } - - public void RemoveProject(ProjectId projectId) - { - } #endregion } } diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs index d9029737cdd..3e668c0a7d1 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -16,11 +17,13 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler [ExportWorkspaceService(typeof(ISolutionCrawlerRegistrationService), ServiceLayer.Host), Shared] internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService { + private const string Default = "*"; + private readonly object _gate; private readonly SolutionCrawlerProgressReporter _progressReporter; private readonly IAsynchronousOperationListener _listener; - private readonly ImmutableArray> _analyzerProviders; + private readonly ImmutableDictionary>> _analyzerProviders; private readonly Dictionary _documentWorkCoordinatorMap; [ImportingConstructor] @@ -30,7 +33,9 @@ internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegi { _gate = new object(); - _analyzerProviders = analyzerProviders.ToImmutableArray(); + _analyzerProviders = analyzerProviders.GroupBy(kv => kv.Metadata.Name).ToImmutableDictionary(g => g.Key, g => g.ToImmutableArray()); + AssertAnalyzerProviders(_analyzerProviders); + _documentWorkCoordinatorMap = new Dictionary(ReferenceEqualityComparer.Instance); _listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.SolutionCrawler); @@ -51,7 +56,7 @@ public void Register(Workspace workspace) var coordinator = new WorkCoordinator( _listener, - _analyzerProviders.Where(l => l.Metadata.WorkspaceKinds.Any(wk => wk == workspace.Kind)), + GetAnalyzerProviders(workspace), new Registration(correlationId, workspace, _progressReporter)); _documentWorkCoordinatorMap.Add(workspace, coordinator); @@ -127,6 +132,99 @@ internal void WaitUntilCompletion_ForTestingPurposesOnly(Workspace workspace) } } + private IEnumerable> GetAnalyzerProviders(Workspace workspace) + { + Lazy lazyProvider; + foreach (var kv in _analyzerProviders) + { + var lazyProviders = kv.Value; + + // try get provider for the specific workspace kind + if (TryGetProvider(workspace.Kind, lazyProviders, out lazyProvider)) + { + yield return lazyProvider; + continue; + } + + // try get default provider + if (TryGetProvider(Default, lazyProviders, out lazyProvider)) + { + yield return lazyProvider; + } + } + } + + private bool TryGetProvider( + string kind, + ImmutableArray> lazyProviders, + out Lazy lazyProvider) + { + // set out param + lazyProvider = null; + + // try find provider for specific workspace kind + if (kind != Default) + { + foreach (var provider in lazyProviders) + { + if (provider.Metadata.WorkspaceKinds?.Any(wk => wk == kind) == true) + { + lazyProvider = provider; + return true; + } + } + + return false; + } + + // try find default provider + foreach (var provider in lazyProviders) + { + if (IsDefaultProvider(provider.Metadata)) + { + lazyProvider = provider; + return true; + } + + return false; + } + + return false; + } + + [Conditional("DEBUG")] + private static void AssertAnalyzerProviders( + ImmutableDictionary>> analyzerProviders) + { +#if DEBUG + // make sure there is duplicated provider defined for same workspace. + var set = new HashSet(); + foreach (var kv in analyzerProviders) + { + foreach (var lazyProvider in kv.Value) + { + if (IsDefaultProvider(lazyProvider.Metadata)) + { + Contract.Requires(set.Add(Default)); + continue; + } + + foreach (var kind in lazyProvider.Metadata.WorkspaceKinds) + { + Contract.Requires(set.Add(kind)); + } + } + + set.Clear(); + } +#endif + } + + private static bool IsDefaultProvider(IncrementalAnalyzerProviderMetadata providerMetadata) + { + return providerMetadata.WorkspaceKinds == null || providerMetadata.WorkspaceKinds.Length == 0; + } + private class Registration { public readonly int CorrelationId; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index 57833da5d86..49cb4fe5d0c 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -75,9 +75,7 @@ private IDiagnosticAnalyzerService GetDiagnosticAnalyzerService(IEnumerable GetActiveFileIncrementalAnalyzers( Registration registration, IEnumerable> providers) { - var matchingProviders = providers.Where(p => p.Metadata.HighPriorityForActiveFile && p.Metadata.WorkspaceKinds.Contains(registration.Workspace.Kind)); - - var orderedAnalyzers = GetOrderedAnalyzers(registration, matchingProviders); + var orderedAnalyzers = GetOrderedAnalyzers(registration, providers.Where(p => p.Metadata.HighPriorityForActiveFile)); SolutionCrawlerLogger.LogActiveFileAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers); return orderedAnalyzers; @@ -86,9 +84,7 @@ private IDiagnosticAnalyzerService GetDiagnosticAnalyzerService(IEnumerable GetIncrementalAnalyzers( Registration registration, IEnumerable> providers) { - var matchingProviders = providers.Where(p => p.Metadata.WorkspaceKinds.Contains(registration.Workspace.Kind)); - - var orderedAnalyzers = GetOrderedAnalyzers(registration, matchingProviders); + var orderedAnalyzers = GetOrderedAnalyzers(registration, providers); SolutionCrawlerLogger.LogAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers); return orderedAnalyzers; diff --git a/src/VisualStudio/Core/Def/Implementation/CompilationErrorTelemetry/CompilationErrorTelemetryIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/CompilationErrorTelemetry/CompilationErrorTelemetryIncrementalAnalyzer.cs index 3e3f14ee0c2..40fd85d5d82 100644 --- a/src/VisualStudio/Core/Def/Implementation/CompilationErrorTelemetry/CompilationErrorTelemetryIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/CompilationErrorTelemetry/CompilationErrorTelemetryIncrementalAnalyzer.cs @@ -8,7 +8,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CompilationErrorTelemetry { - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] + // Disabled compilation error telemetry per discussion with .net team. + // tracking bug - https://github.com/dotnet/roslyn/issues/11133 + // [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] internal class CompilationErrorTelemetryIncrementalAnalyzer : IncrementalAnalyzerProviderBase { public const string Name = "CompilationErrorTelemetryIncrementalAnalyzer"; diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs index 4ffb072bb15..9528d6a8409 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs @@ -8,7 +8,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute { - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] + [ExportIncrementalAnalyzerProvider(Name, new[] { WorkspaceKind.Host }), Shared] internal class DesignerAttributeIncrementalAnalyzerProvider : IncrementalAnalyzerProviderBase { public const string Name = "DesignerAttributeIncrementalAnalyzerProvider"; diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs index f0e85b71626..ccdb557b14e 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -43,9 +43,6 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin private readonly ImmutableArray _metadataReferences; private uint _runningDocumentTableEventsCookie; - // document worker coordinator - private ISolutionCrawlerRegistrationService _registrationService; - [ImportingConstructor] public MiscellaneousFilesWorkspace( IVsEditorAdaptersFactoryService editorAdaptersFactoryService, @@ -53,7 +50,7 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin SaveEventsService saveEventsService, VisualStudioWorkspace visualStudioWorkspace, SVsServiceProvider serviceProvider) : - base(visualStudioWorkspace.Services.HostServices, "MiscellaneousFiles") + base(visualStudioWorkspace.Services.HostServices, WorkspaceKind.MiscellaneousFiles) { _editorAdaptersFactoryService = editorAdaptersFactoryService; _fileTrackingMetadataAsSourceService = fileTrackingMetadataAsSourceService; @@ -74,32 +71,12 @@ public void RegisterLanguage(Guid languageGuid, string languageName, string scri internal void StartSolutionCrawler() { - if (_registrationService == null) - { - lock (this) - { - if (_registrationService == null) - { - _registrationService = this.Services.GetService(); - _registrationService.Register(this); - } - } - } + DiagnosticProvider.Enable(this, DiagnosticProvider.Options.Syntax); } internal void StopSolutionCrawler() { - if (_registrationService != null) - { - lock (this) - { - if (_registrationService != null) - { - _registrationService.Unregister(this, blockingShutdown: true); - _registrationService = null; - } - } - } + DiagnosticProvider.Disable(this); } private LanguageInformation TryGetLanguageInformation(string filename) @@ -255,7 +232,7 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e) } } else - { + { // We should now claim this AttachToDocument(docCookie, moniker); } diff --git a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs index 0b07dfb797e..31a2f6ec1e1 100644 --- a/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/SolutionSize/SolutionSizeTracker.cs @@ -16,7 +16,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionSize /// Track approximate solution size. /// [Export] - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] + [ExportIncrementalAnalyzerProvider(nameof(SolutionSizeTracker), new[] { WorkspaceKind.Host }), Shared] internal class SolutionSizeTracker : IIncrementalAnalyzerProvider { private readonly IncrementalAnalyzer _tracker = new IncrementalAnalyzer(); diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 5f32a6db0a9..0bd3cc30406 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -95,7 +95,6 @@ - diff --git a/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs b/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs index 0d3ddc500d4..6c84b918867 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/CodeModelIncrementalAnalyzer.cs @@ -16,7 +16,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel { - [ExportIncrementalAnalyzerProvider(WorkspaceKind.Host), Shared] + [ExportIncrementalAnalyzerProvider(nameof(CodeModelIncrementalAnalyzerProvider), new[] { WorkspaceKind.Host }), Shared] internal class CodeModelIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider { private readonly IAsynchronousOperationListener _listener; diff --git a/src/VisualStudio/Core/Test/Diagnostics/MiscDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb similarity index 94% rename from src/VisualStudio/Core/Test/Diagnostics/MiscDiagnosticUpdateSourceTests.vb rename to src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb index 2191d15c91e..94224cd04c0 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/MiscDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb @@ -18,14 +18,14 @@ Imports Roslyn.Test.Utilities Imports Roslyn.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics - Public Class MiscDiagnosticUpdateSourceTests + Public Class DefaultDiagnosticUpdateSourceTests Public Async Function TestMiscSquiggles() As Task Dim code = class 123 { } Using workspace = Await TestWorkspace.CreateCSharpAsync(code.ToString()) - Dim miscService = New MiscellaneousDiagnosticAnalyzerService( + Dim miscService = New DefaultDiagnosticAnalyzerService( New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()), New MockDiagnosticUpdateSourceRegistrationService()) @@ -68,7 +68,7 @@ class 123 { } class 123 { } Using workspace = Await TestWorkspace.CreateCSharpAsync(code.ToString()) - Dim miscService = New MiscellaneousDiagnosticAnalyzerService( + Dim miscService = New DefaultDiagnosticAnalyzerService( New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()), New MockDiagnosticUpdateSourceRegistrationService()) @@ -93,7 +93,7 @@ Class 123 End Class Using workspace = Await TestWorkspace.CreateVisualBasicAsync(code.ToString()) - Dim miscService = New MiscellaneousDiagnosticAnalyzerService( + Dim miscService = New DefaultDiagnosticAnalyzerService( New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()), New MockDiagnosticUpdateSourceRegistrationService()) diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index 8cd097525b7..41cb0f64671 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -295,7 +295,7 @@ - + diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs new file mode 100644 index 00000000000..f364a830cbe --- /dev/null +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs @@ -0,0 +1,49 @@ +// 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 Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.SolutionCrawler; + +namespace Microsoft.CodeAnalysis +{ + /// + /// Provide a way for users to turn on and off analyzing workspace for compiler diagnostics + /// + public static class DiagnosticProvider + { + public static void Enable(Workspace workspace, Options options) + { + var service = workspace.Services.GetService(); + + workspace.Options = GetOptions(workspace, options); + service.Register(workspace); + } + + public static void Disable(Workspace workspace) + { + var service = workspace.Services.GetService(); + service.Unregister(workspace); + } + + private static CodeAnalysis.Options.OptionSet GetOptions(Workspace workspace, Options options) + { + return workspace.Options + .WithChangedOption(InternalRuntimeDiagnosticOptions.Syntax, (options & Options.Syntax) == Options.Syntax) + .WithChangedOption(InternalRuntimeDiagnosticOptions.Semantic, (options & Options.Semantic) == Options.Semantic); + } + + [Flags] + public enum Options + { + /// + /// Include syntax errors + /// + Syntax = 0x01, + + /// + /// Include semantic errors + /// + Semantic = 0x02, + } + } +} diff --git a/src/Workspaces/Core/Portable/Diagnostics/InternalRuntimeDiagnosticOptions.cs b/src/Workspaces/Core/Portable/Diagnostics/InternalRuntimeDiagnosticOptions.cs new file mode 100644 index 00000000000..272fae15d90 --- /dev/null +++ b/src/Workspaces/Core/Portable/Diagnostics/InternalRuntimeDiagnosticOptions.cs @@ -0,0 +1,14 @@ +// 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 Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + internal static class InternalRuntimeDiagnosticOptions + { + public const string OptionName = "RuntimeDiagnostic"; + + public static readonly Option Syntax = new Option(OptionName, "Syntax", defaultValue: true); + public static readonly Option Semantic = new Option(OptionName, "Semantic", defaultValue: true); + } +} diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 66cef6d9d1b..f75914d6e22 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1 +1,7 @@ -abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.NameOfExpression(Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode \ No newline at end of file +Microsoft.CodeAnalysis.DiagnosticProvider +Microsoft.CodeAnalysis.DiagnosticProvider.Options +Microsoft.CodeAnalysis.DiagnosticProvider.Options.Semantic = 2 -> Microsoft.CodeAnalysis.DiagnosticProvider.Options +Microsoft.CodeAnalysis.DiagnosticProvider.Options.Syntax = 1 -> Microsoft.CodeAnalysis.DiagnosticProvider.Options +abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.NameOfExpression(Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode +static Microsoft.CodeAnalysis.DiagnosticProvider.Disable(Microsoft.CodeAnalysis.Workspace workspace) -> void +static Microsoft.CodeAnalysis.DiagnosticProvider.Enable(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DiagnosticProvider.Options options) -> void \ No newline at end of file diff --git a/src/Features/Core/Portable/SolutionCrawler/Extensibility/ExportIncrementalAnalyzerProviderAttribute.cs b/src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs similarity index 71% rename from src/Features/Core/Portable/SolutionCrawler/Extensibility/ExportIncrementalAnalyzerProviderAttribute.cs rename to src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs index a94eba4f639..c004e93c4e9 100644 --- a/src/Features/Core/Portable/SolutionCrawler/Extensibility/ExportIncrementalAnalyzerProviderAttribute.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs @@ -10,22 +10,24 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler internal class ExportIncrementalAnalyzerProviderAttribute : ExportAttribute { public bool HighPriorityForActiveFile { get; } + public string Name { get; } public string[] WorkspaceKinds { get; } - public ExportIncrementalAnalyzerProviderAttribute(params string[] workspaceKinds) + public ExportIncrementalAnalyzerProviderAttribute(string name, string[] workspaceKinds) : base(typeof(IIncrementalAnalyzerProvider)) { - if (workspaceKinds == null) + if (name == null) { - throw new ArgumentNullException(nameof(workspaceKinds)); + throw new ArgumentNullException(nameof(name)); } this.WorkspaceKinds = workspaceKinds; + this.Name = name; this.HighPriorityForActiveFile = false; } - public ExportIncrementalAnalyzerProviderAttribute(bool highPriorityForActiveFile, params string[] workspaceKinds) - : this(workspaceKinds) + public ExportIncrementalAnalyzerProviderAttribute(bool highPriorityForActiveFile, string name, string[] workspaceKinds) + : this(name, workspaceKinds) { this.HighPriorityForActiveFile = highPriorityForActiveFile; } diff --git a/src/Features/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs similarity index 100% rename from src/Features/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs rename to src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/Extensibility/IIncrementalAnalyzerProvider.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs similarity index 100% rename from src/Features/Core/Portable/SolutionCrawler/Extensibility/IIncrementalAnalyzerProvider.cs rename to src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs similarity index 100% rename from src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs rename to src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/Extensibility/IncrementalAnalyzerProviderMetadata.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs similarity index 88% rename from src/Features/Core/Portable/SolutionCrawler/Extensibility/IncrementalAnalyzerProviderMetadata.cs rename to src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs index a53ac998e7c..2ea2432aad6 100644 --- a/src/Features/Core/Portable/SolutionCrawler/Extensibility/IncrementalAnalyzerProviderMetadata.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs @@ -8,11 +8,13 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler internal class IncrementalAnalyzerProviderMetadata { public bool HighPriorityForActiveFile { get; } + public string Name { get; } public string[] WorkspaceKinds { get; } public IncrementalAnalyzerProviderMetadata(IDictionary data) { this.HighPriorityForActiveFile = (bool)data.GetValueOrDefault("HighPriorityForActiveFile"); + this.Name = (string)data.GetValueOrDefault("Name"); this.WorkspaceKinds = (string[])data.GetValueOrDefault("WorkspaceKinds"); } } diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs new file mode 100644 index 00000000000..0c50cc19e3d --- /dev/null +++ b/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs @@ -0,0 +1,25 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.SolutionCrawler +{ + /// + /// null implementation of the service. it doesn't do anything since there is no way to observe + /// its impact in this layer. + /// + [ExportWorkspaceService(typeof(ISolutionCrawlerRegistrationService), ServiceLayer.Default), Shared] + internal partial class NullSolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService + { + public void Register(Workspace workspace) + { + // base implementation do nothing. + } + + public void Unregister(Workspace workspace, bool blockingShutdown = false) + { + // base implementation do nothing. + } + } +} diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs b/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs new file mode 100644 index 00000000000..e4583e9285a --- /dev/null +++ b/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.SolutionCrawler +{ + internal static class WellKnownSolutionCrawlerAnalyzers + { + public const string Diagnostic = nameof(Diagnostic); + } +} diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 150c62218e1..6169a1bf2a9 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -334,8 +334,15 @@ + + + + + + + @@ -398,6 +405,8 @@ + + @@ -978,4 +987,4 @@ - + \ No newline at end of file -- GitLab