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

Merge pull request #5546 from heejaechang/nullexception2

deal with cases where item in table control is not associated with any project or document
// 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;
using System.Linq;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal class TestAnalyzerReferenceByLanguage : AnalyzerReference
{
private readonly ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> _analyzersMap;
public TestAnalyzerReferenceByLanguage(ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzersMap)
{
_analyzersMap = analyzersMap;
}
public override string FullPath
{
get
{
return null;
}
}
public override string Display
{
get
{
return nameof(TestAnalyzerReferenceByLanguage);
}
}
public override object Id
{
get
{
return Display;
}
}
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzersForAllLanguages()
{
return _analyzersMap.SelectMany(kvp => kvp.Value).ToImmutableArray();
}
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(string language)
{
ImmutableArray<DiagnosticAnalyzer> analyzers;
if (_analyzersMap.TryGetValue(language, out analyzers))
{
return analyzers;
}
return ImmutableArray<DiagnosticAnalyzer>.Empty;
}
}
}
......@@ -2,7 +2,6 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics;
using Roslyn.Utilities;
......@@ -23,8 +22,12 @@ internal TestDiagnosticAnalyzerService(string language, ImmutableArray<Diagnosti
{
}
internal TestDiagnosticAnalyzerService(ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzersMap, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null)
: this(CreateHostAnalyzerManager(analyzersMap, hostDiagnosticUpdateSource), hostDiagnosticUpdateSource, onAnalyzerException)
internal TestDiagnosticAnalyzerService(
ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzersMap,
AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource = null,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null,
IDiagnosticUpdateSourceRegistrationService registrationService = null)
: this(CreateHostAnalyzerManager(analyzersMap, hostDiagnosticUpdateSource), hostDiagnosticUpdateSource, onAnalyzerException, registrationService)
{
}
......@@ -33,8 +36,12 @@ internal TestDiagnosticAnalyzerService(ImmutableArray<AnalyzerReference> hostAna
{
}
private TestDiagnosticAnalyzerService(HostAnalyzerManager hostAnalyzerManager, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException)
: base(hostAnalyzerManager, hostDiagnosticUpdateSource, new MockDiagnosticUpdateSourceRegistrationService())
private TestDiagnosticAnalyzerService(
HostAnalyzerManager hostAnalyzerManager,
AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
IDiagnosticUpdateSourceRegistrationService registrationService = null)
: base(hostAnalyzerManager, hostDiagnosticUpdateSource, registrationService ?? new MockDiagnosticUpdateSourceRegistrationService())
{
_onAnalyzerException = onAnalyzerException;
}
......@@ -72,55 +79,5 @@ private static HostAnalyzerManager CreateHostAnalyzerManager(ImmutableArray<Anal
{
return _onAnalyzerException ?? base.GetOnAnalyzerException(projectId, diagnosticLogAggregator);
}
private class TestAnalyzerReferenceByLanguage : AnalyzerReference
{
private readonly ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> _analyzersMap;
public TestAnalyzerReferenceByLanguage(ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> analyzersMap)
{
_analyzersMap = analyzersMap;
}
public override string FullPath
{
get
{
return null;
}
}
public override string Display
{
get
{
return nameof(TestAnalyzerReferenceByLanguage);
}
}
public override object Id
{
get
{
return Display;
}
}
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzersForAllLanguages()
{
return _analyzersMap.SelectMany(kvp => kvp.Value).ToImmutableArray();
}
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(string language)
{
ImmutableArray<DiagnosticAnalyzer> analyzers;
if (_analyzersMap.TryGetValue(language, out analyzers))
{
return analyzers;
}
return ImmutableArray<DiagnosticAnalyzer>.Empty;
}
}
}
}
......@@ -223,6 +223,7 @@
<Compile Include="Diagnostics\AbstractDiagnosticProviderBasedUserDiagnosticTest.cs" />
<Compile Include="Diagnostics\AbstractUserDiagnosticTest.cs" />
<Compile Include="Diagnostics\MockDiagnosticUpdateSourceRegistrationService.cs" />
<Compile Include="Diagnostics\TestAnalyzerReferenceByLanguage.cs" />
<Compile Include="Diagnostics\TestDiagnosticAnalyzerDriver.cs" />
<Compile Include="Diagnostics\TestDiagnosticAnalyzerService.cs" />
<Compile Include="Diagnostics\DiagnosticDataTests.cs" />
......
......@@ -187,6 +187,21 @@ public static DocumentId GetDocumentId<T>(T item)
return todo.DocumentId;
}
public static ProjectId GetProjectId<T>(T item)
{
// item must be either one of diagnostic data and todo item
var diagnostic = item as DiagnosticData;
if (diagnostic != null)
{
return diagnostic.ProjectId;
}
var todo = item as TodoItem;
Contract.ThrowIfNull(todo);
return todo.DocumentId.ProjectId;
}
public static Workspace GetWorkspace<T>(T item)
{
// item must be either one of diagnostic data and todo item
......
......@@ -91,10 +91,16 @@ public string ProjectName
{
get
{
var projectId = Extensions.GetProjectId(Primary);
if (projectId == null)
{
return null;
}
if (_cache == null)
{
// return single project name
return Workspace.GetProjectName(PrimaryDocumentId.ProjectId);
return Workspace.GetProjectName(projectId);
}
// return joined project names
......@@ -120,9 +126,15 @@ public Guid ProjectGuid
{
get
{
var projectId = Extensions.GetProjectId(Primary);
if (projectId == null)
{
return Guid.Empty;
}
if (_cache == null)
{
return Workspace.GetProjectGuid(PrimaryDocumentId.ProjectId);
return Workspace.GetProjectGuid(projectId);
}
// if this is aggregated element, there is no projectguid
......
......@@ -243,7 +243,8 @@ private int GenerateDeduplicationKey(DiagnosticData diagnostic)
Hash.Combine(diagnostic.DataLocation.OriginalStartLine,
Hash.Combine(diagnostic.DataLocation.OriginalEndColumn,
Hash.Combine(diagnostic.DataLocation.OriginalEndLine,
Hash.Combine(diagnostic.Id.GetHashCode(), diagnostic.Message.GetHashCode())))));
Hash.Combine(diagnostic.IsSuppressed,
Hash.Combine(diagnostic.Id.GetHashCode(), diagnostic.Message.GetHashCode()))))));
}
}
......@@ -276,13 +277,13 @@ public override bool TryGetValue(int index, string columnName, out object conten
{
case StandardTableKeyNames.ErrorRank:
content = ValueTypeCache.GetOrCreate(GetErrorRank(data));
return true;
return content != null;
case StandardTableKeyNames.ErrorSeverity:
content = ValueTypeCache.GetOrCreate(GetErrorCategory(data.Severity));
return true;
return content != null;
case StandardTableKeyNames.ErrorCode:
content = data.Id;
return true;
return content != null;
case StandardTableKeyNames.ErrorCodeToolTip:
content = GetHelpLinkToolTipText(data);
return content != null;
......@@ -291,19 +292,19 @@ public override bool TryGetValue(int index, string columnName, out object conten
return content != null;
case StandardTableKeyNames.ErrorCategory:
content = data.Category;
return true;
return content != null;
case StandardTableKeyNames.ErrorSource:
content = ValueTypeCache.GetOrCreate(GetErrorSource(_source.BuildTool));
return true;
return content != null;
case StandardTableKeyNames.BuildTool:
content = GetBuildTool(_source.BuildTool);
return content != null;
case StandardTableKeyNames.Text:
content = data.Message;
return true;
return content != null;
case StandardTableKeyNames.DocumentName:
content = GetFileName(data.DataLocation?.OriginalFilePath, data.DataLocation?.MappedFilePath);
return true;
return content != null;
case StandardTableKeyNames.Line:
content = data.DataLocation?.MappedStartLine ?? 0;
return true;
......
......@@ -253,13 +253,13 @@ public override bool TryGetValue(int index, string columnName, out object conten
{
case StandardTableKeyNames.Priority:
content = ValueTypeCache.GetOrCreate((VSTASKPRIORITY)data.Priority);
return true;
return content != null;
case StandardTableKeyNames.Text:
content = data.Message;
return true;
return content != null;
case StandardTableKeyNames.DocumentName:
content = GetFileName(data.OriginalFilePath, data.MappedFilePath);
return true;
return content != null;
case StandardTableKeyNames.Line:
content = GetLineColumn(data).Line;
return true;
......@@ -268,7 +268,7 @@ public override bool TryGetValue(int index, string columnName, out object conten
return true;
case StandardTableKeyNames.TaskCategory:
content = ValueTypeCache.GetOrCreate(VSTASKCATEGORY.CAT_COMMENTS);
return true;
return content != null;
case StandardTableKeyNames.ProjectName:
content = item.ProjectName;
return content != null;
......
......@@ -149,7 +149,8 @@ private int GenerateDeduplicationKey(DiagnosticData diagnostic)
Hash.Combine(diagnostic.DataLocation.OriginalEndColumn,
Hash.Combine(diagnostic.DataLocation.OriginalEndLine,
Hash.Combine(diagnostic.DataLocation.OriginalFilePath,
Hash.Combine(diagnostic.Id.GetHashCode(), diagnostic.Message.GetHashCode()))))));
Hash.Combine(diagnostic.IsSuppressed,
Hash.Combine(diagnostic.Id.GetHashCode(), diagnostic.Message.GetHashCode())))))));
}
}
......@@ -182,13 +183,13 @@ public override bool TryGetValue(int index, string columnName, out object conten
case StandardTableKeyNames.ErrorRank:
// build error gets highest rank
content = ValueTypeCache.GetOrCreate(ErrorRank.Lexical);
return true;
return content != null;
case StandardTableKeyNames.ErrorSeverity:
content = ValueTypeCache.GetOrCreate(GetErrorCategory(data.Severity));
return true;
return content != null;
case StandardTableKeyNames.ErrorCode:
content = data.Id;
return true;
return content != null;
case StandardTableKeyNames.ErrorCodeToolTip:
content = GetHelpLinkToolTipText(data);
return content != null;
......@@ -197,19 +198,19 @@ public override bool TryGetValue(int index, string columnName, out object conten
return content != null;
case StandardTableKeyNames.ErrorCategory:
content = data.Category;
return true;
return content != null;
case StandardTableKeyNames.ErrorSource:
content = ValueTypeCache.GetOrCreate(ErrorSource.Build);
return true;
return content != null;
case StandardTableKeyNames.BuildTool:
content = _source.BuildTool;
return true;
return content != null;
case StandardTableKeyNames.Text:
content = data.Message;
return true;
return content != null;
case StandardTableKeyNames.DocumentName:
content = GetFileName(data.DataLocation?.OriginalFilePath, data.DataLocation?.MappedFilePath);
return true;
return content != null;
case StandardTableKeyNames.Line:
content = data.DataLocation?.MappedStartLine ?? 0;
return true;
......
......@@ -7,8 +7,11 @@ Imports System.Windows.Controls
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Common
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.UnitTests
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Shared.TestHooks
Imports Microsoft.CodeAnalysis.SolutionCrawler
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource
Imports Microsoft.VisualStudio.Shell.TableControl
......@@ -506,13 +509,134 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Using
End Sub
<Fact>
Public Sub TestWorkspaceDiagnostic()
Using workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines(String.Empty)
Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First()
Dim projectId = documentId.ProjectId
Dim item1 = CreateItem(workspace, Nothing, Nothing, 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 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 filename As Object = Nothing
Assert.False(snapshot.TryGetValue(0, StandardTableKeyNames.DocumentName, filename))
Dim projectname As Object = Nothing
Assert.False(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName, projectname))
End Using
End Sub
<Fact>
Public Sub TestProjectDiagnostic()
Using workspace = CSharpWorkspaceFactory.CreateWorkspaceFromLines(String.Empty)
Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First()
Dim projectId = documentId.ProjectId
Dim item1 = CreateItem(workspace, projectId, Nothing, 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 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 filename As Object = Nothing
Assert.False(snapshot.TryGetValue(0, StandardTableKeyNames.DocumentName, filename))
Dim projectname As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName, projectname))
Assert.Equal("Test", projectname)
End Using
End Sub
<Fact>
Public Sub TestAggregatedDiagnostic()
Dim markup = <Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="Proj1">
<Document FilePath="CurrentDocument.cs"><![CDATA[class { }]]></Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="Proj2">
<Document IsLinkFile="true" LinkAssemblyName="Proj1" LinkFilePath="CurrentDocument.cs"/>
</Project>
</Workspace>
Using workspace = TestWorkspaceFactory.CreateWorkspace(markup)
Dim service = New DiagnosticService(AggregateAsynchronousOperationListener.EmptyListeners)
Dim tableManagerProvider = New TestTableManagerProvider()
Dim table = New VisualStudioDiagnosticListTable(workspace, service, tableManagerProvider)
RunCompilerAnalyzer(workspace, service)
Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager)
Dim source = DirectCast(manager.Sources.First(), AbstractRoslynTableDataSource(Of 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 filename As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.DocumentName, filename))
Assert.Equal("CurrentDocument.cs", filename)
Dim projectname As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName, projectname))
Assert.Equal("Proj1, Proj2", projectname)
Dim projectnames As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName + "s", projectnames))
Assert.Equal(2, DirectCast(projectnames, String()).Length)
Dim projectguid As Object = Nothing
Assert.False(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectGuid, projectguid))
End Using
End Sub
Private Sub RunCompilerAnalyzer(workspace As TestWorkspace, registrationService As IDiagnosticUpdateSourceRegistrationService)
Dim snapshot = workspace.CurrentSolution
Dim notificationService = New TestForegroundNotificationService()
Dim compilerAnalyzersMap = DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()
Dim analyzerService = New TestDiagnosticAnalyzerService(compilerAnalyzersMap, registrationService:=registrationService)
Dim service = DirectCast(workspace.Services.GetService(Of ISolutionCrawlerRegistrationService)(), SolutionCrawlerRegistrationService)
service.Register(workspace)
service.WaitUntilCompletion_ForTestingPurposesOnly(workspace, SpecializedCollections.SingletonEnumerable(analyzerService.CreateIncrementalAnalyzer(workspace)).WhereNotNull().ToImmutableArray())
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
Private Function CreateItem(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, Optional severity As DiagnosticSeverity = DiagnosticSeverity.Error, Optional link As String = Nothing) As DiagnosticData
Return New DiagnosticData("test", "test", "test", "test format", severity, True, 0,
workspace, projectId, New DiagnosticDataLocation(documentId, TextSpan.FromBounds(0, 10), "test", 20, 20, 20, 20),
workspace, projectId, If(documentId Is Nothing, Nothing, New DiagnosticDataLocation(documentId, TextSpan.FromBounds(0, 10), "test", 20, 20, 20, 20)),
title:="Title", description:="Description", helpLink:=link)
End Function
......
......@@ -291,8 +291,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
Dim sinkAndSubscription = manager.Sinks_TestOnly.First()
Dim sink = DirectCast(sinkAndSubscription.Key, TestTableManagerProvider.TestTableManager.TestSink)
Dim subscription = sinkAndSubscription.Value
Dim snapshot = sink.Entries.First().GetCurrentSnapshot()
Assert.Equal(1, snapshot.Count)
......@@ -304,6 +302,56 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Using
End Sub
<Fact>
Public Sub TestAggregatedEntries()
Dim markup = <Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="Proj1">
<Document FilePath="test1"><![CDATA[// TODO hello]]></Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="Proj2">
<Document IsLinkFile="true" LinkAssemblyName="Proj1" LinkFilePath="test1"/>
</Project>
</Workspace>
Using workspace = TestWorkspaceFactory.CreateWorkspace(markup)
Dim projects = workspace.CurrentSolution.Projects.ToArray()
Dim item1 = CreateItem(workspace, projects(0).DocumentIds.First())
Dim item2 = CreateItem(workspace, projects(1).DocumentIds.First())
Dim provider = New TestTodoListProvider()
Dim tableManagerProvider = New TestTableManagerProvider()
Dim table = New VisualStudioTodoListTable(workspace, provider, tableManagerProvider)
provider.Items = New TodoItem() {item1, item2}
provider.RaiseTodoListUpdated(workspace)
Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager)
Dim source = DirectCast(manager.Sources.First(), AbstractRoslynTableDataSource(Of TodoItem))
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 filename As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.DocumentName, filename))
Assert.Equal("test1", filename)
Dim projectname As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName, projectname))
Assert.Equal("Proj1, Proj2", projectname)
Dim projectnames As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName + "s", projectnames))
Assert.Equal(2, DirectCast(projectnames, String()).Length)
Dim projectguid As Object = Nothing
Assert.False(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectGuid, projectguid))
End Using
End Sub
Private Function CreateItem(workspace As Workspace, documentId As DocumentId) As TodoItem
Return New TodoItem(0, "test", workspace, documentId, 10, 10, 20, 20, Nothing, "test1")
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册