提交 f14c6db8 编写于 作者: H Heejae Chang

Merge pull request #356 from heejaechang/diagnosticRefactoring

refactor diagnostic service to prepare prototype work
......@@ -2,7 +2,6 @@
using System;
using System.Collections.Immutable;
using System.Threading;
namespace Microsoft.CodeAnalysis.Diagnostics
{
......
......@@ -351,7 +351,6 @@
<Compile Include="Implementation\Diagnostics\ClassificationTypeDefinitions.cs" />
<Compile Include="Implementation\Diagnostics\DiagnosticsClassificationTaggerProvider.cs" />
<Compile Include="Implementation\Diagnostics\DiagnosticsClassificationTaggerProvider.TagSource.cs" />
<Compile Include="Implementation\Diagnostics\InternalDiagnosticsOptions.cs" />
<Compile Include="Implementation\Diagnostics\DiagnosticsSquiggleTaggerProvider.cs" />
<Compile Include="Implementation\Diagnostics\DiagnosticsSquiggleTaggerProvider.TagSource.cs" />
<Compile Include="Implementation\DocumentationComments\AbstractDocumentationCommentCommandHandler.cs" />
......
......@@ -6,6 +6,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
......
......@@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
......@@ -48,8 +49,8 @@ public void SerializationTest_Document()
new TextSpan(50, 60), "mappedFile3", 90, 90, 100, 100, "originalFile3", 110, 110, 120, 120, title: "test3 title", description: "test3 description", helpLink: "http://test3link"),
};
var original = new DiagnosticAnalyzerService.AnalysisData(version1, version2, diagnostics.ToImmutableArray());
var state = new DiagnosticAnalyzerService.DiagnosticIncrementalAnalyzer.DiagnosticState("Test", VersionStamp.Default, LanguageNames.CSharp);
var original = new DiagnosticIncrementalAnalyzer.AnalysisData(version1, version2, diagnostics.ToImmutableArray());
var state = new DiagnosticIncrementalAnalyzer.DiagnosticState("Test", VersionStamp.Default, LanguageNames.CSharp);
state.PersistAsync(document, original, CancellationToken.None).Wait();
var recovered = state.TryGetExistingDataAsync(document, CancellationToken.None).Result;
......@@ -87,8 +88,8 @@ public void SerializationTest_Project()
workspace, document.Project.Id, description: "test3 description", helpLink: "http://test3link"),
};
var original = new DiagnosticAnalyzerService.AnalysisData(version1, version2, diagnostics.ToImmutableArray());
var state = new DiagnosticAnalyzerService.DiagnosticIncrementalAnalyzer.DiagnosticState("Test", VersionStamp.Default, LanguageNames.CSharp);
var original = new DiagnosticIncrementalAnalyzer.AnalysisData(version1, version2, diagnostics.ToImmutableArray());
var state = new DiagnosticIncrementalAnalyzer.DiagnosticState("Test", VersionStamp.Default, LanguageNames.CSharp);
state.PersistAsync(document.Project, original, CancellationToken.None).Wait();
var recovered = state.TryGetExistingDataAsync(document.Project, CancellationToken.None).Result;
......
......@@ -5,6 +5,7 @@ Imports System.Reflection
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Diagnostics.EngineV1
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text
Imports Roslyn.Utilities
......@@ -648,7 +649,7 @@ class AnonymousFunctions
Dim descriptorsMap = diagnosticService.GetDiagnosticDescriptors(project)
Assert.Equal(1, descriptorsMap.Count)
Dim incrementalAnalyzer = DirectCast(diagnosticService.CreateIncrementalAnalyzer(workspace), DiagnosticAnalyzerService.DiagnosticIncrementalAnalyzer)
Dim incrementalAnalyzer = DirectCast(DirectCast(diagnosticService.CreateIncrementalAnalyzer(workspace), DiagnosticAnalyzerService.IncrementalAnalyzerDelegatee).Analyzer, DiagnosticIncrementalAnalyzer)
' Verify that for an analyzer which has a registered compilation start action such that the start action registered an end action,
' we go and force complete all document diagnostics for entire project and then invoke and report end action diagnostics.
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics
......
......@@ -75,7 +75,7 @@ public async Task<FirstDiagnosticResult> GetFirstDiagnosticWithFixAsync(Document
using (var diagnostics = SharedPools.Default<List<DiagnosticData>>().GetPooledObject())
{
var fullResult = await _diagnosticService.TryGetDiagnosticsForSpanAsync(document, range, diagnostics.Object, cancellationToken).ConfigureAwait(false);
var fullResult = await _diagnosticService.TryAppendDiagnosticsForSpanAsync(document, range, diagnostics.Object, cancellationToken).ConfigureAwait(false);
foreach (var diagnostic in diagnostics.Object)
{
cancellationToken.ThrowIfCancellationRequested();
......
// 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.Diagnostics
{
internal static class AnalyzerDriverResources
{
internal static string AnalyzerFailure => FeaturesResources.UserDiagnosticAnalyzerFailure;
internal static string AnalyzerThrows => FeaturesResources.UserDiagnosticAnalyzerThrows;
internal static string ArgumentElementCannotBeNull => FeaturesResources.ArgumentElementCannotBeNull;
internal static string ArgumentCannotBeEmpty => FeaturesResources.ArgumentCannotBeEmpty;
}
}
......@@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
......
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal abstract class BaseDiagnosticIncrementalAnalyzer : IIncrementalAnalyzer
{
#region IIncrementalAnalyzer
public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken);
public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken);
public abstract Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken);
public abstract Task DocumentOpenAsync(Document document, CancellationToken cancellationToken);
public abstract Task DocumentResetAsync(Document document, CancellationToken cancellationToken);
public abstract Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken);
public abstract void RemoveDocument(DocumentId documentId);
public abstract void RemoveProject(ProjectId projectId);
#endregion
#region delegating methods from diagnostic analyzer service to each implementation of the engine
public abstract Task<ImmutableArray<DiagnosticData>> GetSpecificCachedDiagnosticsAsync(Solution solution, object id, CancellationToken cancellationToken);
public abstract Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution solution, object id, CancellationToken cancellationToken);
public abstract Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<bool> TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken);
public abstract Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document document, TextSpan range, CancellationToken cancellationToken);
#endregion
public virtual bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
{
return false;
}
public virtual void LogAnalyzerCountSummary()
{
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
using ProviderId = Int32;
internal partial class DiagnosticAnalyzerService
{
internal partial class DiagnosticIncrementalAnalyzer
{
/// <summary>
/// Maintains all MEF-imported diagnostic analyzers (with diagnostic states), which are enabled for all projects in the workspace,
/// and a map of per-project diagnostic analyzers (with diagnostic states).
/// </summary>
private partial class DiagnosticAnalyzersAndStates
{
private const string UserDiagnosticsPrefixTableName = "<UserDiagnostics>";
private readonly DiagnosticIncrementalAnalyzer _owner;
private readonly WorkspaceAnalyzersAndStates _sharedAnalyzersAndStates;
private readonly ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates> _projectAnalyzersAndStatesMap;
public readonly Workspace Workspace;
public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, AnalyzerManager analyzerManager)
{
_owner = owner;
_sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(analyzerManager);
_projectAnalyzersAndStatesMap = new ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates>();
this.Workspace = workspace;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId)
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
var language = project != null ? project.Language : null;
return GetAllExistingDiagnosticStates(projectId, language);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, string language)
{
var current = _sharedAnalyzersAndStates.GetAllExistingDiagnosticStates(language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllExistingDiagnosticStates());
}
return current;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, StateType type)
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
var language = project != null ? project.Language : null;
return GetAllExistingDiagnosticStates(projectId, type, language);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, StateType type, string language)
{
var current = _sharedAnalyzersAndStates.GetAllExistingDiagnosticStates(type, language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllExistingDiagnosticStates(type));
}
return current;
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllExistingProviderAndIds(Project project)
{
var current = _sharedAnalyzersAndStates.GetAllProviderAndIds(project.Language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(project.Id, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllProviderAndIds());
}
return current;
}
public async Task<IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>>> GetAllProviderAndIdsAsync(Project project, CancellationToken cancellationToken)
{
var current = _sharedAnalyzersAndStates.GetAllProviderAndIds(project.Language);
var projectAnalyzersAndStates = await GetOrCreateProjectAnalyzersAndStatesAsync(project, cancellationToken).ConfigureAwait(false);
if (projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllProviderAndIds());
}
return current;
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider, ProjectId projectId, string language)
{
Contract.ThrowIfFalse(providerId >= 0);
var sharedAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(language);
if (providerId < sharedAnalyzersCount)
{
return _sharedAnalyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider, language);
}
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (!_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) ||
projectAnalyzersAndStates == null)
{
return null;
}
return projectAnalyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider);
}
internal static DiagnosticState GetOrCreateDiagnosticState(DiagnosticState[,] diagnosticStateMaps, StateType stateType, int providerIndex, ProviderId providerId, DiagnosticAnalyzer provider, string language)
{
Contract.ThrowIfFalse(providerIndex >= 0);
Contract.ThrowIfFalse(providerIndex < diagnosticStateMaps.GetLength(1));
if (diagnosticStateMaps[(int)stateType, providerIndex] == null)
{
var nameAndVersion = GetUniqueDiagnosticStateNameAndVersion(stateType, providerId, provider);
var name = nameAndVersion.Item1;
var version = nameAndVersion.Item2;
diagnosticStateMaps[(int)stateType, providerIndex] = new DiagnosticState(name, version, language);
#if DEBUG
// Ensure diagnostic state name is indeed unique.
foreach (var type in s_documentScopeStateTypes)
{
for (var pId = 0; pId < diagnosticStateMaps.GetLength(1); pId++)
{
if (diagnosticStateMaps[(int)type, pId] != null)
{
Contract.ThrowIfFalse(name != diagnosticStateMaps[(int)type, pId].Name ||
(stateType == type &&
(pId == providerIndex || language != diagnosticStateMaps[(int)type, pId].Language)));
}
}
}
#endif
}
return diagnosticStateMaps[(int)stateType, providerIndex];
}
/// <summary>
/// Get the unique state name for the given {type, provider} tuple.
/// Note that this name is used by the underlying persistence stream of the corresponding <see cref="DiagnosticState"/> to Read/Write diagnostic data into the stream.
/// If any two distinct {type, provider} tuples have the same diagnostic state name, we will end up sharing the persistence stream between them, leading to duplicate/missing/incorrect diagnostic data.
/// </summary>
private static ValueTuple<string, VersionStamp> GetUniqueDiagnosticStateNameAndVersion(StateType type, ProviderId providerId, DiagnosticAnalyzer provider)
{
Contract.ThrowIfNull(provider);
// Get the unique ID for given diagnostic analyzer.
// note that we also put version stamp so that we can detect changed provider
var providerType = provider.GetType();
var location = providerType.Assembly.Location;
return ValueTuple.Create(UserDiagnosticsPrefixTableName + "_" + type + "_" + providerType.AssemblyQualifiedName, GetProviderVersion(location));
}
private static VersionStamp GetProviderVersion(string path)
{
if (path == null || !File.Exists(path))
{
return VersionStamp.Default;
}
return VersionStamp.Create(File.GetLastWriteTimeUtc(path));
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId, ProjectId projectId, string language)
{
Contract.ThrowIfFalse(providerId >= 0);
var sharedAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(language);
if (providerId < sharedAnalyzersCount)
{
return _sharedAnalyzersAndStates.GetDiagnosticState(stateType, providerId, language);
}
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (!_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) ||
projectAnalyzersAndStates == null)
{
return null;
}
return projectAnalyzersAndStates.GetDiagnosticState(stateType, providerId);
}
public bool RemoveProjectAnalyzersAndStates(ProjectId projectId)
{
ProjectAnalyzersAndStates projectAnalyzersAndStates;
return _projectAnalyzersAndStatesMap.TryRemove(projectId, out projectAnalyzersAndStates);
}
private async Task<ProjectAnalyzersAndStates> GetOrCreateProjectAnalyzersAndStatesAsync(Project project, CancellationToken cancellationToken)
{
// Update per-project analyzers if needed.
IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> removedStates;
ProjectAnalyzersAndStates projectAnalyzersAndStates = this.GetOrCreateProjectAnalyzersAndStates(project, out removedStates);
if (removedStates.Any())
{
// Analyzers got updated, so clear the existing persistent states.
await _owner.RemoveCacheDataAsync(project, removedStates, cancellationToken).ConfigureAwait(false);
}
return projectAnalyzersAndStates;
}
private ProjectAnalyzersAndStates GetOrCreateProjectAnalyzersAndStates(Project project,
out IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> removedStates)
{
removedStates = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
var newAnalyzersBuilder = ImmutableArray.CreateBuilder<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>>();
foreach (var analyzerReference in project.AnalyzerReferences)
{
// Filter out duplicate analyzer references.
if (_sharedAnalyzersAndStates.HasAnalyzerReference(analyzerReference, project.Language))
{
continue;
}
// Get analyzers in the analyzer reference for the given project language.
// Filter out duplicate analyzers.
// NOTE: The HasAnalyzerReference check above to filter out duplicate analyzer references works only for duplicate AnalyzerFileReference with the same underlying assembly.
// However, we can also have AnalyzerImageReference, which might contain the same DiagnosticAnalyzer instance across different AnalyzerImageReference instances
// and we want to avoid duplicate analyzers for that case. Hence we apply the HasAnalyzer filter here.
var analyzers = analyzerReference.GetAnalyzers(project.Language)
.Where(a => !_sharedAnalyzersAndStates.HasAnalyzer(a, project.Language));
if (analyzers.Any())
{
newAnalyzersBuilder.Add(KeyValuePair.Create(analyzerReference.Display, analyzers));
}
}
var newAnalyzers = newAnalyzersBuilder.ToImmutable();
ProjectAnalyzersAndStates newProjectAnalyzersAndStates = null;
ProjectAnalyzersAndStates currentProjectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(project.Id, out currentProjectAnalyzersAndStates))
{
var newAnalyzersCount = newAnalyzers.Sum(kv => kv.Value.Count());
if (currentProjectAnalyzersAndStates != null && currentProjectAnalyzersAndStates.AnalyzerCount == newAnalyzersCount)
{
Contract.ThrowIfFalse(currentProjectAnalyzersAndStates.AnalyzerCount > 0);
// Project still has the same number of analyzers, does the saved projectAnalyzersAndStates has the same set of analyzers?
var hasSameAnalyzers = true;
foreach (var analyzerPair in newAnalyzers)
{
foreach (var analyzer in analyzerPair.Value)
{
if (!currentProjectAnalyzersAndStates.HasAnalyzer(analyzer))
{
hasSameAnalyzers = false;
break;
}
}
}
if (hasSameAnalyzers)
{
return currentProjectAnalyzersAndStates;
}
}
}
else
{
currentProjectAnalyzersAndStates = null;
}
if (currentProjectAnalyzersAndStates != null)
{
removedStates = currentProjectAnalyzersAndStates.GetAllExistingDiagnosticStates();
}
var workspaceAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(project.Language);
newProjectAnalyzersAndStates = ProjectAnalyzersAndStates.CreateIfAnyAnalyzers(newAnalyzers, workspaceAnalyzersCount, project.Language);
return _projectAnalyzersAndStatesMap.AddOrUpdate(project.Id, newProjectAnalyzersAndStates, (k, c) => newProjectAnalyzersAndStates);
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SolutionCrawler.State;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal partial class DiagnosticAnalyzerService
{
internal partial class DiagnosticIncrementalAnalyzer
{
internal class DiagnosticState : AbstractAnalyzerState<object, object, AnalysisData>
{
private const int FormatVersion = 5;
private readonly string _stateName;
private readonly VersionStamp _version;
private readonly string _language;
public DiagnosticState(string stateName, VersionStamp version, string language)
{
Contract.ThrowIfNull(stateName);
_stateName = stateName;
_version = version;
_language = language;
}
internal string Name
{
get { return _stateName; }
}
internal string Language
{
get { return _language; }
}
protected override object GetCacheKey(object value)
{
var document = value as Document;
if (document != null)
{
return document.Id;
}
var project = (Project)value;
return project.Id;
}
protected override Solution GetSolution(object value)
{
var document = value as Document;
if (document != null)
{
return document.Project.Solution;
}
var project = (Project)value;
return project.Solution;
}
protected override bool ShouldCache(object value)
{
var document = value as Document;
if (document != null)
{
return document.IsOpen();
}
var project = (Project)value;
return project.Solution.Workspace.GetOpenDocumentIds(project.Id).Any();
}
protected override Task<Stream> ReadStreamAsync(IPersistentStorage storage, object value, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return storage.ReadStreamAsync(document, _stateName, cancellationToken);
}
var project = (Project)value;
return storage.ReadStreamAsync(project, _stateName, cancellationToken);
}
protected override AnalysisData TryGetExistingData(Stream stream, object value, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return TryGetExistingData(stream, document.Project, document, cancellationToken);
}
var project = (Project)value;
return TryGetExistingData(stream, project, null, cancellationToken);
}
private AnalysisData TryGetExistingData(Stream stream, Project project, Document document, CancellationToken cancellationToken)
{
var list = SharedPools.Default<List<DiagnosticData>>().AllocateAndClear();
try
{
using (var reader = new ObjectReader(stream))
{
var format = reader.ReadInt32();
if (format != FormatVersion)
{
return null;
}
// saved data is for same provider of different version of dll
var providerVersion = VersionStamp.ReadFrom(reader);
if (providerVersion != _version)
{
return null;
}
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
if (textVersion == VersionStamp.Default || dataVersion == VersionStamp.Default)
{
return null;
}
AppendItems(reader, project, document, list, cancellationToken);
return new AnalysisData(textVersion, dataVersion, list.ToImmutableArray<DiagnosticData>());
}
}
catch (Exception)
{
return null;
}
finally
{
SharedPools.Default<List<DiagnosticData>>().ClearAndFree(list);
}
}
private void AppendItems(ObjectReader reader, Project project, Document document, List<DiagnosticData> list, CancellationToken cancellationToken)
{
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var id = reader.ReadString();
var category = reader.ReadString();
var message = reader.ReadString();
var messageFormat = reader.ReadString();
var title = reader.ReadString();
var description = reader.ReadString();
var helpLink = reader.ReadString();
var severity = (DiagnosticSeverity)reader.ReadInt32();
var defaultSeverity = (DiagnosticSeverity)reader.ReadInt32();
var isEnabledByDefault = reader.ReadBoolean();
var warningLevel = reader.ReadInt32();
var start = reader.ReadInt32();
var length = reader.ReadInt32();
var textSpan = new TextSpan(start, length);
var originalFile = reader.ReadString();
var originalStartLine = reader.ReadInt32();
var originalStartColumn = reader.ReadInt32();
var originalEndLine = reader.ReadInt32();
var originalEndColumn = reader.ReadInt32();
var mappedFile = reader.ReadString();
var mappedStartLine = reader.ReadInt32();
var mappedStartColumn = reader.ReadInt32();
var mappedEndLine = reader.ReadInt32();
var mappedEndColumn = reader.ReadInt32();
var customTagsCount = reader.ReadInt32();
var customTags = GetCustomTags(reader, customTagsCount);
list.Add(new DiagnosticData(
id, category, message, messageFormat, severity, defaultSeverity, isEnabledByDefault, warningLevel, customTags,
project.Solution.Workspace, project.Id, document != null ? document.Id : null, document != null ? textSpan : (TextSpan?)null,
mappedFile, mappedStartLine, mappedStartColumn, mappedEndLine, mappedEndColumn,
originalFile, originalStartLine, originalStartColumn, originalEndLine, originalEndColumn,
title: title,
description: description,
helpLink: helpLink));
}
}
private static IReadOnlyList<string> GetCustomTags(ObjectReader reader, int count)
{
if (count > 0)
{
var tags = new List<string>(count);
for (var i = 0; i < count; i++)
{
tags.Add(reader.ReadString());
}
return new ReadOnlyCollection<string>(tags);
}
return SpecializedCollections.EmptyReadOnlyList<string>();
}
protected override Task<bool> WriteStreamAsync(IPersistentStorage storage, object value, Stream stream, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return storage.WriteStreamAsync(document, _stateName, stream, cancellationToken);
}
var project = (Project)value;
return storage.WriteStreamAsync(project, _stateName, stream, cancellationToken);
}
protected override void WriteTo(Stream stream, AnalysisData data, CancellationToken cancellationToken)
{
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
writer.WriteInt32(FormatVersion);
_version.WriteTo(writer);
data.TextVersion.WriteTo(writer);
data.DataVersion.WriteTo(writer);
writer.WriteInt32(data.Items.Length);
foreach (var item in data.Items)
{
cancellationToken.ThrowIfCancellationRequested();
writer.WriteString(item.Id);
writer.WriteString(item.Category);
writer.WriteString(item.Message);
writer.WriteString(item.MessageFormat);
writer.WriteString(item.Title);
writer.WriteString(item.Description);
writer.WriteString(item.HelpLink);
writer.WriteInt32((int)item.Severity);
writer.WriteInt32((int)item.DefaultSeverity);
writer.WriteBoolean(item.IsEnabledByDefault);
writer.WriteInt32(item.WarningLevel);
if (item.HasTextSpan)
{
// document state
writer.WriteInt32(item.TextSpan.Start);
writer.WriteInt32(item.TextSpan.Length);
}
else
{
// project state
writer.WriteInt32(0);
writer.WriteInt32(0);
}
writer.WriteString(item.OriginalFilePath);
writer.WriteInt32(item.OriginalStartLine);
writer.WriteInt32(item.OriginalStartColumn);
writer.WriteInt32(item.OriginalEndLine);
writer.WriteInt32(item.OriginalEndColumn);
writer.WriteString(item.MappedFilePath);
writer.WriteInt32(item.MappedStartLine);
writer.WriteInt32(item.MappedStartColumn);
writer.WriteInt32(item.MappedEndLine);
writer.WriteInt32(item.MappedEndColumn);
writer.WriteInt32(item.CustomTags.Count);
foreach (var tag in item.CustomTags)
{
writer.WriteString(tag);
}
}
}
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
using ProviderId = Int32;
internal partial class DiagnosticAnalyzerService
{
internal partial class DiagnosticIncrementalAnalyzer
{
/// <summary>
/// AnalyzerExecutor returns analyzed data without side effect.
/// it might uses cache to skip unnecessary duplicate analysis, but it will never update any internal state such as state
///
/// this is not finished form. as refactoring going on, this class will become more stateless in respect to caller, and less dependency on owner.
/// </summary>
internal class AnalyzerExecutor
{
private readonly DiagnosticIncrementalAnalyzer _owner;
public AnalyzerExecutor(DiagnosticIncrementalAnalyzer owner)
{
_owner = owner;
}
public async Task<AnalysisData> GetSyntaxAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver)
{
try
{
var document = analyzerDriver.Document;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Syntax, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (CheckSyntaxVersions(document, existingData, versions))
{
return existingData;
}
var diagnosticData = await GetSyntaxDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetDocumentAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDrvier)
{
try
{
var document = analyzerDrvier.Document;
var cancellationToken = analyzerDrvier.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Document, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (CheckSemanticVersions(document, existingData, versions))
{
return existingData;
}
var diagnosticData = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDrvier).ConfigureAwait(false);
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetDocumentBodyAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver,
SyntaxNode root, SyntaxNode member, int memberId, bool supportsSemanticInSpan, MemberRangeMap.MemberRanges ranges)
{
try
{
var document = analyzerDriver.Document;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Document, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
ImmutableArray<DiagnosticData> diagnosticData;
if (supportsSemanticInSpan && CanUseDocumentState(existingData, ranges.TextVersion, versions.DataVersion))
{
var memberDxData = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
diagnosticData = _owner.UpdateDocumentDiagnostics(existingData, ranges.Ranges, memberDxData.AsImmutableOrEmpty(), root.SyntaxTree, member, memberId);
ValidateMemberDiagnostics(providerId, provider, document, root, diagnosticData);
}
else
{
// if we can't re-use existing document state, only option we have is updating whole document state here.
var dx = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
diagnosticData = dx.AsImmutableOrEmpty();
}
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData);
}
catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetProjectAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver)
{
try
{
var project = analyzerDriver.Project;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Project, providerId, provider, project.Id, project.Language);
var existingData = await state.TryGetExistingDataAsync(project, cancellationToken).ConfigureAwait(false);
if (CheckSemanticVersions(project, existingData, versions))
{
return existingData;
}
// TODO: remove ForceAnalyzeAllDocuments at some point
var diagnosticData = await GetProjectDiagnosticsAsync(providerId, provider, analyzerDriver, _owner.ForceAnalyzeAllDocuments).ConfigureAwait(false);
return new AnalysisData(VersionStamp.Default, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
private DiagnosticAnalyzersAndStates AnalyzersAndState
{
get { return _owner._analyzersAndState; }
}
private bool CanUseDocumentState(AnalysisData existingData, VersionStamp textVersion, VersionStamp dataVersion)
{
if (existingData == null)
{
return false;
}
// make sure data stored in the cache is one from its previous text update
return existingData.DataVersion.Equals(dataVersion) && existingData.TextVersion.Equals(textVersion);
}
private static ImmutableArray<DiagnosticData> GetExistingItems(AnalysisData existingData)
{
if (existingData == null)
{
return ImmutableArray<DiagnosticData>.Empty;
}
return existingData.Items;
}
[Conditional("DEBUG")]
private void ValidateMemberDiagnostics(ProviderId providerId, DiagnosticAnalyzer provider, Document document, SyntaxNode root, ImmutableArray<DiagnosticData> diagnostics)
{
#if RANGE
var documentBasedDriver = new DiagnosticAnalyzerDriver(document, root.FullSpan, root, CancellationToken.None);
var expected = GetSemanticDiagnosticsAsync(providerId, provider, documentBasedDriver).WaitAndGetResult(documentBasedDriver.CancellationToken) ?? SpecializedCollections.EmptyEnumerable<DiagnosticData>();
Contract.Requires(diagnostics.SetEquals(expected));
#endif
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
using ProviderId = Int32;
internal partial class DiagnosticAnalyzerService
{
internal partial class DiagnosticIncrementalAnalyzer
{
private partial class DiagnosticAnalyzersAndStates
{
private partial class WorkspaceAnalyzersAndStates
{
/// <summary>
/// Maintains all workspace diagnostic analyzers (with diagnostic states) for a specific language.
/// </summary>
private class PerLanguageAnalyzersAndStates
{
private readonly string _language;
private readonly ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> _diagnosticAnalyzerIdMap;
private readonly int _analyzerCount;
private readonly DiagnosticState[,] _diagnosticStateMaps;
public PerLanguageAnalyzersAndStates(AnalyzerManager analyzerManager, string language)
{
_language = language;
// TODO: dynamically re-order providers so that cheap one runs first and slower runs later.
_diagnosticAnalyzerIdMap = CreateAnalyzerIdMap(analyzerManager, language);
_analyzerCount = _diagnosticAnalyzerIdMap.Values.Flatten().Count();
_diagnosticStateMaps = new DiagnosticState[s_stateTypeCount, _analyzerCount];
}
private static ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> CreateAnalyzerIdMap(
AnalyzerManager analyzerManager, string language)
{
var index = 0;
var map = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>>();
foreach (var kv in analyzerManager.GetHostDiagnosticAnalyzersPerReference(language))
{
var perAnalyzerMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ProviderId>();
var referenceIdentity = kv.Key;
var perLanguageAnalyzers = kv.Value;
if (perLanguageAnalyzers.Length > 0)
{
foreach (var analyzer in perLanguageAnalyzers)
{
var analyzerId = index++;
perAnalyzerMap.Add(analyzer, analyzerId);
}
map.Add(referenceIdentity, perAnalyzerMap.ToImmutable());
}
}
return map.ToImmutable();
}
public int AnalyzerCount
{
get { return _analyzerCount; }
}
public bool HasAnalyzerReference(AnalyzerReference analyzerReference)
{
Contract.ThrowIfNull(analyzerReference);
if (analyzerReference is AnalyzerFileReference)
{
// Filter out duplicate analyzer references with same assembly name/full path.
return analyzerReference.Display != null && _diagnosticAnalyzerIdMap.ContainsKey(analyzerReference.Display);
}
else
{
// For non-file references, we will check individual DiagnosticAnalyzer instances for duplicates.
return false;
}
}
public bool HasAnalyzer(DiagnosticAnalyzer analyzer)
{
Contract.ThrowIfNull(analyzer);
return _diagnosticAnalyzerIdMap.Values.Any(dict => dict.ContainsKey(analyzer));
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(StateType type)
{
foreach (var analyzerAndId in _diagnosticAnalyzerIdMap.Values.Flatten())
{
yield return Tuple.Create(_diagnosticStateMaps[(int)type, analyzerAndId.Value], analyzerAndId.Value, type);
}
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllProviderAndIds()
{
return _diagnosticAnalyzerIdMap.Values.Flatten();
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider)
{
Contract.ThrowIfFalse(providerId >= 0);
Contract.ThrowIfFalse(providerId < this.AnalyzerCount);
return DiagnosticAnalyzersAndStates.GetOrCreateDiagnosticState(_diagnosticStateMaps, stateType, providerId, providerId, provider, _language);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId)
{
Contract.ThrowIfFalse(providerId >= 0);
Contract.ThrowIfFalse(providerId < this.AnalyzerCount);
return _diagnosticStateMaps[(int)stateType, providerId];
}
}
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
using ProviderId = Int32;
internal partial class DiagnosticAnalyzerService
{
internal partial class DiagnosticIncrementalAnalyzer
{
private partial class DiagnosticAnalyzersAndStates
{
/// <summary>
/// Maintains all workspace diagnostic analyzers (with diagnostic states), which are enabled for all projects in the workspace.
/// </summary>
private partial class WorkspaceAnalyzersAndStates
{
private readonly AnalyzerManager _analyzerManager;
private ImmutableDictionary<string, PerLanguageAnalyzersAndStates> _perLanguageAnalyzersAndStatesMap;
public WorkspaceAnalyzersAndStates(AnalyzerManager analyzerManager)
{
_analyzerManager = analyzerManager;
_perLanguageAnalyzersAndStatesMap = ImmutableDictionary<string, PerLanguageAnalyzersAndStates>.Empty;
}
private static PerLanguageAnalyzersAndStates CreatePerLanguageAnalyzersAndStates(string language, WorkspaceAnalyzersAndStates @this)
{
return new PerLanguageAnalyzersAndStates(@this._analyzerManager, language);
}
private PerLanguageAnalyzersAndStates GetOrCreatePerLanguageAnalyzersAndStates(string language)
{
return ImmutableInterlocked.GetOrAdd(ref _perLanguageAnalyzersAndStatesMap, language, CreatePerLanguageAnalyzersAndStates, this);
}
public int GetAnalyzerCount(string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.AnalyzerCount;
}
public bool HasAnalyzerReference(AnalyzerReference analyzerReference, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.HasAnalyzerReference(analyzerReference);
}
public bool HasAnalyzer(DiagnosticAnalyzer analyzer, string language)
{
if (analyzer == null)
{
return false;
}
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.HasAnalyzer(analyzer);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(string languageOpt)
{
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var type in s_documentScopeStateTypes)
{
current = current.Concat(GetAllExistingDiagnosticStates(type, languageOpt));
}
return current;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(StateType type, string languageOpt)
{
if (languageOpt != null)
{
PerLanguageAnalyzersAndStates analyzersStates;
if (_perLanguageAnalyzersAndStatesMap.TryGetValue(languageOpt, out analyzersStates))
{
return analyzersStates.GetAllExistingDiagnosticStates(type);
}
else
{
return SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
}
}
// This might be a removed or closed document/project/solution.
// Return all existing states.
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var analyzersAndStates in _perLanguageAnalyzersAndStatesMap.Values)
{
current = current.Concat(analyzersAndStates.GetAllExistingDiagnosticStates(type));
}
return current;
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllProviderAndIds(string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetAllProviderAndIds();
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetDiagnosticState(stateType, providerId);
}
}
}
}
}
}
......@@ -53,7 +53,7 @@ public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticA
public void Reanalyze(Workspace workspace, IEnumerable<ProjectId> projectIds = null, IEnumerable<DocumentId> documentIds = null)
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
var service = workspace.Services.GetService<ISolutionCrawlerService>();
if (service != null && _map.TryGetValue(workspace, out analyzer))
......@@ -62,13 +62,13 @@ public void Reanalyze(Workspace workspace, IEnumerable<ProjectId> projectIds = n
}
}
public Task<bool> TryGetDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken)
public Task<bool> TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken)
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(document.Project.Solution.Workspace, out analyzer))
{
// always make sure that analyzer is called on background thread.
return Task.Run(async () => await analyzer.TryGetDiagnosticAsync(document, range, diagnostics, cancellationToken).ConfigureAwait(false), cancellationToken);
return Task.Run(async () => await analyzer.TryAppendDiagnosticsForSpanAsync(document, range, diagnostics, cancellationToken).ConfigureAwait(false), cancellationToken);
}
return SpecializedTasks.False;
......@@ -76,11 +76,11 @@ public Task<bool> TryGetDiagnosticsForSpanAsync(Document document, TextSpan rang
public Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document document, TextSpan range, CancellationToken cancellationToken)
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(document.Project.Solution.Workspace, out analyzer))
{
// always make sure that analyzer is called on background thread.
return Task.Run(async () => await analyzer.GetDiagnosticsAsync(document, range, cancellationToken).ConfigureAwait(false), cancellationToken);
return Task.Run(async () => await analyzer.GetDiagnosticsForSpanAsync(document, range, cancellationToken).ConfigureAwait(false), cancellationToken);
}
return SpecializedTasks.EmptyEnumerable<DiagnosticData>();
......@@ -88,7 +88,7 @@ public Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document doc
public Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken))
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(workspace, out analyzer))
{
return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, cancellationToken);
......@@ -99,7 +99,7 @@ public Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Workspace
public Task<ImmutableArray<DiagnosticData>> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, CancellationToken cancellationToken)
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(workspace, out analyzer))
{
return analyzer.GetSpecificCachedDiagnosticsAsync(workspace.CurrentSolution, id, cancellationToken);
......@@ -110,7 +110,7 @@ public Task<ImmutableArray<DiagnosticData>> GetSpecificCachedDiagnosticsAsync(Wo
public Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken))
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(solution.Workspace, out analyzer))
{
return analyzer.GetDiagnosticsAsync(solution, projectId, documentId, cancellationToken);
......@@ -121,7 +121,7 @@ public Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(Solution solutio
public Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution solution, object id, CancellationToken cancellationToken)
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(solution.Workspace, out analyzer))
{
return analyzer.GetSpecificDiagnosticsAsync(solution, id, cancellationToken);
......@@ -133,7 +133,7 @@ public Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution
public Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(
Solution solution, ProjectId projectId = null, DocumentId documentId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken))
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(solution.Workspace, out analyzer))
{
return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, cancellationToken);
......@@ -145,7 +145,7 @@ public Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution
public Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(
Solution solution, ProjectId projectId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken))
{
DiagnosticIncrementalAnalyzer analyzer;
BaseDiagnosticIncrementalAnalyzer analyzer;
if (_map.TryGetValue(solution.Workspace, out analyzer))
{
return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, cancellationToken);
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
......@@ -12,12 +19,12 @@ namespace Microsoft.CodeAnalysis.Diagnostics
[ExportIncrementalAnalyzerProvider(highPriorityForActiveFile: true, workspaceKinds: new string[] { WorkspaceKind.Host, WorkspaceKind.Interactive })]
internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider
{
private readonly ConditionalWeakTable<Workspace, DiagnosticIncrementalAnalyzer> _map;
private readonly ConditionalWeakTable<Workspace, DiagnosticIncrementalAnalyzer>.CreateValueCallback _createIncrementalAnalyzer;
private readonly ConditionalWeakTable<Workspace, BaseDiagnosticIncrementalAnalyzer> _map;
private readonly ConditionalWeakTable<Workspace, BaseDiagnosticIncrementalAnalyzer>.CreateValueCallback _createIncrementalAnalyzer;
private DiagnosticAnalyzerService()
{
_map = new ConditionalWeakTable<Workspace, DiagnosticIncrementalAnalyzer>();
_map = new ConditionalWeakTable<Workspace, BaseDiagnosticIncrementalAnalyzer>();
_createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback;
}
......@@ -30,26 +37,174 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
return null;
}
return GetOrCreateIncrementalAnalyzerCore(workspace);
return GetOrCreateIncrementalAnalyzer(workspace);
}
private DiagnosticIncrementalAnalyzer GetOrCreateIncrementalAnalyzerCore(Workspace workspace)
private BaseDiagnosticIncrementalAnalyzer GetOrCreateIncrementalAnalyzer(Workspace workspace)
{
return _map.GetValue(workspace, _createIncrementalAnalyzer);
}
private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace)
private BaseDiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace)
{
// subscribe to active context changed event for new workspace
workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged;
var correlationId = LogAggregator.GetNextId();
return new DiagnosticIncrementalAnalyzer(this, correlationId, workspace, _analyzerManager);
return new IncrementalAnalyzerDelegatee(this, workspace, _analyzerManager);
}
private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e)
{
Reanalyze(e.Document.Project.Solution.Workspace, documentIds: SpecializedCollections.SingletonEnumerable(e.Document.Id));
}
// internal for testing
internal class IncrementalAnalyzerDelegatee : BaseDiagnosticIncrementalAnalyzer
{
private readonly Workspace _workspace;
private readonly AnalyzerManager _analyzerManager;
private readonly DiagnosticAnalyzerService _owner;
// v1 diagnostic engine
private readonly EngineV1.DiagnosticIncrementalAnalyzer _engineV1;
// v2 diagnostic engine - for now v1
private readonly EngineV2.DiagnosticIncrementalAnalyzer _engineV2;
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);
}
#region IIncrementalAnalyzer
public override Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken)
{
return Analyzer.AnalyzeSyntaxAsync(document, cancellationToken);
}
public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken)
{
return Analyzer.AnalyzeDocumentAsync(document, bodyOpt, cancellationToken);
}
public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken)
{
return Analyzer.AnalyzeProjectAsync(project, semanticsChanged, cancellationToken);
}
public override Task DocumentOpenAsync(Document document, CancellationToken cancellationToken)
{
return Analyzer.DocumentOpenAsync(document, cancellationToken);
}
public override Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
{
return Analyzer.DocumentResetAsync(document, cancellationToken);
}
public override Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken)
{
return Analyzer.NewSolutionSnapshotAsync(solution, cancellationToken);
}
public override void RemoveDocument(DocumentId documentId)
{
Analyzer.RemoveDocument(documentId);
}
public override void RemoveProject(ProjectId projectId)
{
Analyzer.RemoveProject(projectId);
}
public override bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
{
return e.Option.Feature == SimplificationOptions.PerLanguageFeatureName ||
e.Option.Feature == SimplificationOptions.NonPerLanguageFeatureName ||
e.Option == ServiceFeatureOnOffOptions.ClosedFileDiagnostic ||
e.Option == InternalDiagnosticsOptions.UseDiagnosticEngineV2 ||
Analyzer.NeedsReanalysisOnOptionChanged(sender, e);
}
#endregion
#region delegating methods from diagnostic analyzer service to each implementation of the engine
public override Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken))
{
return Analyzer.GetCachedDiagnosticsAsync(solution, projectId, documentId, cancellationToken);
}
public override Task<ImmutableArray<DiagnosticData>> GetSpecificCachedDiagnosticsAsync(Solution solution, object id, CancellationToken cancellationToken)
{
return Analyzer.GetSpecificCachedDiagnosticsAsync(solution, id, cancellationToken);
}
public override Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, CancellationToken cancellationToken = default(CancellationToken))
{
return Analyzer.GetDiagnosticsAsync(solution, projectId, documentId, cancellationToken);
}
public override Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution solution, object id, CancellationToken cancellationToken)
{
return Analyzer.GetSpecificDiagnosticsAsync(solution, id, cancellationToken);
}
public override Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken))
{
return Analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, cancellationToken);
}
public override Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, ImmutableHashSet<string> diagnosticIds = null, CancellationToken cancellationToken = default(CancellationToken))
{
return Analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, cancellationToken);
}
public override Task<bool> TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken)
{
return Analyzer.TryAppendDiagnosticsForSpanAsync(document, range, diagnostics, cancellationToken);
}
public override Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document document, TextSpan range, CancellationToken cancellationToken)
{
return Analyzer.GetDiagnosticsForSpanAsync(document, range, cancellationToken);
}
#endregion
public void TurnOff(bool useV2)
{
var turnedOffAnalyzer = GetAnalyzer(!useV2);
foreach (var project in _workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
turnedOffAnalyzer.RemoveDocument(document.Id);
}
turnedOffAnalyzer.RemoveProject(project.Id);
}
}
// internal for testing
internal BaseDiagnosticIncrementalAnalyzer Analyzer
{
get
{
var option = _workspace.Options.GetOption(InternalDiagnosticsOptions.UseDiagnosticEngineV2);
return GetAnalyzer(option);
}
}
private BaseDiagnosticIncrementalAnalyzer GetAnalyzer(bool useV2)
{
return useV2 ? (BaseDiagnosticIncrementalAnalyzer)_engineV2 : _engineV1;
}
}
}
}
// 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.Collections.Generic;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Host.Mef;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
......
......@@ -5,7 +5,7 @@
using System.Collections.Immutable;
using System.Threading;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal abstract class AbstractSyntaxNodeAnalyzerService<TLanguageKindEnum> : ISyntaxNodeAnalyzerService where TLanguageKindEnum : struct
{
......
......@@ -2,7 +2,7 @@
using System;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
[Flags]
internal enum DiagnosticAnalyzerCategory
......
......@@ -8,6 +8,7 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.GeneratedCodeRecognition;
using Microsoft.CodeAnalysis.Internal.Log;
......@@ -16,9 +17,9 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal partial class DiagnosticAnalyzerDriver
internal class DiagnosticAnalyzerDriver
{
private readonly Document _document;
......@@ -602,12 +603,4 @@ private bool CatchAnalyzerException(Exception e, DiagnosticAnalyzer analyzer, bo
return true;
}
}
internal static class AnalyzerDriverResources
{
internal static string AnalyzerFailure => FeaturesResources.UserDiagnosticAnalyzerFailure;
internal static string AnalyzerThrows => FeaturesResources.UserDiagnosticAnalyzerThrows;
internal static string ArgumentElementCannotBeNull => FeaturesResources.ArgumentElementCannotBeNull;
internal static string ArgumentCannotBeEmpty => FeaturesResources.ArgumentCannotBeEmpty;
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
internal partial class DiagnosticIncrementalAnalyzer
{
/// <summary>
/// Maintains all MEF-imported diagnostic analyzers (with diagnostic states), which are enabled for all projects in the workspace,
/// and a map of per-project diagnostic analyzers (with diagnostic states).
/// </summary>
private partial class DiagnosticAnalyzersAndStates
{
private const string UserDiagnosticsPrefixTableName = "<UserDiagnostics>";
private readonly DiagnosticIncrementalAnalyzer _owner;
private readonly WorkspaceAnalyzersAndStates _sharedAnalyzersAndStates;
private readonly ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates> _projectAnalyzersAndStatesMap;
public readonly Workspace Workspace;
public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, AnalyzerManager analyzerManager)
{
_owner = owner;
_sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(analyzerManager);
_projectAnalyzersAndStatesMap = new ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates>();
this.Workspace = workspace;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId)
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
var language = project != null ? project.Language : null;
return GetAllExistingDiagnosticStates(projectId, language);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, string language)
{
var current = _sharedAnalyzersAndStates.GetAllExistingDiagnosticStates(language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllExistingDiagnosticStates());
}
return current;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, StateType type)
{
var project = this.Workspace.CurrentSolution.GetProject(projectId);
var language = project != null ? project.Language : null;
return GetAllExistingDiagnosticStates(projectId, type, language);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(ProjectId projectId, StateType type, string language)
{
var current = _sharedAnalyzersAndStates.GetAllExistingDiagnosticStates(type, language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllExistingDiagnosticStates(type));
}
return current;
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllExistingProviderAndIds(Project project)
{
var current = _sharedAnalyzersAndStates.GetAllProviderAndIds(project.Language);
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(project.Id, out projectAnalyzersAndStates) &&
projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllProviderAndIds());
}
return current;
}
public async Task<IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>>> GetAllProviderAndIdsAsync(Project project, CancellationToken cancellationToken)
{
var current = _sharedAnalyzersAndStates.GetAllProviderAndIds(project.Language);
var projectAnalyzersAndStates = await GetOrCreateProjectAnalyzersAndStatesAsync(project, cancellationToken).ConfigureAwait(false);
if (projectAnalyzersAndStates != null)
{
current = current.Concat(projectAnalyzersAndStates.GetAllProviderAndIds());
}
return current;
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider, ProjectId projectId, string language)
{
Contract.ThrowIfFalse(providerId >= 0);
var sharedAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(language);
if (providerId < sharedAnalyzersCount)
{
return _sharedAnalyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider, language);
}
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (!_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) ||
projectAnalyzersAndStates == null)
{
return null;
}
return projectAnalyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider);
}
internal static DiagnosticState GetOrCreateDiagnosticState(DiagnosticState[,] diagnosticStateMaps, StateType stateType, int providerIndex, ProviderId providerId, DiagnosticAnalyzer provider, string language)
{
Contract.ThrowIfFalse(providerIndex >= 0);
Contract.ThrowIfFalse(providerIndex < diagnosticStateMaps.GetLength(1));
if (diagnosticStateMaps[(int)stateType, providerIndex] == null)
{
var nameAndVersion = GetUniqueDiagnosticStateNameAndVersion(stateType, providerId, provider);
var name = nameAndVersion.Item1;
var version = nameAndVersion.Item2;
diagnosticStateMaps[(int)stateType, providerIndex] = new DiagnosticState(name, version, language);
#if DEBUG
// Ensure diagnostic state name is indeed unique.
foreach (var type in s_documentScopeStateTypes)
{
for (var pId = 0; pId < diagnosticStateMaps.GetLength(1); pId++)
{
if (diagnosticStateMaps[(int)type, pId] != null)
{
Contract.ThrowIfFalse(name != diagnosticStateMaps[(int)type, pId].Name ||
(stateType == type &&
(pId == providerIndex || language != diagnosticStateMaps[(int)type, pId].Language)));
}
}
}
#endif
}
return diagnosticStateMaps[(int)stateType, providerIndex];
}
/// <summary>
/// Get the unique state name for the given {type, provider} tuple.
/// Note that this name is used by the underlying persistence stream of the corresponding <see cref="DiagnosticState"/> to Read/Write diagnostic data into the stream.
/// If any two distinct {type, provider} tuples have the same diagnostic state name, we will end up sharing the persistence stream between them, leading to duplicate/missing/incorrect diagnostic data.
/// </summary>
private static ValueTuple<string, VersionStamp> GetUniqueDiagnosticStateNameAndVersion(StateType type, ProviderId providerId, DiagnosticAnalyzer provider)
{
Contract.ThrowIfNull(provider);
// Get the unique ID for given diagnostic analyzer.
// note that we also put version stamp so that we can detect changed provider
var providerType = provider.GetType();
var location = providerType.Assembly.Location;
return ValueTuple.Create(UserDiagnosticsPrefixTableName + "_" + type + "_" + providerType.AssemblyQualifiedName, GetProviderVersion(location));
}
private static VersionStamp GetProviderVersion(string path)
{
if (path == null || !File.Exists(path))
{
return VersionStamp.Default;
}
return VersionStamp.Create(File.GetLastWriteTimeUtc(path));
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId, ProjectId projectId, string language)
{
Contract.ThrowIfFalse(providerId >= 0);
var sharedAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(language);
if (providerId < sharedAnalyzersCount)
{
return _sharedAnalyzersAndStates.GetDiagnosticState(stateType, providerId, language);
}
ProjectAnalyzersAndStates projectAnalyzersAndStates;
if (!_projectAnalyzersAndStatesMap.TryGetValue(projectId, out projectAnalyzersAndStates) ||
projectAnalyzersAndStates == null)
{
return null;
}
return projectAnalyzersAndStates.GetDiagnosticState(stateType, providerId);
}
public bool RemoveProjectAnalyzersAndStates(ProjectId projectId)
{
ProjectAnalyzersAndStates projectAnalyzersAndStates;
return _projectAnalyzersAndStatesMap.TryRemove(projectId, out projectAnalyzersAndStates);
}
private async Task<ProjectAnalyzersAndStates> GetOrCreateProjectAnalyzersAndStatesAsync(Project project, CancellationToken cancellationToken)
{
// Update per-project analyzers if needed.
IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> removedStates;
ProjectAnalyzersAndStates projectAnalyzersAndStates = this.GetOrCreateProjectAnalyzersAndStates(project, out removedStates);
if (removedStates.Any())
{
// Analyzers got updated, so clear the existing persistent states.
await _owner.RemoveCacheDataAsync(project, removedStates, cancellationToken).ConfigureAwait(false);
}
return projectAnalyzersAndStates;
}
private ProjectAnalyzersAndStates GetOrCreateProjectAnalyzersAndStates(Project project,
out IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> removedStates)
{
removedStates = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
var newAnalyzersBuilder = ImmutableArray.CreateBuilder<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>>();
foreach (var analyzerReference in project.AnalyzerReferences)
{
// Filter out duplicate analyzer references.
if (_sharedAnalyzersAndStates.HasAnalyzerReference(analyzerReference, project.Language))
{
continue;
}
// Get analyzers in the analyzer reference for the given project language.
// Filter out duplicate analyzers.
// NOTE: The HasAnalyzerReference check above to filter out duplicate analyzer references works only for duplicate AnalyzerFileReference with the same underlying assembly.
// However, we can also have AnalyzerImageReference, which might contain the same DiagnosticAnalyzer instance across different AnalyzerImageReference instances
// and we want to avoid duplicate analyzers for that case. Hence we apply the HasAnalyzer filter here.
var analyzers = analyzerReference.GetAnalyzers(project.Language)
.Where(a => !_sharedAnalyzersAndStates.HasAnalyzer(a, project.Language));
if (analyzers.Any())
{
newAnalyzersBuilder.Add(KeyValuePair.Create(analyzerReference.Display, analyzers));
}
}
var newAnalyzers = newAnalyzersBuilder.ToImmutable();
ProjectAnalyzersAndStates newProjectAnalyzersAndStates = null;
ProjectAnalyzersAndStates currentProjectAnalyzersAndStates;
if (_projectAnalyzersAndStatesMap.TryGetValue(project.Id, out currentProjectAnalyzersAndStates))
{
var newAnalyzersCount = newAnalyzers.Sum(kv => kv.Value.Count());
if (currentProjectAnalyzersAndStates != null && currentProjectAnalyzersAndStates.AnalyzerCount == newAnalyzersCount)
{
Contract.ThrowIfFalse(currentProjectAnalyzersAndStates.AnalyzerCount > 0);
// Project still has the same number of analyzers, does the saved projectAnalyzersAndStates has the same set of analyzers?
var hasSameAnalyzers = true;
foreach (var analyzerPair in newAnalyzers)
{
foreach (var analyzer in analyzerPair.Value)
{
if (!currentProjectAnalyzersAndStates.HasAnalyzer(analyzer))
{
hasSameAnalyzers = false;
break;
}
}
}
if (hasSameAnalyzers)
{
return currentProjectAnalyzersAndStates;
}
}
}
else
{
currentProjectAnalyzersAndStates = null;
}
if (currentProjectAnalyzersAndStates != null)
{
removedStates = currentProjectAnalyzersAndStates.GetAllExistingDiagnosticStates();
}
var workspaceAnalyzersCount = _sharedAnalyzersAndStates.GetAnalyzerCount(project.Language);
newProjectAnalyzersAndStates = ProjectAnalyzersAndStates.CreateIfAnyAnalyzers(newAnalyzers, workspaceAnalyzersCount, project.Language);
return _projectAnalyzersAndStatesMap.AddOrUpdate(project.Id, newProjectAnalyzersAndStates, (k, c) => newProjectAnalyzersAndStates);
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SolutionCrawler.State;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal partial class DiagnosticIncrementalAnalyzer
{
internal class DiagnosticState : AbstractAnalyzerState<object, object, AnalysisData>
{
private const int FormatVersion = 5;
private readonly string _stateName;
private readonly VersionStamp _version;
private readonly string _language;
public DiagnosticState(string stateName, VersionStamp version, string language)
{
Contract.ThrowIfNull(stateName);
_stateName = stateName;
_version = version;
_language = language;
}
internal string Name
{
get { return _stateName; }
}
internal string Language
{
get { return _language; }
}
protected override object GetCacheKey(object value)
{
var document = value as Document;
if (document != null)
{
return document.Id;
}
var project = (Project)value;
return project.Id;
}
protected override Solution GetSolution(object value)
{
var document = value as Document;
if (document != null)
{
return document.Project.Solution;
}
var project = (Project)value;
return project.Solution;
}
protected override bool ShouldCache(object value)
{
var document = value as Document;
if (document != null)
{
return document.IsOpen();
}
var project = (Project)value;
return project.Solution.Workspace.GetOpenDocumentIds(project.Id).Any();
}
protected override Task<Stream> ReadStreamAsync(IPersistentStorage storage, object value, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return storage.ReadStreamAsync(document, _stateName, cancellationToken);
}
var project = (Project)value;
return storage.ReadStreamAsync(project, _stateName, cancellationToken);
}
protected override AnalysisData TryGetExistingData(Stream stream, object value, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return TryGetExistingData(stream, document.Project, document, cancellationToken);
}
var project = (Project)value;
return TryGetExistingData(stream, project, null, cancellationToken);
}
private AnalysisData TryGetExistingData(Stream stream, Project project, Document document, CancellationToken cancellationToken)
{
var list = SharedPools.Default<List<DiagnosticData>>().AllocateAndClear();
try
{
using (var reader = new ObjectReader(stream))
{
var format = reader.ReadInt32();
if (format != FormatVersion)
{
return null;
}
// saved data is for same provider of different version of dll
var providerVersion = VersionStamp.ReadFrom(reader);
if (providerVersion != _version)
{
return null;
}
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
if (textVersion == VersionStamp.Default || dataVersion == VersionStamp.Default)
{
return null;
}
AppendItems(reader, project, document, list, cancellationToken);
return new AnalysisData(textVersion, dataVersion, list.ToImmutableArray<DiagnosticData>());
}
}
catch (Exception)
{
return null;
}
finally
{
SharedPools.Default<List<DiagnosticData>>().ClearAndFree(list);
}
}
private void AppendItems(ObjectReader reader, Project project, Document document, List<DiagnosticData> list, CancellationToken cancellationToken)
{
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var id = reader.ReadString();
var category = reader.ReadString();
var message = reader.ReadString();
var messageFormat = reader.ReadString();
var title = reader.ReadString();
var description = reader.ReadString();
var helpLink = reader.ReadString();
var severity = (DiagnosticSeverity)reader.ReadInt32();
var defaultSeverity = (DiagnosticSeverity)reader.ReadInt32();
var isEnabledByDefault = reader.ReadBoolean();
var warningLevel = reader.ReadInt32();
var start = reader.ReadInt32();
var length = reader.ReadInt32();
var textSpan = new TextSpan(start, length);
var originalFile = reader.ReadString();
var originalStartLine = reader.ReadInt32();
var originalStartColumn = reader.ReadInt32();
var originalEndLine = reader.ReadInt32();
var originalEndColumn = reader.ReadInt32();
var mappedFile = reader.ReadString();
var mappedStartLine = reader.ReadInt32();
var mappedStartColumn = reader.ReadInt32();
var mappedEndLine = reader.ReadInt32();
var mappedEndColumn = reader.ReadInt32();
var customTagsCount = reader.ReadInt32();
var customTags = GetCustomTags(reader, customTagsCount);
list.Add(new DiagnosticData(
id, category, message, messageFormat, severity, defaultSeverity, isEnabledByDefault, warningLevel, customTags,
project.Solution.Workspace, project.Id, document != null ? document.Id : null, document != null ? textSpan : (TextSpan?)null,
mappedFile, mappedStartLine, mappedStartColumn, mappedEndLine, mappedEndColumn,
originalFile, originalStartLine, originalStartColumn, originalEndLine, originalEndColumn,
title: title,
description: description,
helpLink: helpLink));
}
}
private static IReadOnlyList<string> GetCustomTags(ObjectReader reader, int count)
{
if (count > 0)
{
var tags = new List<string>(count);
for (var i = 0; i < count; i++)
{
tags.Add(reader.ReadString());
}
return new ReadOnlyCollection<string>(tags);
}
return SpecializedCollections.EmptyReadOnlyList<string>();
}
protected override Task<bool> WriteStreamAsync(IPersistentStorage storage, object value, Stream stream, CancellationToken cancellationToken)
{
var document = value as Document;
if (document != null)
{
return storage.WriteStreamAsync(document, _stateName, stream, cancellationToken);
}
var project = (Project)value;
return storage.WriteStreamAsync(project, _stateName, stream, cancellationToken);
}
protected override void WriteTo(Stream stream, AnalysisData data, CancellationToken cancellationToken)
{
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
writer.WriteInt32(FormatVersion);
_version.WriteTo(writer);
data.TextVersion.WriteTo(writer);
data.DataVersion.WriteTo(writer);
writer.WriteInt32(data.Items.Length);
foreach (var item in data.Items)
{
cancellationToken.ThrowIfCancellationRequested();
writer.WriteString(item.Id);
writer.WriteString(item.Category);
writer.WriteString(item.Message);
writer.WriteString(item.MessageFormat);
writer.WriteString(item.Title);
writer.WriteString(item.Description);
writer.WriteString(item.HelpLink);
writer.WriteInt32((int)item.Severity);
writer.WriteInt32((int)item.DefaultSeverity);
writer.WriteBoolean(item.IsEnabledByDefault);
writer.WriteInt32(item.WarningLevel);
if (item.HasTextSpan)
{
// document state
writer.WriteInt32(item.TextSpan.Start);
writer.WriteInt32(item.TextSpan.Length);
}
else
{
// project state
writer.WriteInt32(0);
writer.WriteInt32(0);
}
writer.WriteString(item.OriginalFilePath);
writer.WriteInt32(item.OriginalStartLine);
writer.WriteInt32(item.OriginalStartColumn);
writer.WriteInt32(item.OriginalEndLine);
writer.WriteInt32(item.OriginalEndColumn);
writer.WriteString(item.MappedFilePath);
writer.WriteInt32(item.MappedStartLine);
writer.WriteInt32(item.MappedStartColumn);
writer.WriteInt32(item.MappedEndLine);
writer.WriteInt32(item.MappedEndColumn);
writer.WriteInt32(item.CustomTags.Count);
foreach (var tag in item.CustomTags)
{
writer.WriteString(tag);
}
}
}
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
internal partial class DiagnosticIncrementalAnalyzer
{
/// <summary>
/// AnalyzerExecutor returns analyzed data without side effect.
/// it might uses cache to skip unnecessary duplicate analysis, but it will never update any internal state such as state
///
/// this is not finished form. as refactoring going on, this class will become more stateless in respect to caller, and less dependency on owner.
/// </summary>
internal class AnalyzerExecutor
{
private readonly DiagnosticIncrementalAnalyzer _owner;
public AnalyzerExecutor(DiagnosticIncrementalAnalyzer owner)
{
_owner = owner;
}
public async Task<AnalysisData> GetSyntaxAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver)
{
try
{
var document = analyzerDriver.Document;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Syntax, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (CheckSyntaxVersions(document, existingData, versions))
{
return existingData;
}
var diagnosticData = await GetSyntaxDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetDocumentAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDrvier)
{
try
{
var document = analyzerDrvier.Document;
var cancellationToken = analyzerDrvier.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Document, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (CheckSemanticVersions(document, existingData, versions))
{
return existingData;
}
var diagnosticData = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDrvier).ConfigureAwait(false);
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetDocumentBodyAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver,
SyntaxNode root, SyntaxNode member, int memberId, bool supportsSemanticInSpan, MemberRangeMap.MemberRanges ranges)
{
try
{
var document = analyzerDriver.Document;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Document, providerId, provider, document.Project.Id, document.Project.Language);
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
ImmutableArray<DiagnosticData> diagnosticData;
if (supportsSemanticInSpan && CanUseDocumentState(existingData, ranges.TextVersion, versions.DataVersion))
{
var memberDxData = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
diagnosticData = _owner.UpdateDocumentDiagnostics(existingData, ranges.Ranges, memberDxData.AsImmutableOrEmpty(), root.SyntaxTree, member, memberId);
ValidateMemberDiagnostics(providerId, provider, document, root, diagnosticData);
}
else
{
// if we can't re-use existing document state, only option we have is updating whole document state here.
var dx = await GetSemanticDiagnosticsAsync(providerId, provider, analyzerDriver).ConfigureAwait(false);
diagnosticData = dx.AsImmutableOrEmpty();
}
return new AnalysisData(versions.TextVersion, versions.DataVersion, GetExistingItems(existingData), diagnosticData);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
public async Task<AnalysisData> GetProjectAnalysisDataAsync(
DiagnosticAnalyzer provider, ProviderId providerId, VersionArgument versions, DiagnosticAnalyzerDriver analyzerDriver)
{
try
{
var project = analyzerDriver.Project;
var cancellationToken = analyzerDriver.CancellationToken;
var state = AnalyzersAndState.GetOrCreateDiagnosticState(StateType.Project, providerId, provider, project.Id, project.Language);
var existingData = await state.TryGetExistingDataAsync(project, cancellationToken).ConfigureAwait(false);
if (CheckSemanticVersions(project, existingData, versions))
{
return existingData;
}
// TODO: remove ForceAnalyzeAllDocuments at some point
var diagnosticData = await GetProjectDiagnosticsAsync(providerId, provider, analyzerDriver, _owner.ForceAnalyzeAllDocuments).ConfigureAwait(false);
return new AnalysisData(VersionStamp.Default, versions.DataVersion, GetExistingItems(existingData), diagnosticData.AsImmutableOrEmpty());
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
private DiagnosticAnalyzersAndStates AnalyzersAndState
{
get { return _owner._analyzersAndState; }
}
private bool CanUseDocumentState(AnalysisData existingData, VersionStamp textVersion, VersionStamp dataVersion)
{
if (existingData == null)
{
return false;
}
// make sure data stored in the cache is one from its previous text update
return existingData.DataVersion.Equals(dataVersion) && existingData.TextVersion.Equals(textVersion);
}
private static ImmutableArray<DiagnosticData> GetExistingItems(AnalysisData existingData)
{
if (existingData == null)
{
return ImmutableArray<DiagnosticData>.Empty;
}
return existingData.Items;
}
[Conditional("DEBUG")]
private void ValidateMemberDiagnostics(ProviderId providerId, DiagnosticAnalyzer provider, Document document, SyntaxNode root, ImmutableArray<DiagnosticData> diagnostics)
{
#if RANGE
var documentBasedDriver = new DiagnosticAnalyzerDriver(document, root.FullSpan, root, CancellationToken.None);
var expected = GetSemanticDiagnosticsAsync(providerId, provider, documentBasedDriver).WaitAndGetResult(documentBasedDriver.CancellationToken) ?? SpecializedCollections.EmptyEnumerable<DiagnosticData>();
Contract.Requires(diagnostics.SetEquals(expected));
#endif
}
}
}
}
......@@ -3,9 +3,9 @@
using System.Collections.Immutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal partial class DiagnosticAnalyzerService
internal partial class DiagnosticIncrementalAnalyzer
{
public class AnalysisData
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
internal partial class DiagnosticIncrementalAnalyzer
{
private partial class DiagnosticAnalyzersAndStates
{
/// <summary>
/// Maintains per-project diagnostic analyzers and the corresponding diagnostic states.
/// </summary>
private class ProjectAnalyzersAndStates
{
private readonly ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> _analyzerIdMap;
private readonly DiagnosticState[,] _diagnosticStateMaps;
private readonly int _startAnalyzerId;
private readonly int _analyzerCount;
private readonly string _projectLanguage;
public static ProjectAnalyzersAndStates CreateIfAnyAnalyzers(IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int sharedWorkspaceAnalyzersCount, string projectLanguage)
{
// Make sure we have at least one analyzer.
if (projectSpecificAnalyzers == null || !projectSpecificAnalyzers.Any())
{
return null;
}
return new ProjectAnalyzersAndStates(projectSpecificAnalyzers, sharedWorkspaceAnalyzersCount, projectLanguage);
}
private ProjectAnalyzersAndStates(IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int sharedWorkspaceAnalyzersCount, string projectLanguage)
{
Contract.ThrowIfFalse(projectSpecificAnalyzers != null && projectSpecificAnalyzers.Any());
_startAnalyzerId = sharedWorkspaceAnalyzersCount;
_projectLanguage = projectLanguage;
_analyzerIdMap = CreateAnalyzerIdMap(projectSpecificAnalyzers, _startAnalyzerId);
_analyzerCount = _analyzerIdMap.Values.Flatten().Count();
Contract.ThrowIfFalse(_analyzerCount > 0);
_diagnosticStateMaps = new DiagnosticState[s_stateTypeCount, _analyzerCount];
}
private static ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> CreateAnalyzerIdMap(
IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int startAnalyzerId)
{
var index = startAnalyzerId;
var map = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>>();
foreach (var analyzerList in projectSpecificAnalyzers)
{
var analyzerMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ProviderId>();
foreach (var analyzer in analyzerList.Value)
{
Contract.ThrowIfNull(analyzer);
analyzerMap.Add(analyzer, index++);
}
if (map.ContainsKey(analyzerList.Key))
{
map[analyzerList.Key] = map[analyzerList.Key].AddRange(analyzerMap);
}
else
{
map.Add(analyzerList.Key, analyzerMap.ToImmutable());
}
}
return map.ToImmutable();
}
public int AnalyzerCount
{
get
{
Contract.ThrowIfFalse(_analyzerCount > 0);
return _analyzerCount;
}
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates()
{
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var type in s_documentScopeStateTypes)
{
current = current.Concat(GetAllExistingDiagnosticStates(type));
}
return current;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(StateType type)
{
foreach (var analyzerAndId in _analyzerIdMap.Values.Flatten())
{
var state = _diagnosticStateMaps[(int)type, analyzerAndId.Value - _startAnalyzerId];
yield return Tuple.Create(state, analyzerAndId.Value, type);
}
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllProviderAndIds()
{
return _analyzerIdMap.Values.Flatten();
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider)
{
Contract.ThrowIfFalse(providerId >= _startAnalyzerId);
Contract.ThrowIfFalse(providerId < _startAnalyzerId + this.AnalyzerCount);
return DiagnosticAnalyzersAndStates.GetOrCreateDiagnosticState(_diagnosticStateMaps, stateType, providerId - _startAnalyzerId, providerId, provider, _projectLanguage);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId)
{
Contract.ThrowIfFalse(providerId >= _startAnalyzerId);
Contract.ThrowIfFalse(providerId < _startAnalyzerId + this.AnalyzerCount);
return _diagnosticStateMaps[(int)stateType, providerId - _startAnalyzerId];
}
public bool HasAnalyzer(DiagnosticAnalyzer analyzer)
{
Contract.ThrowIfNull(analyzer);
return _analyzerIdMap.Values.Any(dict => dict.ContainsKey(analyzer));
}
}
}
}
}
// 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.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal partial class DiagnosticAnalyzerService
internal partial class DiagnosticIncrementalAnalyzer
{
public enum StateType : int
{
......
......@@ -6,74 +6,58 @@
using System.Linq;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
internal partial class DiagnosticAnalyzerService
internal partial class DiagnosticIncrementalAnalyzer
{
internal partial class DiagnosticIncrementalAnalyzer
private partial class DiagnosticAnalyzersAndStates
{
private partial class DiagnosticAnalyzersAndStates
private partial class WorkspaceAnalyzersAndStates
{
/// <summary>
/// Maintains per-project diagnostic analyzers and the corresponding diagnostic states.
/// Maintains all workspace diagnostic analyzers (with diagnostic states) for a specific language.
/// </summary>
private class ProjectAnalyzersAndStates
private class PerLanguageAnalyzersAndStates
{
private readonly ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> _analyzerIdMap;
private readonly DiagnosticState[,] _diagnosticStateMaps;
private readonly int _startAnalyzerId;
private readonly string _language;
private readonly ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> _diagnosticAnalyzerIdMap;
private readonly int _analyzerCount;
private readonly string _projectLanguage;
public static ProjectAnalyzersAndStates CreateIfAnyAnalyzers(IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int sharedWorkspaceAnalyzersCount, string projectLanguage)
{
// Make sure we have at least one analyzer.
if (projectSpecificAnalyzers == null || !projectSpecificAnalyzers.Any())
{
return null;
}
return new ProjectAnalyzersAndStates(projectSpecificAnalyzers, sharedWorkspaceAnalyzersCount, projectLanguage);
}
private readonly DiagnosticState[,] _diagnosticStateMaps;
private ProjectAnalyzersAndStates(IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int sharedWorkspaceAnalyzersCount, string projectLanguage)
public PerLanguageAnalyzersAndStates(AnalyzerManager analyzerManager, string language)
{
Contract.ThrowIfFalse(projectSpecificAnalyzers != null && projectSpecificAnalyzers.Any());
_startAnalyzerId = sharedWorkspaceAnalyzersCount;
_projectLanguage = projectLanguage;
_analyzerIdMap = CreateAnalyzerIdMap(projectSpecificAnalyzers, _startAnalyzerId);
_analyzerCount = _analyzerIdMap.Values.Flatten().Count();
Contract.ThrowIfFalse(_analyzerCount > 0);
_language = language;
// TODO: dynamically re-order providers so that cheap one runs first and slower runs later.
_diagnosticAnalyzerIdMap = CreateAnalyzerIdMap(analyzerManager, language);
_analyzerCount = _diagnosticAnalyzerIdMap.Values.Flatten().Count();
_diagnosticStateMaps = new DiagnosticState[s_stateTypeCount, _analyzerCount];
}
private static ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> CreateAnalyzerIdMap(
IEnumerable<KeyValuePair<string, IEnumerable<DiagnosticAnalyzer>>> projectSpecificAnalyzers, int startAnalyzerId)
AnalyzerManager analyzerManager, string language)
{
var index = startAnalyzerId;
var index = 0;
var map = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>>();
foreach (var analyzerList in projectSpecificAnalyzers)
foreach (var kv in analyzerManager.GetHostDiagnosticAnalyzersPerReference(language))
{
var analyzerMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ProviderId>();
var perAnalyzerMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ProviderId>();
foreach (var analyzer in analyzerList.Value)
{
Contract.ThrowIfNull(analyzer);
analyzerMap.Add(analyzer, index++);
}
var referenceIdentity = kv.Key;
var perLanguageAnalyzers = kv.Value;
if (map.ContainsKey(analyzerList.Key))
if (perLanguageAnalyzers.Length > 0)
{
map[analyzerList.Key] = map[analyzerList.Key].AddRange(analyzerMap);
}
else
{
map.Add(analyzerList.Key, analyzerMap.ToImmutable());
foreach (var analyzer in perLanguageAnalyzers)
{
var analyzerId = index++;
perAnalyzerMap.Add(analyzer, analyzerId);
}
map.Add(referenceIdentity, perAnalyzerMap.ToImmutable());
}
}
......@@ -82,58 +66,58 @@ private ProjectAnalyzersAndStates(IEnumerable<KeyValuePair<string, IEnumerable<D
public int AnalyzerCount
{
get
{
Contract.ThrowIfFalse(_analyzerCount > 0);
return _analyzerCount;
}
get { return _analyzerCount; }
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates()
public bool HasAnalyzerReference(AnalyzerReference analyzerReference)
{
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var type in s_documentScopeStateTypes)
Contract.ThrowIfNull(analyzerReference);
if (analyzerReference is AnalyzerFileReference)
{
current = current.Concat(GetAllExistingDiagnosticStates(type));
// Filter out duplicate analyzer references with same assembly name/full path.
return analyzerReference.Display != null && _diagnosticAnalyzerIdMap.ContainsKey(analyzerReference.Display);
}
else
{
// For non-file references, we will check individual DiagnosticAnalyzer instances for duplicates.
return false;
}
}
return current;
public bool HasAnalyzer(DiagnosticAnalyzer analyzer)
{
Contract.ThrowIfNull(analyzer);
return _diagnosticAnalyzerIdMap.Values.Any(dict => dict.ContainsKey(analyzer));
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(StateType type)
{
foreach (var analyzerAndId in _analyzerIdMap.Values.Flatten())
foreach (var analyzerAndId in _diagnosticAnalyzerIdMap.Values.Flatten())
{
var state = _diagnosticStateMaps[(int)type, analyzerAndId.Value - _startAnalyzerId];
yield return Tuple.Create(state, analyzerAndId.Value, type);
yield return Tuple.Create(_diagnosticStateMaps[(int)type, analyzerAndId.Value], analyzerAndId.Value, type);
}
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllProviderAndIds()
{
return _analyzerIdMap.Values.Flatten();
return _diagnosticAnalyzerIdMap.Values.Flatten();
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider)
{
Contract.ThrowIfFalse(providerId >= _startAnalyzerId);
Contract.ThrowIfFalse(providerId < _startAnalyzerId + this.AnalyzerCount);
Contract.ThrowIfFalse(providerId >= 0);
Contract.ThrowIfFalse(providerId < this.AnalyzerCount);
return DiagnosticAnalyzersAndStates.GetOrCreateDiagnosticState(_diagnosticStateMaps, stateType, providerId - _startAnalyzerId, providerId, provider, _projectLanguage);
return DiagnosticAnalyzersAndStates.GetOrCreateDiagnosticState(_diagnosticStateMaps, stateType, providerId, providerId, provider, _language);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId)
{
Contract.ThrowIfFalse(providerId >= _startAnalyzerId);
Contract.ThrowIfFalse(providerId < _startAnalyzerId + this.AnalyzerCount);
return _diagnosticStateMaps[(int)stateType, providerId - _startAnalyzerId];
}
Contract.ThrowIfFalse(providerId >= 0);
Contract.ThrowIfFalse(providerId < this.AnalyzerCount);
public bool HasAnalyzer(DiagnosticAnalyzer analyzer)
{
Contract.ThrowIfNull(analyzer);
return _analyzerIdMap.Values.Any(dict => dict.ContainsKey(analyzer));
return _diagnosticStateMaps[(int)stateType, providerId];
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
internal partial class DiagnosticIncrementalAnalyzer
{
private partial class DiagnosticAnalyzersAndStates
{
/// <summary>
/// Maintains all workspace diagnostic analyzers (with diagnostic states), which are enabled for all projects in the workspace.
/// </summary>
private partial class WorkspaceAnalyzersAndStates
{
private readonly AnalyzerManager _analyzerManager;
private ImmutableDictionary<string, PerLanguageAnalyzersAndStates> _perLanguageAnalyzersAndStatesMap;
public WorkspaceAnalyzersAndStates(AnalyzerManager analyzerManager)
{
_analyzerManager = analyzerManager;
_perLanguageAnalyzersAndStatesMap = ImmutableDictionary<string, PerLanguageAnalyzersAndStates>.Empty;
}
private static PerLanguageAnalyzersAndStates CreatePerLanguageAnalyzersAndStates(string language, WorkspaceAnalyzersAndStates @this)
{
return new PerLanguageAnalyzersAndStates(@this._analyzerManager, language);
}
private PerLanguageAnalyzersAndStates GetOrCreatePerLanguageAnalyzersAndStates(string language)
{
return ImmutableInterlocked.GetOrAdd(ref _perLanguageAnalyzersAndStatesMap, language, CreatePerLanguageAnalyzersAndStates, this);
}
public int GetAnalyzerCount(string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.AnalyzerCount;
}
public bool HasAnalyzerReference(AnalyzerReference analyzerReference, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.HasAnalyzerReference(analyzerReference);
}
public bool HasAnalyzer(DiagnosticAnalyzer analyzer, string language)
{
if (analyzer == null)
{
return false;
}
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.HasAnalyzer(analyzer);
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(string languageOpt)
{
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var type in s_documentScopeStateTypes)
{
current = current.Concat(GetAllExistingDiagnosticStates(type, languageOpt));
}
return current;
}
public IEnumerable<Tuple<DiagnosticState, ProviderId, StateType>> GetAllExistingDiagnosticStates(StateType type, string languageOpt)
{
if (languageOpt != null)
{
PerLanguageAnalyzersAndStates analyzersStates;
if (_perLanguageAnalyzersAndStatesMap.TryGetValue(languageOpt, out analyzersStates))
{
return analyzersStates.GetAllExistingDiagnosticStates(type);
}
else
{
return SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
}
}
// This might be a removed or closed document/project/solution.
// Return all existing states.
var current = SpecializedCollections.EmptyEnumerable<Tuple<DiagnosticState, ProviderId, StateType>>();
foreach (var analyzersAndStates in _perLanguageAnalyzersAndStatesMap.Values)
{
current = current.Concat(analyzersAndStates.GetAllExistingDiagnosticStates(type));
}
return current;
}
public IEnumerable<KeyValuePair<DiagnosticAnalyzer, ProviderId>> GetAllProviderAndIds(string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetAllProviderAndIds();
}
public DiagnosticState GetOrCreateDiagnosticState(StateType stateType, ProviderId providerId, DiagnosticAnalyzer provider, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetOrCreateDiagnosticState(stateType, providerId, provider);
}
public DiagnosticState GetDiagnosticState(StateType stateType, ProviderId providerId, string language)
{
var analyzersAndStates = this.GetOrCreatePerLanguageAnalyzersAndStates(language);
return analyzersAndStates.GetDiagnosticState(stateType, providerId);
}
}
}
}
}
......@@ -5,7 +5,7 @@
using System.Threading;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
internal interface ISyntaxNodeAnalyzerService : ILanguageService
{
......
......@@ -11,7 +11,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.EngineV1
{
using ProviderId = Int32;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Host;
......
......@@ -54,7 +54,7 @@ internal interface IDiagnosticAnalyzerService
/// it will return true if it was able to return all up-to-date diagnostics.
/// otherwise, false indicating there are some missing diagnostics in the diagnostic list
/// </summary>
Task<bool> TryGetDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken);
Task<bool> TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, CancellationToken cancellationToken);
/// <summary>
/// return up to date diagnostics for the given span for the document
......
......@@ -2,7 +2,7 @@
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal static class InternalDiagnosticsOptions
{
......@@ -10,5 +10,8 @@ internal static class InternalDiagnosticsOptions
[ExportOption]
public static readonly Option<bool> BlueSquiggleForBuildDiagnostic = new Option<bool>(OptionName, "Blue Squiggle For Build Diagnostic", defaultValue: false);
[ExportOption]
public static readonly Option<bool> UseDiagnosticEngineV2 = new Option<bool>(OptionName, "Use Diagnostic Engine V2", defaultValue: false);
}
}
......@@ -8,7 +8,7 @@
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.Log
{
internal class DiagnosticAnalyzerLogger
{
......
......@@ -5,7 +5,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Internal.Log;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.Log
{
internal class DiagnosticLogAggregator : LogAggregator
{
......
......@@ -2,7 +2,7 @@
using Microsoft.CodeAnalysis.Internal.Log;
namespace Microsoft.CodeAnalysis.Diagnostics
namespace Microsoft.CodeAnalysis.Diagnostics.Log
{
internal static class DiagnosticLogger
{
......
此差异已折叠。
// 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.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.EngineV1;
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
......
......@@ -247,7 +247,7 @@ public static void LogIncrementalAnalyzerProcessorStatistics(int correlationId,
foreach (var analyzer in analyzers)
{
var diagIncrementalAnalyzer = analyzer as DiagnosticAnalyzerService.DiagnosticIncrementalAnalyzer;
var diagIncrementalAnalyzer = analyzer as BaseDiagnosticIncrementalAnalyzer;
if (diagIncrementalAnalyzer != null)
{
diagIncrementalAnalyzer.LogAnalyzerCountSummary();
......
......@@ -138,6 +138,14 @@ private void OnOptionChanged(object sender, OptionChangedEventArgs e)
return;
}
// TODO: remove this once prototype is done
// it is here just because it was convenient to add per workspace option change monitoring
// for incremental analyzer
if (e.Option == Diagnostics.InternalDiagnosticsOptions.UseDiagnosticEngineV2)
{
_documentAndProjectWorkerProcessor.ChangeDiagnosticsEngine((bool)e.Value);
}
ReanalyzeOnOptionChange(sender, e);
}
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Diagnostics.EngineV1
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics
......
......@@ -6,6 +6,7 @@
using System.Windows.Documents;
using System.Windows.Navigation;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Text.Differencing;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册