提交 302123fd 编写于 作者: H Heejae Chang

guard us from build errors which are marked as CS or VBXXXX for a linked file...

guard us from build errors which are marked as CS or VBXXXX for a linked file that doesnt belong to current solution we have.

this can happen for a project that is just added or already removed or project is added but design time build is not finished.

previously, that could crash VS, but now we will handle it gracefully.

we should now also maintain orders build error is reported.
上级 cf3206d2
......@@ -138,7 +138,8 @@ public override ImmutableArray<ITrackingPoint> GetTrackingPoints(ImmutableArray<
private int GenerateDeduplicationKey(DiagnosticData diagnostic)
{
if (diagnostic.DataLocation == null ||
if (diagnostic.DocumentId == null ||
diagnostic.DataLocation == null ||
diagnostic.DataLocation.OriginalFilePath == null)
{
return diagnostic.GetHashCode();
......
......@@ -69,6 +69,13 @@ private ITableDataSource GetCurrentDataSource()
AddInitialTableSource(workspace.CurrentSolution, _liveTableSource);
}
/// this is for test only
internal VisualStudioDiagnosticListTable(Workspace workspace, IDiagnosticService diagnosticService, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) :
this(null, workspace, diagnosticService, errorSource, provider)
{
AddInitialTableSource(workspace.CurrentSolution, _buildTableSource);
}
private VisualStudioDiagnosticListTable(
SVsServiceProvider serviceProvider,
Workspace workspace,
......
......@@ -392,14 +392,17 @@ private void RaiseBuildStarted(bool started)
private class InprogressState
{
private int _incrementDoNotAccessDirectly = 0;
private readonly ExternalErrorDiagnosticUpdateSource _owner;
private readonly Solution _solution;
private readonly HashSet<ProjectId> _builtProjects = new HashSet<ProjectId>();
private readonly Dictionary<ProjectId, HashSet<DiagnosticData>> _projectMap = new Dictionary<ProjectId, HashSet<DiagnosticData>>();
private readonly Dictionary<DocumentId, HashSet<DiagnosticData>> _documentMap = new Dictionary<DocumentId, HashSet<DiagnosticData>>();
private readonly Dictionary<ProjectId, HashSet<string>> _diagnosticIdMap = new Dictionary<ProjectId, HashSet<string>>();
private readonly Dictionary<ProjectId, Dictionary<DiagnosticData, int>> _projectMap = new Dictionary<ProjectId, Dictionary<DiagnosticData, int>>();
private readonly Dictionary<DocumentId, Dictionary<DiagnosticData, int>> _documentMap = new Dictionary<DocumentId, Dictionary<DiagnosticData, int>>();
public InprogressState(ExternalErrorDiagnosticUpdateSource owner, Solution solution)
{
_owner = owner;
......@@ -445,7 +448,9 @@ public bool SupportedDiagnosticId(ProjectId projectId, string id)
public ImmutableArray<DiagnosticData> GetBuildDiagnostics()
{
return ImmutableArray.CreateRange(_projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d)));
// return errors in the order that is reported
return ImmutableArray.CreateRange(
_projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d)).OrderBy(kv => kv.Value).Select(kv => kv.Key));
}
public void Built(ProjectId projectId)
......@@ -475,7 +480,7 @@ public IEnumerable<ProjectId> GetProjectsWithoutErrors(Solution solution)
{
var diagnostics = ImmutableArray.CreateRange(
_projectMap.Where(kv => kv.Key == projectId).SelectMany(kv => kv.Value).Concat(
_documentMap.Where(kv => kv.Key.ProjectId == projectId).SelectMany(kv => kv.Value)).Where(liveDiagnosticChecker));
_documentMap.Where(kv => kv.Key.ProjectId == projectId).SelectMany(kv => kv.Value)).Select(kv => kv.Key).Where(liveDiagnosticChecker));
builder.Add(projectId, diagnostics);
}
......@@ -503,16 +508,24 @@ public void AddError(ProjectId key, DiagnosticData diagnostic)
AddError(_projectMap, key, diagnostic);
}
private void AddErrors<T>(Dictionary<T, HashSet<DiagnosticData>> map, T key, HashSet<DiagnosticData> diagnostics)
private void AddErrors<T>(Dictionary<T, Dictionary<DiagnosticData, int>> map, T key, HashSet<DiagnosticData> diagnostics)
{
var errors = GetErrorSet(map, key);
errors.UnionWith(diagnostics);
foreach (var diagnostic in diagnostics)
{
errors.Add(diagnostic, GetNextIncrement());
}
}
private void AddError<T>(Dictionary<T, HashSet<DiagnosticData>> map, T key, DiagnosticData diagnostic)
private void AddError<T>(Dictionary<T, Dictionary<DiagnosticData, int>> map, T key, DiagnosticData diagnostic)
{
var errors = GetErrorSet(map, key);
errors.Add(diagnostic);
errors.Add(diagnostic, GetNextIncrement());
}
private int GetNextIncrement()
{
return _incrementDoNotAccessDirectly++;
}
private IEnumerable<ProjectId> GetProjectIds()
......@@ -520,9 +533,9 @@ private IEnumerable<ProjectId> GetProjectIds()
return _documentMap.Keys.Select(k => k.ProjectId).Concat(_projectMap.Keys).Distinct();
}
private HashSet<DiagnosticData> GetErrorSet<T>(Dictionary<T, HashSet<DiagnosticData>> map, T key)
private Dictionary<DiagnosticData, int> GetErrorSet<T>(Dictionary<T, Dictionary<DiagnosticData, int>> map, T key)
{
return map.GetOrAdd(key, _ => new HashSet<DiagnosticData>(DiagnosticDataComparer.Instance));
return map.GetOrAdd(key, _ => new Dictionary<DiagnosticData, int>(DiagnosticDataComparer.Instance));
}
}
......
......@@ -9,12 +9,14 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Common
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.UnitTests
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
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.LanguageServices.Implementation.TaskList
Imports Microsoft.VisualStudio.Shell.TableControl
Imports Microsoft.VisualStudio.Shell.TableManager
Imports Roslyn.Test.Utilities
......@@ -627,7 +629,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
Await asyncListener.CreateWaitTask()
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)
......@@ -651,11 +652,101 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics
End Using
End Function
<WpfFact>
Public Async Function TestAggregatedDiagnosticCSErrorWithFileLocationButNoDocumentId() As Task
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 = TestWorkspace.Create(markup)
Dim asyncListener = New AsynchronousOperationListener()
Dim listeners = AsynchronousOperationListener.CreateListeners(ValueTuple.Create(FeatureAttribute.DiagnosticService, asyncListener))
Dim service = New DiagnosticService(listeners)
Dim analyzerService = New MyDiagnosticAnalyzerService(ImmutableDictionary(Of String, ImmutableArray(Of DiagnosticAnalyzer)).Empty, service, asyncListener)
Dim registration = New MockDiagnosticUpdateSourceRegistrationService()
Dim updateSource = New ExternalErrorDiagnosticUpdateSource(workspace, analyzerService, registration, asyncListener)
Dim tableManagerProvider = New TestTableManagerProvider()
Dim table = New VisualStudioDiagnosticListTable(workspace, service, updateSource, tableManagerProvider)
Dim document1 = workspace.CurrentSolution.Projects.First(Function(p) p.Name = "Proj1").Documents.First()
Dim document2 = workspace.CurrentSolution.Projects.First(Function(p) p.Name = "Proj2").Documents.First()
Dim diagnostic1 = CreateItem(workspace, document1.Id)
Dim diagnostic2 = CreateItem(workspace, document2.Id)
updateSource.AddNewErrors(document1.Project.Id,
New DiagnosticData(diagnostic1.Id, diagnostic1.Category, diagnostic1.Message, diagnostic1.ENUMessageForBingSearch,
diagnostic1.Severity, diagnostic1.IsEnabledByDefault, diagnostic1.WarningLevel,
diagnostic1.Workspace, diagnostic1.ProjectId,
New DiagnosticDataLocation(
Nothing,
diagnostic1.DataLocation.SourceSpan,
diagnostic1.DataLocation.OriginalFilePath,
diagnostic1.DataLocation.OriginalStartLine,
diagnostic1.DataLocation.OriginalStartColumn,
diagnostic1.DataLocation.OriginalEndLine,
diagnostic1.DataLocation.OriginalEndColumn,
diagnostic1.DataLocation.MappedFilePath,
diagnostic1.DataLocation.MappedStartLine,
diagnostic1.DataLocation.MappedStartColumn,
diagnostic1.DataLocation.MappedEndLine,
diagnostic1.DataLocation.MappedEndColumn),
diagnostic1.AdditionalLocations, diagnostic1.Title, diagnostic1.Description, diagnostic1.HelpLink,
diagnostic1.IsSuppressed, diagnostic1.CustomTags, diagnostic1.Properties))
updateSource.AddNewErrors(document2.Project.Id,
New DiagnosticData(diagnostic2.Id, diagnostic2.Category, diagnostic2.Message, diagnostic2.ENUMessageForBingSearch,
diagnostic2.Severity, diagnostic2.IsEnabledByDefault, diagnostic2.WarningLevel,
diagnostic2.Workspace, diagnostic2.ProjectId,
New DiagnosticDataLocation(
Nothing,
diagnostic2.DataLocation.SourceSpan,
diagnostic2.DataLocation.OriginalFilePath,
diagnostic2.DataLocation.OriginalStartLine,
diagnostic2.DataLocation.OriginalStartColumn,
diagnostic2.DataLocation.OriginalEndLine,
diagnostic2.DataLocation.OriginalEndColumn,
diagnostic2.DataLocation.MappedFilePath,
diagnostic2.DataLocation.MappedStartLine,
diagnostic2.DataLocation.MappedStartColumn,
diagnostic2.DataLocation.MappedEndLine,
diagnostic2.DataLocation.MappedEndColumn),
diagnostic2.AdditionalLocations, diagnostic2.Title, diagnostic2.Description, diagnostic2.HelpLink,
diagnostic2.IsSuppressed, diagnostic2.CustomTags, diagnostic2.Properties))
updateSource.OnSolutionBuild(Me, Shell.UIContextChangedEventArgs.From(False))
Await asyncListener.CreateWaitTask()
Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager)
Dim sinkAndSubscription = manager.Sinks_TestOnly.First()
Dim sink = DirectCast(sinkAndSubscription.Key, TestTableManagerProvider.TestTableManager.TestSink)
Dim snapshot = sink.Entries.First().GetCurrentSnapshot()
Assert.Equal(2, snapshot.Count)
Dim filename As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.DocumentName, filename))
Assert.Equal("test", filename)
Dim projectname As Object = Nothing
Assert.True(snapshot.TryGetValue(0, StandardTableKeyNames.ProjectName, projectname))
Assert.Equal("Proj1", projectname)
End Using
End Function
Private Sub RunCompilerAnalyzer(workspace As TestWorkspace, registrationService As IDiagnosticUpdateSourceRegistrationService, listener As IAsynchronousOperationListener)
Dim snapshot = workspace.CurrentSolution
Dim notificationService = New TestForegroundNotificationService()
Dim compilerAnalyzersMap = DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()
Dim analyzerService = New MyDiagnosticAnalyzerService(compilerAnalyzersMap, registrationService, listener)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册