提交 2e438ae3 编写于 作者: H Heejae Chang

added error source support

now, error list will have column (which is off by default) that shows source of each errors in a
format "assembly name" for analyzers node. and "assembly name" [vsix name] for vsix
上级 d4d0b1d5
......@@ -212,10 +212,9 @@ class Test
}
}
private class LiveId : UpdateArgsId
private class LiveId : ISupportLiveUpdate
{
// use just a random analyzer
public LiveId() : base(new CSharpSimplifyTypeNamesDiagnosticAnalyzer())
public LiveId()
{
}
}
......
......@@ -111,7 +111,7 @@ public void AppendIntersectingSpans(int start, int length, IntervalIntrospector
}
// only follow minimum length for live diagnostic. otherwise, let it be zero length.
var minimumLegnth = _id is UpdateArgsId ? _owner.MinimumLength : 0;
var minimumLegnth = _id is ISupportLiveUpdate ? _owner.MinimumLength : 0;
foreach (var data in result)
{
......
......@@ -94,7 +94,7 @@ private static DiagnosticData MakeDiagnosticData(ProjectId projectId, Document d
DebuggingSession session, object errorId, Workspace workspace, Solution solution, ProjectId projectId, DocumentId documentId, ImmutableArray<DiagnosticData> items)
{
return new DiagnosticsUpdatedArgs(
id: Tuple.Create(session, errorId),
id: new EnCId(session, errorId),
workspace: workspace,
solution: solution,
projectId: projectId,
......@@ -110,5 +110,18 @@ private void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
updated(this, args);
}
}
private class EnCId : ErrorSourceId.Base<DebuggingSession, object>
{
public EnCId(DebuggingSession session, object errorId) :
base(session, errorId)
{
}
public override string ErrorSource
{
get { return PredefinedErrorSources.EnC; }
}
}
}
}
......@@ -30,7 +30,7 @@ internal TestDiagnosticAnalyzerService(ImmutableDictionary<string, ImmutableArra
}
internal TestDiagnosticAnalyzerService(AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null)
: base(workspaceAnalyzerAssemblies: SpecializedCollections.EmptyEnumerable<string>(), hostDiagnosticUpdateSource: hostDiagnosticUpdateSource)
: base(SpecializedCollections.EmptyEnumerable<HostDiagnosticAnalyzerPackage>(), hostDiagnosticUpdateSource)
{
_onAnalyzerException = onAnalyzerException;
}
......
// 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.ComponentModel.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
......@@ -11,12 +12,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Preview
[Export(typeof(IWorkspaceDiagnosticAnalyzerProviderService))]
internal class TestOnly_CompilerDiagnosticAnalyzerProviderService : IWorkspaceDiagnosticAnalyzerProviderService
{
private readonly IEnumerable<string> _compilerAnalyzerAssemblies;
private readonly HostDiagnosticAnalyzerPackage _info;
[ImportingConstructor]
public TestOnly_CompilerDiagnosticAnalyzerProviderService()
{
_compilerAnalyzerAssemblies = GetCompilerAnalyzerAssemblies().Distinct();
_info = new HostDiagnosticAnalyzerPackage("Compiler", GetCompilerAnalyzerAssemblies().Distinct().ToImmutableArray());
}
private static IEnumerable<string> GetCompilerAnalyzerAssemblies()
......@@ -31,9 +32,9 @@ private static IEnumerable<string> GetCompilerAnalyzerAssemblies()
}
}
public IEnumerable<string> GetWorkspaceAnalyzerAssemblies()
public IEnumerable<HostDiagnosticAnalyzerPackage> GetHostDiagnosticAnalyzerPackages()
{
return _compilerAnalyzerAssemblies;
yield return _info;
}
}
}
......@@ -124,7 +124,7 @@ private void ClearAnalyzerDiagnostics(DiagnosticAnalyzer analyzer, ProjectId pro
private DiagnosticsUpdatedArgs MakeArgs(DiagnosticAnalyzer analyzer, ImmutableHashSet<DiagnosticData> items, Project project)
{
return new DiagnosticsUpdatedArgs(
id: Tuple.Create(this, analyzer, project?.Id),
id: new HostArgsId(this, analyzer, project?.Id),
workspace: this.Workspace,
solution: project?.Solution,
projectId: project?.Id,
......@@ -142,5 +142,33 @@ internal ImmutableHashSet<DiagnosticData> TestOnly_GetReportedDiagnostics(Diagno
return diagnostics;
}
private class HostArgsId : AnalyzerUpdateArgsId
{
private readonly AbstractHostDiagnosticUpdateSource _source;
private readonly ProjectId _projectId;
public HostArgsId(AbstractHostDiagnosticUpdateSource source, DiagnosticAnalyzer analyzer, ProjectId id) : base(analyzer)
{
this._source = source;
this._projectId = id;
}
public override bool Equals(object obj)
{
var other = obj as HostArgsId;
if (other == null)
{
return false;
}
return _source == other._source && _projectId == other._projectId && base.Equals(obj);
}
public override int GetHashCode()
{
return Hash.Combine(_source.GetHashCode(), Hash.Combine(_projectId.GetHashCode(), base.GetHashCode()));
}
}
}
}
......@@ -43,6 +43,12 @@ public static bool IsCompilerAnalyzer(this DiagnosticAnalyzer analyzer)
return ValueTuple.Create(GetAssemblyQualifiedNameWithoutVersion(type), GetAnalyzerVersion(type.Assembly.Location));
}
public static string GetAnalyzerAssemblyName(this DiagnosticAnalyzer analyzer)
{
var type = analyzer.GetType();
return type.Assembly.GetName().Name;
}
private static string GetAssemblyQualifiedNameWithoutVersion(Type type)
{
var name = type.AssemblyQualifiedName;
......
......@@ -5,29 +5,26 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// <summary>
/// Base type of a type that is used as <see cref="DiagnosticsUpdatedArgs.Id"/> for live diagnostic
/// </summary>
internal class UpdateArgsId
internal class AnalyzerUpdateArgsId : ErrorSourceId.Base<DiagnosticAnalyzer>, ISupportLiveUpdate
{
public readonly DiagnosticAnalyzer Analyzer;
public DiagnosticAnalyzer Analyzer => _Field1;
protected UpdateArgsId(DiagnosticAnalyzer analyzer)
protected AnalyzerUpdateArgsId(DiagnosticAnalyzer analyzer) :
base(analyzer)
{
Analyzer = analyzer;
}
public override bool Equals(object obj)
public override string ErrorSource
{
var other = obj as UpdateArgsId;
if (other == null)
get
{
return false;
}
return Analyzer == other.Analyzer;
}
if (Analyzer == null)
{
return string.Empty;
}
public override int GetHashCode()
{
return Analyzer == null ? 0 : Analyzer.GetHashCode();
return Analyzer.GetAnalyzerAssemblyName();
}
}
}
}
......@@ -27,10 +27,8 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
[ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners,
[Import(AllowDefault = true)]IWorkspaceDiagnosticAnalyzerProviderService diagnosticAnalyzerProviderService = null,
[Import(AllowDefault = true)]AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource = null)
: this(workspaceAnalyzerAssemblies: diagnosticAnalyzerProviderService != null ?
diagnosticAnalyzerProviderService.GetWorkspaceAnalyzerAssemblies() :
SpecializedCollections.EmptyEnumerable<string>(),
hostDiagnosticUpdateSource: hostDiagnosticUpdateSource)
: this(diagnosticAnalyzerProviderService != null ? diagnosticAnalyzerProviderService.GetHostDiagnosticAnalyzerPackages() : SpecializedCollections.EmptyEnumerable<HostDiagnosticAnalyzerPackage>(),
hostDiagnosticUpdateSource)
{
_listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.DiagnosticService);
}
......@@ -38,9 +36,9 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
public IAsynchronousOperationListener Listener => _listener;
// protected for testing purposes.
protected DiagnosticAnalyzerService(IEnumerable<string> workspaceAnalyzerAssemblies, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) : this()
protected DiagnosticAnalyzerService(IEnumerable<HostDiagnosticAnalyzerPackage> workspaceAnalyzerPackages, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) : this()
{
_hostAnalyzerManager = new HostAnalyzerManager(workspaceAnalyzerAssemblies, hostDiagnosticUpdateSource);
_hostAnalyzerManager = new HostAnalyzerManager(workspaceAnalyzerPackages, hostDiagnosticUpdateSource);
_hostDiagnosticUpdateSource = hostDiagnosticUpdateSource;
}
......@@ -174,6 +172,16 @@ public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
return _hostAnalyzerManager.IsCompilerDiagnostic(language, diagnostic);
}
public DiagnosticAnalyzer GetCompilerDiagnosticAnalyzer(string language)
{
return _hostAnalyzerManager.GetCompilerDiagnosticAnalyzer(language);
}
public bool IsCompilerDiagnosticAnalyzer(string language, DiagnosticAnalyzer analyzer)
{
return _hostAnalyzerManager.IsCompilerDiagnosticAnalyzer(language, analyzer);
}
// virtual for testing purposes.
internal virtual Action<Exception, DiagnosticAnalyzer, Diagnostic> GetOnAnalyzerException(ProjectId projectId, DiagnosticLogAggregator diagnosticLogAggregator)
{
......
// 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 Roslyn.Utilities;
......@@ -81,14 +82,33 @@ public VersionArgument(VersionStamp textVersion, VersionStamp dataVersion, Versi
}
}
public class ArgumentKey : UpdateArgsId
public class HostAnalyzerKey : ArgumentKey
{
private readonly string _analyzerPackageName;
public HostAnalyzerKey(DiagnosticAnalyzer analyzer, StateType stateType, object key, string analyzerPackageName) :
base(analyzer, stateType, key)
{
_analyzerPackageName = analyzerPackageName;
}
public override string ErrorSource
{
get
{
return _analyzerPackageName;
}
}
}
public class ArgumentKey : AnalyzerUpdateArgsId
{
public readonly StateType StateType;
public readonly object Key;
public ArgumentKey(DiagnosticAnalyzer analyzer, StateType stateTypeId, object key) : base(analyzer)
public ArgumentKey(DiagnosticAnalyzer analyzer, StateType stateType, object key) : base(analyzer)
{
this.StateType = stateTypeId;
this.StateType = stateType;
this.Key = key;
}
......@@ -100,12 +120,12 @@ public override bool Equals(object obj)
return false;
}
return base.Equals(obj) && StateType == other.StateType && Key == other.Key;
return StateType == other.StateType && Key == other.Key && base.Equals(obj);
}
public override int GetHashCode()
{
return Hash.Combine(Key, Hash.Combine(base.GetHashCode(), (int)StateType));
return Hash.Combine(Key, Hash.Combine((int)StateType, base.GetHashCode()));
}
}
}
......
......@@ -58,7 +58,7 @@ public StateSet GetOrCreateStateSet(string language, DiagnosticAnalyzer analyzer
{
var analyzersPerReference = _owner.AnalyzerManager.GetHostDiagnosticAnalyzersPerReference(language);
var analyzerMap = CreateAnalyzerMap(language, analyzersPerReference.Values);
var analyzerMap = CreateAnalyzerMap(_owner.AnalyzerManager, language, analyzersPerReference.Values);
VerifyDiagnosticStates(analyzerMap.Values);
return analyzerMap;
......
......@@ -78,7 +78,7 @@ public void RemoveStateSet(ProjectId projectId)
}
var newAnalyzersPerReference = _owner.AnalyzerManager.CreateProjectDiagnosticAnalyzersPerReference(project);
var newMap = StateManager.CreateAnalyzerMap(project.Language, newAnalyzersPerReference.Values);
var newMap = StateManager.CreateAnalyzerMap(_owner.AnalyzerManager, project.Language, newAnalyzersPerReference.Values);
RaiseProjectAnalyzerReferenceChangedIfNeeded(project, newAnalyzersPerReference, newMap);
......@@ -135,7 +135,7 @@ public void RemoveStateSet(ProjectId projectId)
return ImmutableDictionary<DiagnosticAnalyzer, StateSet>.Empty;
}
return StateManager.CreateAnalyzerMap(project.Language, analyzersPerReference.Values);
return StateManager.CreateAnalyzerMap(_owner.AnalyzerManager, project.Language, analyzersPerReference.Values);
}
private void RaiseProjectAnalyzerReferenceChangedIfNeeded(
......
......@@ -112,8 +112,10 @@ private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChange
}
private static ImmutableDictionary<DiagnosticAnalyzer, StateSet> CreateAnalyzerMap(
string language, IEnumerable<ImmutableArray<DiagnosticAnalyzer>> analyzerCollection)
HostAnalyzerManager analyzerManager, string language, IEnumerable<ImmutableArray<DiagnosticAnalyzer>> analyzerCollection)
{
var compilerAnalyzer = analyzerManager.GetCompilerDiagnosticAnalyzer(language);
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, StateSet>();
foreach (var analyzers in analyzerCollection)
{
......@@ -128,13 +130,27 @@ private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChange
continue;
}
builder.Add(analyzer, new StateSet(language, analyzer));
var errorSourceName = analyzer == compilerAnalyzer ?
PredefinedErrorSources.Compiler : GetErrorSourceName(analyzerManager, language, analyzer);
builder.Add(analyzer, new StateSet(language, analyzer, errorSourceName));
}
}
return builder.ToImmutable();
}
private static string GetErrorSourceName(HostAnalyzerManager analyzerManager, string language, DiagnosticAnalyzer analyzer)
{
var packageName = analyzerManager.GetDiagnosticAnalyzerPackageName(language, analyzer);
if (packageName == null)
{
return null;
}
return $"{analyzer.GetAnalyzerAssemblyName()} [{packageName}]";
}
[Conditional("DEBUG")]
private static void VerifyDiagnosticStates(IEnumerable<StateSet> stateSets)
{
......
......@@ -17,17 +17,20 @@ private class StateSet
private readonly string _language;
private readonly DiagnosticAnalyzer _analyzer;
private readonly string _errorSourceName;
private readonly DiagnosticState[] _state;
public StateSet(string language, DiagnosticAnalyzer analyzer)
public StateSet(string language, DiagnosticAnalyzer analyzer, string errorSourceName)
{
_language = language;
_analyzer = analyzer;
_errorSourceName = errorSourceName;
_state = CreateDiagnosticStates(language, analyzer);
}
public string ErrorSourceName => _errorSourceName;
public string Language => _language;
public DiagnosticAnalyzer Analyzer => _analyzer;
......
......@@ -138,14 +138,14 @@ private async Task AnalyzeSyntaxAsync(Document document, ImmutableHashSet<string
var data = await _executor.GetSyntaxAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false);
if (data.FromCache)
{
RaiseDiagnosticsUpdated(StateType.Syntax, document.Id, stateSet.Analyzer, new SolutionArgument(document), data.Items);
RaiseDiagnosticsUpdated(StateType.Syntax, document.Id, stateSet, new SolutionArgument(document), data.Items);
continue;
}
var state = stateSet.GetState(StateType.Syntax);
await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false);
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Syntax, document, stateSet.Analyzer, data.OldItems, data.Items);
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Syntax, document, stateSet, data.OldItems, data.Items);
}
}
}
......@@ -224,11 +224,11 @@ private async Task AnalyzeBodyDocumentAsync(Document document, SyntaxNode member
if (data.FromCache)
{
RaiseDiagnosticsUpdated(StateType.Document, document.Id, stateSet.Analyzer, new SolutionArgument(document), data.Items);
RaiseDiagnosticsUpdated(StateType.Document, document.Id, stateSet, new SolutionArgument(document), data.Items);
continue;
}
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet.Analyzer, data.OldItems, data.Items);
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items);
}
}
}
......@@ -260,7 +260,7 @@ private async Task AnalyzeDocumentAsync(Document document, VersionArgument versi
var data = await _executor.GetDocumentAnalysisDataAsync(userDiagnosticDriver, stateSet, versions).ConfigureAwait(false);
if (data.FromCache)
{
RaiseDiagnosticsUpdated(StateType.Document, document.Id, stateSet.Analyzer, new SolutionArgument(document), data.Items);
RaiseDiagnosticsUpdated(StateType.Document, document.Id, stateSet, new SolutionArgument(document), data.Items);
continue;
}
......@@ -272,7 +272,7 @@ private async Task AnalyzeDocumentAsync(Document document, VersionArgument versi
var state = stateSet.GetState(StateType.Document);
await state.PersistAsync(document, data.ToPersistData(), cancellationToken).ConfigureAwait(false);
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet.Analyzer, data.OldItems, data.Items);
RaiseDocumentDiagnosticsUpdatedIfNeeded(StateType.Document, document, stateSet, data.OldItems, data.Items);
}
}
}
......@@ -314,14 +314,14 @@ private async Task AnalyzeProjectAsync(Project project, ImmutableHashSet<string>
var data = await _executor.GetProjectAnalysisDataAsync(analyzerDriver, stateSet, versions).ConfigureAwait(false);
if (data.FromCache)
{
RaiseProjectDiagnosticsUpdated(project, stateSet.Analyzer, data.Items);
RaiseProjectDiagnosticsUpdated(project, stateSet, data.Items);
continue;
}
var state = stateSet.GetState(StateType.Project);
await PersistProjectData(project, state, data).ConfigureAwait(false);
RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet.Analyzer, data.OldItems, data.Items);
RaiseProjectDiagnosticsUpdatedIfNeeded(project, stateSet, data.OldItems, data.Items);
}
}
}
......@@ -385,7 +385,7 @@ public override void RemoveDocument(DocumentId documentId)
var solutionArgs = new SolutionArgument(null, documentId.ProjectId, documentId);
for (var stateType = 0; stateType < s_stateTypeCount; stateType++)
{
RaiseDiagnosticsUpdated((StateType)stateType, documentId, stateSet.Analyzer, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated((StateType)stateType, documentId, stateSet, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
}
}
}
......@@ -400,7 +400,7 @@ public override void RemoveProject(ProjectId projectId)
stateSet.Remove(projectId);
var solutionArgs = new SolutionArgument(null, projectId, null);
RaiseDiagnosticsUpdated(StateType.Project, projectId, stateSet.Analyzer, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated(StateType.Project, projectId, stateSet, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
}
}
......@@ -517,7 +517,7 @@ private static bool CheckSemanticVersions(Project project, AnalysisData existing
}
private void RaiseDocumentDiagnosticsUpdatedIfNeeded(
StateType type, Document document, DiagnosticAnalyzer analyzer, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
StateType type, Document document, StateSet stateSet, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
{
var noItems = existingItems.Length == 0 && newItems.Length == 0;
if (noItems)
......@@ -525,11 +525,11 @@ private static bool CheckSemanticVersions(Project project, AnalysisData existing
return;
}
RaiseDiagnosticsUpdated(type, document.Id, analyzer, new SolutionArgument(document), newItems);
RaiseDiagnosticsUpdated(type, document.Id, stateSet, new SolutionArgument(document), newItems);
}
private void RaiseProjectDiagnosticsUpdatedIfNeeded(
Project project, DiagnosticAnalyzer analyzer, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
Project project, StateSet stateSet, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
{
var noItems = existingItems.Length == 0 && newItems.Length == 0;
if (noItems)
......@@ -537,12 +537,12 @@ private static bool CheckSemanticVersions(Project project, AnalysisData existing
return;
}
RaiseProjectDiagnosticsRemovedIfNeeded(project, analyzer, existingItems, newItems);
RaiseProjectDiagnosticsUpdated(project, analyzer, newItems);
RaiseProjectDiagnosticsRemovedIfNeeded(project, stateSet, existingItems, newItems);
RaiseProjectDiagnosticsUpdated(project, stateSet, newItems);
}
private void RaiseProjectDiagnosticsRemovedIfNeeded(
Project project, DiagnosticAnalyzer analyzer, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
Project project, StateSet stateSet, ImmutableArray<DiagnosticData> existingItems, ImmutableArray<DiagnosticData> newItems)
{
if (existingItems.Length == 0)
{
......@@ -554,28 +554,28 @@ private static bool CheckSemanticVersions(Project project, AnalysisData existing
{
if (documentId == null)
{
RaiseDiagnosticsUpdated(StateType.Project, project.Id, analyzer, new SolutionArgument(project), ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated(StateType.Project, project.Id, stateSet, new SolutionArgument(project), ImmutableArray<DiagnosticData>.Empty);
continue;
}
var document = project.GetDocument(documentId);
var argument = documentId == null ? new SolutionArgument(null, documentId.ProjectId, documentId) : new SolutionArgument(document);
RaiseDiagnosticsUpdated(StateType.Project, documentId, analyzer, argument, ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated(StateType.Project, documentId, stateSet, argument, ImmutableArray<DiagnosticData>.Empty);
}
}
private void RaiseProjectDiagnosticsUpdated(Project project, DiagnosticAnalyzer analyzer, ImmutableArray<DiagnosticData> diagnostics)
private void RaiseProjectDiagnosticsUpdated(Project project, StateSet stateSet, ImmutableArray<DiagnosticData> diagnostics)
{
var group = diagnostics.GroupBy(d => d.DocumentId);
foreach (var kv in group)
{
if (kv.Key == null)
{
RaiseDiagnosticsUpdated(StateType.Project, project.Id, analyzer, new SolutionArgument(project), kv.ToImmutableArrayOrEmpty());
RaiseDiagnosticsUpdated(StateType.Project, project.Id, stateSet, new SolutionArgument(project), kv.ToImmutableArrayOrEmpty());
continue;
}
RaiseDiagnosticsUpdated(StateType.Project, kv.Key, analyzer, new SolutionArgument(project.GetDocument(kv.Key)), kv.ToImmutableArrayOrEmpty());
RaiseDiagnosticsUpdated(StateType.Project, kv.Key, stateSet, new SolutionArgument(project.GetDocument(kv.Key)), kv.ToImmutableArrayOrEmpty());
}
}
......@@ -585,14 +585,17 @@ private static ImmutableArray<DiagnosticData> GetDiagnosticData(ILookup<Document
}
private void RaiseDiagnosticsUpdated(
StateType type, object key, DiagnosticAnalyzer analyzer, SolutionArgument solution, ImmutableArray<DiagnosticData> diagnostics)
StateType type, object key, StateSet stateSet, SolutionArgument solution, ImmutableArray<DiagnosticData> diagnostics)
{
if (Owner == null)
{
return;
}
var id = new ArgumentKey(analyzer, type, key);
// get right arg id for the given analyzer
var id = stateSet.ErrorSourceName != null ?
(object)new HostAnalyzerKey(stateSet.Analyzer, type, key, stateSet.ErrorSourceName) : (object)new ArgumentKey(stateSet.Analyzer, type, key);
Owner.RaiseDiagnosticsUpdated(this,
new DiagnosticsUpdatedArgs(id, Workspace, solution.Solution, solution.ProjectId, solution.DocumentId, diagnostics));
}
......@@ -815,13 +818,15 @@ private void ClearDocumentStates(Document document, IEnumerable<StateSet> states
for (var stateType = 0; stateType < s_stateTypeCount; stateType++)
{
cancellationToken.ThrowIfCancellationRequested();
ClearDocumentState(document, state.Analyzer, (StateType)stateType, state.GetState((StateType)stateType), raiseEvent);
ClearDocumentState(document, state, (StateType)stateType, raiseEvent);
}
}
}
private void ClearDocumentState(Document document, DiagnosticAnalyzer analyzer, StateType type, DiagnosticState state, bool raiseEvent)
private void ClearDocumentState(Document document, StateSet stateSet, StateType type, bool raiseEvent)
{
var state = stateSet.GetState(type);
// remove saved info
state.Remove(document.Id);
......@@ -831,7 +836,7 @@ private void ClearDocumentState(Document document, DiagnosticAnalyzer analyzer,
var documentId = document.Id;
var solutionArgs = new SolutionArgument(document);
RaiseDiagnosticsUpdated(type, document.Id, analyzer, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated(type, document.Id, stateSet, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
}
}
......@@ -842,21 +847,23 @@ private void ClearProjectStatesAsync(Project project, IEnumerable<StateSet> stat
ClearDocumentStates(document, states, raiseEvent: true, cancellationToken: cancellationToken);
}
foreach (var state in states)
foreach (var stateSet in states)
{
cancellationToken.ThrowIfCancellationRequested();
ClearProjectState(project, state.Analyzer, state.GetState(StateType.Project));
ClearProjectState(project, stateSet);
}
}
private void ClearProjectState(Project project, DiagnosticAnalyzer analyzer, DiagnosticState state)
private void ClearProjectState(Project project, StateSet stateSet)
{
var state = stateSet.GetState(StateType.Project);
// remove saved cache
state.Remove(project.Id);
// raise diagnostic updated event
var solutionArgs = new SolutionArgument(project);
RaiseDiagnosticsUpdated(StateType.Project, project.Id, analyzer, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
RaiseDiagnosticsUpdated(StateType.Project, project.Id, stateSet, solutionArgs, ImmutableArray<DiagnosticData>.Empty);
}
private async Task HandleSuppressedAnalyzerAsync(Document document, StateSet stateSet, StateType type, CancellationToken cancellationToken)
......@@ -865,7 +872,7 @@ private async Task HandleSuppressedAnalyzerAsync(Document document, StateSet sta
var existingData = await state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (existingData?.Items.Length > 0)
{
ClearDocumentState(document, stateSet.Analyzer, type, state, raiseEvent: true);
ClearDocumentState(document, stateSet, type, raiseEvent: true);
}
}
......@@ -875,7 +882,7 @@ private async Task HandleSuppressedAnalyzerAsync(Project project, StateSet state
var existingData = await state.TryGetExistingDataAsync(project, cancellationToken).ConfigureAwait(false);
if (existingData?.Items.Length > 0)
{
ClearProjectState(project, stateSet.Analyzer, state);
ClearProjectState(project, stateSet);
}
}
......
......@@ -39,7 +39,7 @@ public override async Task SynchronizeWithBuildAsync(Project project, ImmutableA
var mergedDiagnostics = MergeDiagnostics(liveDiagnostics, GetExistingDiagnostics(existingDiagnostics));
await state.PersistAsync(project, new AnalysisData(projectTextVersion, semanticVersion, mergedDiagnostics), CancellationToken.None).ConfigureAwait(false);
RaiseDiagnosticsUpdated(StateType.Project, project.Id, stateSet.Analyzer, new SolutionArgument(project), mergedDiagnostics);
RaiseDiagnosticsUpdated(StateType.Project, project.Id, stateSet, new SolutionArgument(project), mergedDiagnostics);
}
}
}
......@@ -105,7 +105,7 @@ private bool PreferLiveErrorsOnOpenedFiles(Workspace workspace)
var mergedDiagnostics = MergeDiagnostics(diagnostics, GetExistingDiagnostics(existingDiagnostics));
await state.PersistAsync(document, new AnalysisData(textVersion, semanticVersion, mergedDiagnostics), CancellationToken.None).ConfigureAwait(false);
RaiseDiagnosticsUpdated(stateType, document.Id, stateSet.Analyzer, new SolutionArgument(document), mergedDiagnostics);
RaiseDiagnosticsUpdated(stateType, document.Id, stateSet, new SolutionArgument(document), mergedDiagnostics);
}
private static ImmutableArray<DiagnosticData> GetExistingDiagnostics(AnalysisData 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 Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Support ErrorSource information.
/// </summary>
internal abstract class ErrorSourceId
{
public abstract string ErrorSource { get; }
internal abstract class Base<T> : ErrorSourceId
{
protected readonly T _Field1;
public Base(T field)
{
_Field1 = field;
}
public override bool Equals(object obj)
{
var other = obj as Base<T>;
if (other == null)
{
return false;
}
return object.Equals(_Field1, other._Field1);
}
public override int GetHashCode()
{
return _Field1?.GetHashCode() ?? 0;
}
}
internal abstract class Base<T1, T2> : Base<T2>
{
private readonly T1 _Field2;
public Base(T1 field1, T2 field2) : base(field2)
{
_Field2 = field1;
}
public override bool Equals(object obj)
{
var other = obj as Base<T1, T2>;
if (other == null)
{
return false;
}
return object.Equals(_Field2, other._Field2) && base.Equals(other);
}
public override int GetHashCode()
{
return Hash.Combine(_Field2?.GetHashCode() ?? 0, base.GetHashCode());
}
}
}
}
......@@ -4,10 +4,8 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Roslyn.Utilities;
......@@ -24,6 +22,11 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// </summary>
internal sealed partial class HostAnalyzerManager
{
/// <summary>
/// This contains vsix info on where <see cref="HostDiagnosticAnalyzerPackage"/> comes from.
/// </summary>
private readonly ImmutableArray<HostDiagnosticAnalyzerPackage> _hostDiagnosticAnalyzerPackages;
/// <summary>
/// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
///
......@@ -31,6 +34,11 @@ internal sealed partial class HostAnalyzerManager
/// </summary>
private readonly ImmutableDictionary<string, AnalyzerReference> _hostAnalyzerReferencesMap;
/// <summary>
/// Map contains <see cref="AnalyzerReference"/> to <see cref="HostDiagnosticAnalyzerPackage.Name"/>.
/// </summary>
private readonly ImmutableDictionary<AnalyzerReference, string> _hostAnalyzerReferenceNameMap;
/// <summary>
/// Key is the language the <see cref="DiagnosticAnalyzer"/> supports and key for the second map is analyzer reference identity and
/// <see cref="DiagnosticAnalyzer"/> for that assembly reference.
......@@ -58,18 +66,30 @@ internal sealed partial class HostAnalyzerManager
/// </summary>
private ImmutableDictionary<string, DiagnosticAnalyzer> _compilerDiagnosticAnalyzerMap;
/// <summary>
/// map from host diagnostic analyzer to package name it came from
/// </summary>
private ImmutableDictionary<DiagnosticAnalyzer, string> _hostDiagnosticAnalzyerPackageNameMap;
/// <summary>
/// map to compiler diagnostic analyzer descriptor.
/// </summary>
private ImmutableDictionary<DiagnosticAnalyzer, HashSet<string>> _compilerDiagnosticAnalyzerDescriptorMap;
public HostAnalyzerManager(IEnumerable<string> hostAnalyzerAssemblies, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) :
this(CreateAnalyzerReferencesFromAssemblies(hostAnalyzerAssemblies), hostDiagnosticUpdateSource)
public HostAnalyzerManager(IEnumerable<HostDiagnosticAnalyzerPackage> hostAnalyzerPackages, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) :
this(CreateAnalyzerReferencesFromPackages(hostAnalyzerPackages), hostAnalyzerPackages.ToImmutableArrayOrEmpty(), hostDiagnosticUpdateSource)
{
}
public HostAnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferences, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource)
public HostAnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferences, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) :
this(hostAnalyzerReferences, ImmutableArray<HostDiagnosticAnalyzerPackage>.Empty, hostDiagnosticUpdateSource)
{
}
private HostAnalyzerManager(
ImmutableArray<AnalyzerReference> hostAnalyzerReferences, ImmutableArray<HostDiagnosticAnalyzerPackage> hostAnalyzerPackages, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource)
{
_hostDiagnosticAnalyzerPackages = hostAnalyzerPackages;
_hostDiagnosticUpdateSource = hostDiagnosticUpdateSource;
_hostAnalyzerReferencesMap = hostAnalyzerReferences.IsDefault ? ImmutableDictionary<string, AnalyzerReference>.Empty : CreateAnalyzerReferencesMap(hostAnalyzerReferences);
......@@ -78,6 +98,7 @@ public HostAnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferen
_compilerDiagnosticAnalyzerMap = ImmutableDictionary<string, DiagnosticAnalyzer>.Empty;
_compilerDiagnosticAnalyzerDescriptorMap = ImmutableDictionary<DiagnosticAnalyzer, HashSet<string>>.Empty;
_hostDiagnosticAnalzyerPackageNameMap = ImmutableDictionary<DiagnosticAnalyzer, string>.Empty;
DiagnosticAnalyzerLogger.LogWorkspaceAnalyzers(hostAnalyzerReferences);
}
......@@ -104,7 +125,7 @@ public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticA
/// </summary>
public ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> GetHostDiagnosticAnalyzersPerReference(string language)
{
return _hostDiagnosticAnalyzersPerLanguageMap.GetOrAdd(language, CreateHostDiagnosticAnalyzers);
return _hostDiagnosticAnalyzersPerLanguageMap.GetOrAdd(language, CreateHostDiagnosticAnalyzersAndBuildMap);
}
/// <summary>
......@@ -172,6 +193,49 @@ public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
return false;
}
/// <summary>
/// Return compiler <see cref="DiagnosticAnalyzer"/> for the given language.
/// </summary>
public DiagnosticAnalyzer GetCompilerDiagnosticAnalyzer(string language)
{
var map = GetHostDiagnosticAnalyzersPerReference(language);
DiagnosticAnalyzer compilerAnalyzer;
if (_compilerDiagnosticAnalyzerMap.TryGetValue(language, out compilerAnalyzer))
{
return compilerAnalyzer;
}
return null;
}
/// <summary>
/// Check whether given <see cref="DiagnosticAnalyzer"/> is compiler analyzer for the language or not.
/// </summary>
public bool IsCompilerDiagnosticAnalyzer(string language, DiagnosticAnalyzer analyzer)
{
var map = GetHostDiagnosticAnalyzersPerReference(language);
DiagnosticAnalyzer compilerAnalyzer;
return _compilerDiagnosticAnalyzerMap.TryGetValue(language, out compilerAnalyzer) && compilerAnalyzer == analyzer;
}
/// <summary>
/// Get Name of Package (vsix) which Host <see cref="DiagnosticAnalyzer"/> is from.
/// </summary>
public string GetDiagnosticAnalyzerPackageName(string language, DiagnosticAnalyzer analyzer)
{
var map = GetHostDiagnosticAnalyzersPerReference(language);
string name;
if (_hostDiagnosticAnalzyerPackageNameMap.TryGetValue(analyzer, out name))
{
return name;
}
return null;
}
private ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>> CreateDiagnosticDescriptorsPerReference(
ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzersMap)
{
......@@ -195,10 +259,12 @@ public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
return builder.ToImmutable();
}
private ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> CreateHostDiagnosticAnalyzers(string language)
private ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> CreateHostDiagnosticAnalyzersAndBuildMap(string language)
{
Contract.ThrowIfNull(language);
var nameMap = CreateAnalyzerPathToPackageNameMap();
var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<DiagnosticAnalyzer>>();
foreach (var kv in _hostAnalyzerReferencesMap)
{
......@@ -213,6 +279,8 @@ public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
UpdateCompilerAnalyzerMapIfNeeded(language, analyzers);
UpdateDiagnosticAnalyzerToPackageNameMap(nameMap, reference, analyzers);
// there can't be duplication since _hostAnalyzerReferenceMap is already de-duplicated.
builder.Add(referenceIdenity, analyzers);
}
......@@ -220,6 +288,46 @@ public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
return builder.ToImmutable();
}
private void UpdateDiagnosticAnalyzerToPackageNameMap(
ImmutableDictionary<string, string> nameMap,
AnalyzerReference reference,
ImmutableArray<DiagnosticAnalyzer> analyzers)
{
var fileReference = reference as AnalyzerFileReference;
if (fileReference == null)
{
return;
}
string name;
if (!nameMap.TryGetValue(fileReference.FullPath, out name))
{
return;
}
foreach (var analyzer in analyzers)
{
ImmutableInterlocked.GetOrAdd(ref _hostDiagnosticAnalzyerPackageNameMap, analyzer, _ => name);
}
}
private ImmutableDictionary<string, string> CreateAnalyzerPathToPackageNameMap()
{
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var package in _hostDiagnosticAnalyzerPackages)
{
foreach (var assembly in package.Assemblies)
{
if (!builder.ContainsKey(assembly))
{
builder.Add(assembly, package.Name);
}
}
}
return builder.ToImmutable();
}
private void UpdateCompilerAnalyzerMapIfNeeded(string language, ImmutableArray<DiagnosticAnalyzer> analyzers)
{
if (_compilerDiagnosticAnalyzerMap.ContainsKey(language))
......@@ -292,13 +400,15 @@ private bool CheckAnalyzerReferenceIdentity(AnalyzerReference reference)
return builder.ToImmutable();
}
private static ImmutableArray<AnalyzerReference> CreateAnalyzerReferencesFromAssemblies(IEnumerable<string> analyzerAssemblies)
private static ImmutableArray<AnalyzerReference> CreateAnalyzerReferencesFromPackages(IEnumerable<HostDiagnosticAnalyzerPackage> analyzerPackages)
{
if (analyzerAssemblies == null || analyzerAssemblies.IsEmpty())
if (analyzerPackages == null || analyzerPackages.IsEmpty())
{
return ImmutableArray<AnalyzerReference>.Empty;
}
var analyzerAssemblies = analyzerPackages.SelectMany(p => p.Assemblies);
// We want to load the analyzer assembly assets in default context.
// Use Assembly.Load instead of Assembly.LoadFrom to ensure that if the assembly is ngen'ed, then the native image gets loaded.
Func<string, Assembly> getAssembly = (fullPath) => Assembly.Load(AssemblyName.GetAssemblyName(fullPath));
......
// 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.Immutable;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Information on vsix that contains diagnostic analyzers
/// </summary>
internal class HostDiagnosticAnalyzerPackage
{
public readonly string Name;
public readonly ImmutableArray<string> Assemblies;
public HostDiagnosticAnalyzerPackage(string name, ImmutableArray<string> assemblies)
{
this.Name = name;
this.Assemblies = assemblies;
}
}
}
......@@ -81,5 +81,15 @@ internal interface IDiagnosticAnalyzerService
/// Check whether given diagnostic is compiler diagnostic or not
/// </summary>
bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic);
/// <summary>
/// Get compiler analyzer for the given language
/// </summary>
DiagnosticAnalyzer GetCompilerDiagnosticAnalyzer(string language);
/// <summary>
/// Check whether given <see cref="DiagnosticAnalyzer"/> is compiler analyzer for the language or not.
/// </summary>
bool IsCompilerDiagnosticAnalyzer(string language, DiagnosticAnalyzer 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
{
/// <summary>
/// Marker interface to indicate whether given diagnostic args are from live analysis.
/// </summary>
internal interface ISupportLiveUpdate
{
}
}
// 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;
namespace Microsoft.CodeAnalysis.Diagnostics
{
......@@ -12,7 +10,6 @@ internal interface IWorkspaceDiagnosticAnalyzerProviderService
/// Gets the analyzers shared across the entire workspace session.
/// This includes the analyzers included through VSIX installations.
/// </summary>
/// <returns></returns>
IEnumerable<string> GetWorkspaceAnalyzerAssemblies();
IEnumerable<HostDiagnosticAnalyzerPackage> GetHostDiagnosticAnalyzerPackages();
}
}
// 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 PredefinedErrorSources
{
public static readonly string Build = FeaturesResources.ErrorSourceBuild;
public static readonly string EnC = FeaturesResources.ErrorSourceEnC;
public static readonly string Compiler = FeaturesResources.ErrorSourceIntelliSense;
}
}
......@@ -180,6 +180,9 @@
<Compile Include="Diagnostics\AnalyzerDriverResources.cs" />
<Compile Include="Diagnostics\AnalyzerHelper.cs" />
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource.cs" />
<Compile Include="Diagnostics\AnalyzerUpdateArgsId.cs" />
<Compile Include="Diagnostics\HostDiagnosticAnalyzerPackage.cs" />
<Compile Include="Diagnostics\PredefinedErrorSources.cs" />
<Compile Include="Diagnostics\DiagnosticAnalyzerService_BuildSynchronization.cs" />
<Compile Include="Diagnostics\EngineV1\DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs" />
<Compile Include="Diagnostics\EngineV1\DiagnosticIncrementalAnalyzer.StateManager.cs" />
......@@ -188,6 +191,7 @@
<Compile Include="Diagnostics\EngineV1\DiagnosticIncrementalAnalyzer.StateSet.cs" />
<Compile Include="Diagnostics\EngineV1\DiagnosticIncrementalAnalyzer_BuildSynchronization.cs" />
<Compile Include="Diagnostics\EngineV1\DiagnosticIncrementalAnalyzer_GetLatestDiagnosticsForSpan.cs" />
<Compile Include="Diagnostics\ErrorSourceId.cs" />
<Compile Include="Diagnostics\HostAnalyzerManager.cs" />
<Compile Include="Diagnostics\Analyzers\IDEDiagnosticIds.cs" />
<Compile Include="Diagnostics\BaseDiagnosticIncrementalAnalyzer.cs" />
......@@ -217,7 +221,7 @@
<Compile Include="Diagnostics\IWorkspaceDiagnosticAnalyzerProviderService.cs" />
<Compile Include="Diagnostics\IWorkspaceVenusSpanMappingService.cs" />
<Compile Include="Diagnostics\PredefinedDiagnosticProviderNames.cs" />
<Compile Include="Diagnostics\UpdateArgsId.cs" />
<Compile Include="Diagnostics\ISupportLiveUpdate.cs" />
<Compile Include="Diagnostics\WorkspaceAnalyzerOptions.cs" />
<Compile Include="EditAndContinue\DebuggingState.cs" />
<Compile Include="EditAndContinue\DebuggingStateChangedEventArgs.cs" />
......
......@@ -591,6 +591,33 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Build.
/// </summary>
internal static string ErrorSourceBuild {
get {
return ResourceManager.GetString("ErrorSourceBuild", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit And Continue.
/// </summary>
internal static string ErrorSourceEnC {
get {
return ResourceManager.GetString("ErrorSourceEnC", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to IntelliSense Build.
/// </summary>
internal static string ErrorSourceIntelliSense {
get {
return ResourceManager.GetString("ErrorSourceIntelliSense", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Exceptions:.
/// </summary>
......
......@@ -762,4 +762,13 @@ Do you want to continue?</value>
<data name="ModifyingAWhichContainsStaticLocal" xml:space="preserve">
<value>Modifying '{0}' which contains a static variable will prevent the debug session from continuing.</value>
</data>
<data name="ErrorSourceBuild" xml:space="preserve">
<value>Build</value>
</data>
<data name="ErrorSourceEnC" xml:space="preserve">
<value>Edit And Continue</value>
</data>
<data name="ErrorSourceIntelliSense" xml:space="preserve">
<value>IntelliSense Build</value>
</data>
</root>
\ No newline at end of file
......@@ -21,6 +21,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics
[Shared]
internal partial class MiscellaneousDiagnosticAnalyzerService : IIncrementalAnalyzerProvider, IDiagnosticUpdateSource
{
private readonly IDiagnosticAnalyzerService analyzerService;
[ImportingConstructor]
public MiscellaneousDiagnosticAnalyzerService(IDiagnosticAnalyzerService analyzerService)
{
this.analyzerService = analyzerService;
}
public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
{
if (!workspace.Options.GetOption(ServiceComponentOnOffOptions.DiagnosticProvider))
......@@ -82,7 +90,9 @@ public async Task AnalyzeSyntaxAsync(Document document, CancellationToken cancel
Contract.Requires(document.Project.Solution.Workspace == _workspace);
var diagnosticData = diagnostics == null ? ImmutableArray<DiagnosticData>.Empty : diagnostics.Select(d => DiagnosticData.Create(document, d)).ToImmutableArrayOrEmpty();
_service.RaiseDiagnosticsUpdated(new DiagnosticsUpdatedArgs(new MiscUpdateArgsId(document.Id), _workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData));
_service.RaiseDiagnosticsUpdated(
new DiagnosticsUpdatedArgs(new MiscUpdateArgsId(document.Id),
_workspace, document.Project.Solution, document.Project.Id, document.Id, diagnosticData));
}
public void RemoveDocument(DocumentId documentId)
......@@ -137,13 +147,18 @@ public void RemoveProject(ProjectId projectId)
{
}
private class MiscUpdateArgsId : UpdateArgsId
private class MiscUpdateArgsId : ErrorSourceId.Base<DocumentId>, ISupportLiveUpdate
{
private readonly DocumentId _documentId;
public MiscUpdateArgsId(DocumentId documentId) : base(documentId)
{
}
public MiscUpdateArgsId(DocumentId documentId) : base(null)
public override string ErrorSource
{
_documentId = documentId;
get
{
return PredefinedErrorSources.Compiler;
}
}
public override bool Equals(object obj)
......@@ -154,12 +169,12 @@ public override bool Equals(object obj)
return false;
}
return _documentId == other._documentId && base.Equals(obj);
return base.Equals(obj);
}
public override int GetHashCode()
{
return Hash.Combine(_documentId.GetHashCode(), base.GetHashCode());
return base.GetHashCode();
}
}
}
......
// 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.ComponentModel.Composition;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.VisualStudio.ExtensionManager;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
......@@ -13,22 +17,38 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics
/// These analyzers are used across this workspace session.
/// </summary>
[Export(typeof(IWorkspaceDiagnosticAnalyzerProviderService))]
internal partial class VisualStudioWorkspaceDiagnosticAnalyzerProviderService : IWorkspaceDiagnosticAnalyzerProviderService
internal class VisualStudioWorkspaceDiagnosticAnalyzerProviderService : IWorkspaceDiagnosticAnalyzerProviderService
{
private readonly IEnumerable<string> _workspaceAnalyzerAssemblies;
private const string AnalyzerContentTypeName = "Microsoft.VisualStudio.Analyzer";
private readonly ImmutableArray<HostDiagnosticAnalyzerPackage> _hostDiagnosticAnalyzerInfo;
[ImportingConstructor]
public VisualStudioWorkspaceDiagnosticAnalyzerProviderService(VisualStudioWorkspaceImpl workspace)
{
// Get the analyzer assets for installed VSIX extensions through the VSIX extension manager.
var extensionManager = workspace.GetVsService<SVsExtensionManager, IVsExtensionManager>();
_workspaceAnalyzerAssemblies = extensionManager.GetEnabledExtensionContentLocations(AnalyzerContentTypeName);
var builder = ImmutableArray.CreateBuilder<HostDiagnosticAnalyzerPackage>();
foreach (var extension in extensionManager.GetEnabledExtensions(AnalyzerContentTypeName))
{
var name = extension.Header.LocalizedName;
var assemblies = extension.Content.Where(ShouldInclude).Select(c => Path.Combine(extension.InstallPath, c.RelativePath));
builder.Add(new HostDiagnosticAnalyzerPackage(name, assemblies.ToImmutableArray()));
}
_hostDiagnosticAnalyzerInfo = builder.ToImmutable();
}
public IEnumerable<HostDiagnosticAnalyzerPackage> GetHostDiagnosticAnalyzerPackages()
{
return _hostDiagnosticAnalyzerInfo;
}
public IEnumerable<string> GetWorkspaceAnalyzerAssemblies()
private bool ShouldInclude(IExtensionContent content)
{
return _workspaceAnalyzerAssemblies;
return string.Equals(content.ContentTypeName, AnalyzerContentTypeName, StringComparison.InvariantCultureIgnoreCase);
}
}
}
......@@ -30,6 +30,7 @@ internal class VisualStudioBaseDiagnosticListTable : AbstractTable<DiagnosticsUp
StandardTableColumnDefinitions.ErrorCode,
StandardTableColumnDefinitions.Text,
StandardTableColumnDefinitions.ErrorCategory,
StandardTableColumnDefinitions.ErrorSource,
StandardTableColumnDefinitions.ProjectName,
StandardTableColumnDefinitions.DocumentName,
StandardTableColumnDefinitions.Line,
......@@ -181,8 +182,9 @@ private class TableEntriesFactory : AbstractTableEntriesFactory<DiagnosticData>
private readonly ProjectId _projectId;
private readonly DocumentId _documentId;
private readonly object _id;
private readonly string _errorSource;
public TableEntriesFactory(TableDataSource source, Workspace workspace, ProjectId projectId, DocumentId documentId, object id) :
public TableEntriesFactory(TableDataSource source, Workspace workspace, ProjectId projectId, DocumentId documentId, object id) :
base(source)
{
_source = source;
......@@ -190,6 +192,7 @@ private class TableEntriesFactory : AbstractTableEntriesFactory<DiagnosticData>
_projectId = projectId;
_documentId = documentId;
_id = id;
_errorSource = (id as ErrorSourceId)?.ErrorSource ?? string.Empty;
}
protected override ImmutableArray<DiagnosticData> GetItems()
......@@ -287,6 +290,9 @@ public override bool TryGetValue(int index, string columnName, out object conten
case StandardTableKeyNames.ErrorCategory:
content = item.Category;
return true;
case StandardTableKeyNames.ErrorSource:
content = _factory._errorSource;
return content != null;
case StandardTableKeyNames.Text:
content = item.Message;
return true;
......
......@@ -454,23 +454,15 @@ private HashSet<DiagnosticData> GetErrors<T>(Dictionary<T, HashSet<DiagnosticDat
}
}
private class ArgumentKey
private class ArgumentKey : ErrorSourceId.Base<object>
{
public object Key;
public ArgumentKey(object key)
{
this.Key = key;
}
public DocumentId DocumentId
public ArgumentKey(object key) : base(key)
{
get { return this.Key as DocumentId; }
}
public ProjectId ProjectId
public override string ErrorSource
{
get { return this.Key as ProjectId; }
get { return PredefinedErrorSources.Build; }
}
public override bool Equals(object obj)
......@@ -481,12 +473,12 @@ public override bool Equals(object obj)
return false;
}
return Key == other.Key;
return base.Equals(obj);
}
public override int GetHashCode()
{
return this.Key.GetHashCode();
return base.GetHashCode();
}
}
......
......@@ -489,6 +489,35 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Using
End Sub
<Fact>
Public Sub TestErrorSource()
Using workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines(String.Empty)
Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First()
Dim projectId = documentId.ProjectId
Dim item1 = CreateItem(workspace, projectId, documentId, DiagnosticSeverity.Error, "http://link/")
Dim provider = New TestDiagnosticService(item1)
Dim tableManagerProvider = New TestTableManagerProvider()
Dim table = New VisualStudioDiagnosticListTable(workspace, provider, tableManagerProvider)
provider.RaiseDiagnosticsUpdated(workspace)
Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager)
Dim source = DirectCast(manager.Sources.First(), AbstractRoslynTableDataSource(Of DiagnosticsUpdatedArgs, DiagnosticData))
Dim sinkAndSubscription = manager.Sinks_TestOnly.First()
Dim sink = DirectCast(sinkAndSubscription.Key, TestTableManagerProvider.TestTableManager.TestSink)
Dim snapshot = sink.Entries.First().GetCurrentSnapshot()
Assert.Equal(1, snapshot.Count)
Dim errorSource As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ErrorSource, errorSource))
Assert.Equal("ErrorSource", errorSource.ToString())
End Using
End Sub
Private Function CreateItem(workspace As Workspace, documentId As DocumentId, Optional severity As DiagnosticSeverity = DiagnosticSeverity.Error) As DiagnosticData
Return CreateItem(workspace, documentId.ProjectId, documentId, severity)
End Function
......@@ -529,7 +558,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
Dim id = If(CObj(item.DocumentId), item.ProjectId)
RaiseEvent DiagnosticsUpdated(Me, New DiagnosticsUpdatedArgs(
ValueTuple.Create(Of IDiagnosticService, Object)(Me, id), workspace, workspace.CurrentSolution, item.ProjectId, item.DocumentId, items.ToImmutableArray()))
New ErrorId(Me, id), workspace, workspace.CurrentSolution, item.ProjectId, item.DocumentId, items.ToImmutableArray()))
End Sub
Public Sub RaiseDiagnosticsUpdated(workspace As Workspace)
......@@ -537,21 +566,35 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
For Each group In documentMap
RaiseEvent DiagnosticsUpdated(Me, New DiagnosticsUpdatedArgs(
ValueTuple.Create(Of IDiagnosticService, Object)(Me, group.Key), workspace, workspace.CurrentSolution, group.Key.ProjectId, group.Key, group.ToImmutableArrayOrEmpty()))
New ErrorId(Me, group.Key), workspace, workspace.CurrentSolution, group.Key.ProjectId, group.Key, group.ToImmutableArrayOrEmpty()))
Next
Dim projectMap = Items.Where(Function(t) t.DocumentId Is Nothing).Where(Function(t) t.Workspace Is workspace).ToLookup(Function(t) t.ProjectId)
For Each group In projectMap
RaiseEvent DiagnosticsUpdated(Me, New DiagnosticsUpdatedArgs(
ValueTuple.Create(Of IDiagnosticService, Object)(Me, group.Key), workspace, workspace.CurrentSolution, group.Key, Nothing, group.ToImmutableArrayOrEmpty()))
New ErrorId(Me, group.Key), workspace, workspace.CurrentSolution, group.Key, Nothing, group.ToImmutableArrayOrEmpty()))
Next
End Sub
Public Sub RaiseClearDiagnosticsUpdated(workspace As Workspace, projectId As ProjectId, documentId As DocumentId)
RaiseEvent DiagnosticsUpdated(Me, New DiagnosticsUpdatedArgs(
ValueTuple.Create(Of IDiagnosticService, Object)(Me, documentId), workspace, workspace.CurrentSolution, projectId, documentId, ImmutableArray(Of DiagnosticData).Empty))
New ErrorId(Me, documentId), workspace, workspace.CurrentSolution, projectId, documentId, ImmutableArray(Of DiagnosticData).Empty))
End Sub
Private Class ErrorId
Inherits ErrorSourceId.Base(Of TestDiagnosticService, Object)
Public Sub New(service As TestDiagnosticService, id As Object)
MyBase.New(service, id)
End Sub
Public Overrides ReadOnly Property ErrorSource As String
Get
Return "ErrorSource"
End Get
End Property
End Class
End Class
End Class
End Namespace
\ No newline at end of file
......@@ -173,6 +173,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
Public Function IsCompilerDiagnostic(language As String, diagnostic As DiagnosticData) As Boolean Implements IDiagnosticAnalyzerService.IsCompilerDiagnostic
Return False
End Function
Public Function GetCompilerDiagnosticAnalyzer(language As String) As DiagnosticAnalyzer Implements IDiagnosticAnalyzerService.GetCompilerDiagnosticAnalyzer
Return Nothing
End Function
Public Function IsCompilerDiagnosticAnalyzer(language As String, analyzer As DiagnosticAnalyzer) As Boolean Implements IDiagnosticAnalyzerService.IsCompilerDiagnosticAnalyzer
Return False
End Function
End Class
End Class
End Namespace
......@@ -22,7 +22,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
class 123 { }
</code>
Using workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines(code.ToString())
Dim miscService = New MiscellaneousDiagnosticAnalyzerService()
Dim miscService = New MiscellaneousDiagnosticAnalyzerService(New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()))
Assert.False(miscService.SupportGetDiagnostics)
Dim diagnosticWaiter = New DiagnosticServiceWaiter()
......@@ -57,6 +57,49 @@ class 123 { }
End Using
End Sub
<Fact>
Public Sub TestMiscCSharpErrorSource()
Dim code = <code>
class 123 { }
</code>
Using workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines(code.ToString())
Dim miscService = New MiscellaneousDiagnosticAnalyzerService(New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()))
Dim errorSource = String.Empty
AddHandler miscService.DiagnosticsUpdated, Sub(e, a)
Dim id = DirectCast(a.Id, ErrorSourceId)
errorSource = id.ErrorSource
End Sub
Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace)
analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), CancellationToken.None).PumpingWait()
Assert.Equal(PredefinedErrorSources.Compiler, errorSource)
End Using
End Sub
<Fact>
Public Sub TestMiscVBErrorSource()
Dim code = <code>
Class 123
End Class
</code>
Using workspace = VisualBasicWorkspaceFactory.CreateWorkspaceFromLines(code.ToString())
Dim miscService = New MiscellaneousDiagnosticAnalyzerService(New TestDiagnosticAnalyzerService(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()))
Dim errorSource = String.Empty
AddHandler miscService.DiagnosticsUpdated, Sub(e, a)
Dim id = DirectCast(a.Id, ErrorSourceId)
errorSource = id.ErrorSource
End Sub
Dim analyzer = miscService.CreateIncrementalAnalyzer(workspace)
analyzer.AnalyzeSyntaxAsync(workspace.CurrentSolution.Projects.First().Documents.First(), CancellationToken.None).PumpingWait()
Assert.Equal(PredefinedErrorSources.Compiler, errorSource)
End Using
End Sub
Private Class DiagnosticServiceWaiter : Inherits AsynchronousOperationListener : End Class
Private Class ErrorSquiggleWaiter : Inherits AsynchronousOperationListener : End Class
End Class
......
......@@ -94,8 +94,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.EditAndContinue
Public Function IsCompilerDiagnostic(language As String, diagnostic As DiagnosticData) As Boolean Implements IDiagnosticAnalyzerService.IsCompilerDiagnostic
Return False
End Function
End Class
End Class
Public Function GetCompilerDiagnosticAnalyzer(language As String) As DiagnosticAnalyzer Implements IDiagnosticAnalyzerService.GetCompilerDiagnosticAnalyzer
Return Nothing
End Function
Public Function IsCompilerDiagnosticAnalyzer(language As String, analyzer As DiagnosticAnalyzer) As Boolean Implements IDiagnosticAnalyzerService.IsCompilerDiagnosticAnalyzer
Return False
End Function
End Class
End Class
End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册