Skip to content

Commit

Permalink
Document load failures - restore reverted PR #40044 (#40815)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat authored Jan 13, 2020
1 parent 023183d commit 94d1e80
Show file tree
Hide file tree
Showing 53 changed files with 719 additions and 555 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,17 @@ public void TestHostAnalyzerOrdering()
var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace);
var analyzers = incrementalAnalyzer.GetAnalyzersTestOnly(project).ToArray();

Assert.Equal(typeof(CSharpCompilerDiagnosticAnalyzer), analyzers[0].GetType());
Assert.Equal(typeof(Analyzer), analyzers[1].GetType());
Assert.Equal(typeof(Priority0Analyzer), analyzers[2].GetType());
Assert.Equal(typeof(Priority1Analyzer), analyzers[3].GetType());
Assert.Equal(typeof(Priority10Analyzer), analyzers[4].GetType());
Assert.Equal(typeof(Priority15Analyzer), analyzers[5].GetType());
Assert.Equal(typeof(Priority20Analyzer), analyzers[6].GetType());
AssertEx.Equal(new[]
{
typeof(FileContentLoadAnalyzer),
typeof(CSharpCompilerDiagnosticAnalyzer),
typeof(Analyzer),
typeof(Priority0Analyzer),
typeof(Priority1Analyzer),
typeof(Priority10Analyzer),
typeof(Priority15Analyzer),
typeof(Priority20Analyzer)
}, analyzers.Select(a => a.GetType()));
}

[Fact]
Expand Down
102 changes: 102 additions & 0 deletions src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
' 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.IO
Imports System.Reflection
Imports System.Threading
Imports System.Threading.Tasks
Expand All @@ -27,6 +28,26 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests
Return New AnalyzerFileReference(fullPath, _assemblyLoader)
End Function

Private Class FailingTextLoader
Inherits TextLoader

Dim _path As String

Friend Overrides ReadOnly Property FilePath As String
Get
Return _path
End Get
End Property

Sub New(path As String)
_path = path
End Sub

Public Overrides Function LoadTextAndVersionAsync(workspace As Workspace, documentId As DocumentId, cancellationToken As CancellationToken) As Task(Of TextAndVersion)
Throw New InvalidDataException("Bad data!")
End Function
End Class

<WpfFact, Trait(Traits.Feature, Traits.Features.Diagnostics)>
Public Sub TestProjectAnalyzers()
Dim test = <Workspace>
Expand Down Expand Up @@ -515,6 +536,47 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests
End Using
End Sub

<Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)>
Public Async Function TestDiagnosticAnalyzer_FileLoadFailure() As Task
Dim test = <Workspace>
<Project Language="C#" CommonReferences="true">
<Document FilePath="Test.cs">
class Goo { void M() {} }
</Document>
</Project>
</Workspace>

Using workspace = TestWorkspace.CreateWorkspace(test)
Dim solution = workspace.CurrentSolution
Dim documentId = solution.Projects.Single().DocumentIds.Single()
solution = solution.WithDocumentTextLoader(documentId, New FailingTextLoader("Test.cs"), PreservationMode.PreserveIdentity)
workspace.ChangeSolution(solution)

Dim project = solution.Projects.Single()
Dim document = project.Documents.Single()

' analyzer throws an exception
Dim analyzer = New CodeBlockStartedAnalyzer(Of Microsoft.CodeAnalysis.CSharp.SyntaxKind)
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
project = project.AddAnalyzerReference(analyzerReference)

Dim exceptionDiagnosticsSource = New TestHostDiagnosticUpdateSource(workspace)
Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider)
Dim diagnosticService = New TestDiagnosticAnalyzerService(hostDiagnosticUpdateSource:=exceptionDiagnosticsSource, mefExportProvider.GetExports(Of PrimaryWorkspace).Single.Value)

Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace)
Dim span = (Await document.GetSyntaxRootAsync().ConfigureAwait(False)).FullSpan
Dim diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, span).ConfigureAwait(False)
Assert.Equal(1, diagnostics.Count())
Assert.True(diagnostics(0).Id = "IDE1100")
Assert.Equal(String.Format(WorkspacesResources.Error_reading_content_of_source_file_0_1, "Test.cs", "Bad data!"), diagnostics(0).Message)

' analyzer should not be executed on a file that can't be loaded
diagnostics = exceptionDiagnosticsSource.GetTestAccessor().GetReportedDiagnostics(analyzer)
Assert.Empty(diagnostics)
End Using
End Function

<WpfFact, WorkItem(937939, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/937939"), Trait(Traits.Feature, Traits.Features.Diagnostics)>
Public Sub TestOperationAnalyzers()
Dim test = <Workspace>
Expand Down Expand Up @@ -817,6 +879,46 @@ class AnonymousFunctions
End Using
End Function

<Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)>
Public Async Function TestStatefulCompilationAnalyzer_FileLoadFailure() As Task
Dim test = <Workspace>
<Project Language="C#" CommonReferences="true">
<Document FilePath="Test.cs">
class Goo { void M() {} }
</Document>
</Project>
</Workspace>

Using workspace = TestWorkspace.CreateWorkspace(test)
Dim solution = workspace.CurrentSolution
Dim documentId = solution.Projects.Single().DocumentIds.Single()
solution = solution.WithDocumentTextLoader(documentId, New FailingTextLoader("Test.cs"), PreservationMode.PreserveIdentity)
workspace.ChangeSolution(solution)

Dim project = solution.Projects.Single()
Dim document = project.Documents.Single()

Dim analyzer = New StatefulCompilationAnalyzer
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
project = project.AddAnalyzerReference(analyzerReference)

Dim projectDiagnostics = Await DiagnosticProviderTestUtilities.GetProjectDiagnosticsAsync(workspaceAnalyzerOpt:=Nothing, project:=project)

' The analyzer is invoked but the compilation does not contain a syntax tree that failed to load.
AssertEx.Equal(
{
"StatefulCompilationAnalyzerDiagnostic: Compilation NamedType Count: 0"
}, projectDiagnostics.Select(Function(d) d.Id & ": " & d.GetMessage()))

Dim documentDiagnostics = Await DiagnosticProviderTestUtilities.GetDocumentDiagnosticsAsync(workspaceAnalyzerOpt:=Nothing, document, TextSpan.FromBounds(0, 0))
AssertEx.Equal(
{
"IDE1100: " & String.Format(WorkspacesResources.Error_reading_content_of_source_file_0_1, "Test.cs", "Bad data!")
}, documentDiagnostics.Select(Function(d) d.Id & ": " & d.GetMessage()))

End Using
End Function

<WpfFact, WorkItem(9462, "https://github.com/dotnet/roslyn/issues/9462"), Trait(Traits.Feature, Traits.Features.Diagnostics)>
Public Sub TestMultiplePartialDefinitionsInAFile()
Dim test = <Workspace>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,6 @@ public async Task<IEnumerable<Diagnostic>> GetAllDiagnosticsAsync(Project projec
return diagnostics;
}

public async Task<IEnumerable<Diagnostic>> GetAllDiagnosticsAsync(DiagnosticAnalyzer workspaceAnalyzerOpt, Solution solution)
{
var diagnostics = new List<Diagnostic>();
foreach (var project in solution.Projects)
{
var projectDiagnostics = await GetAllDiagnosticsAsync(project);
diagnostics.AddRange(projectDiagnostics);
}

return diagnostics;
}

public Task<IEnumerable<Diagnostic>> GetDocumentDiagnosticsAsync(Document document, TextSpan span)
{
return GetDiagnosticsAsync(document.Project, document, span, getDocumentDiagnostics: true, getProjectDiagnostics: false);
Expand Down
25 changes: 19 additions & 6 deletions src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ internal static class AnalyzerHelper
private const string AnalyzerExceptionDiagnosticCategory = "Intellisense";

public static bool IsWorkspaceDiagnosticAnalyzer(this DiagnosticAnalyzer analyzer)
=> analyzer is DocumentDiagnosticAnalyzer || analyzer is ProjectDiagnosticAnalyzer;
=> analyzer is DocumentDiagnosticAnalyzer || analyzer is ProjectDiagnosticAnalyzer || analyzer == FileContentLoadAnalyzer.Instance;

public static bool IsBuiltInAnalyzer(this DiagnosticAnalyzer analyzer)
=> analyzer is IBuiltInAnalyzer || analyzer.IsWorkspaceDiagnosticAnalyzer() || analyzer.IsCompilerAnalyzer();
Expand Down Expand Up @@ -306,10 +306,24 @@ public static async Task<IEnumerable<DiagnosticData>> ComputeDiagnosticsAsync(
Document document,
DiagnosticAnalyzer analyzer,
AnalysisKind kind,
TextSpan? spanOpt,
TextSpan? span,
DiagnosticLogAggregator? logAggregator,
CancellationToken cancellationToken)
{
var loadDiagnostic = await document.State.GetLoadDiagnosticAsync(cancellationToken).ConfigureAwait(false);

if (analyzer == FileContentLoadAnalyzer.Instance)
{
return loadDiagnostic != null ?
SpecializedCollections.SingletonEnumerable(DiagnosticData.Create(loadDiagnostic, document)) :
SpecializedCollections.EmptyEnumerable<DiagnosticData>();
}

if (loadDiagnostic != null)
{
return SpecializedCollections.EmptyEnumerable<DiagnosticData>();
}

if (analyzer is DocumentDiagnosticAnalyzer documentAnalyzer)
{
var diagnostics = await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(
Expand Down Expand Up @@ -373,7 +387,7 @@ public static async Task<IEnumerable<DiagnosticData>> ComputeDiagnosticsAsync(
return SpecializedCollections.EmptyEnumerable<DiagnosticData>();
}

diagnostics = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, spanOpt, singleAnalyzer, cancellationToken).ConfigureAwait(false);
diagnostics = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, span, singleAnalyzer, cancellationToken).ConfigureAwait(false);

Debug.Assert(diagnostics.Length == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilationWithAnalyzers.Compilation).Count());
return diagnostics.ConvertToLocalDiagnostics(document);
Expand All @@ -385,7 +399,7 @@ public static async Task<IEnumerable<DiagnosticData>> ComputeDiagnosticsAsync(

public static bool SupportAnalysisKind(this DiagnosticAnalyzerService service, DiagnosticAnalyzer analyzer, string language, AnalysisKind kind)
{
// compiler diagnostic analyzer always support all kinds
// compiler diagnostic analyzer always supports all kinds:
if (service.IsCompilerDiagnosticAnalyzer(language, analyzer))
{
return true;
Expand All @@ -395,7 +409,7 @@ public static bool SupportAnalysisKind(this DiagnosticAnalyzerService service, D
{
AnalysisKind.Syntax => analyzer.SupportsSyntaxDiagnosticAnalysis(),
AnalysisKind.Semantic => analyzer.SupportsSemanticDiagnosticAnalysis(),
_ => Contract.FailWithReturn<bool>("shouldn't reach here"),
_ => throw ExceptionUtilities.UnexpectedValue(kind)
};
}

Expand All @@ -412,7 +426,6 @@ public static async Task<IEnumerable<Diagnostic>> ComputeDocumentDiagnosticAnaly

try
{

var analyzeAsync = kind switch
{
AnalysisKind.Syntax => analyzer.AnalyzeSyntaxAsync(document, cancellationToken),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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>
/// A dummy singleton analyzer. Its only purpose is to represent file content load failures in maps that are keyed by <see cref="DiagnosticAnalyzer"/>.
/// </summary>
internal sealed class FileContentLoadAnalyzer : DiagnosticAnalyzer
{
internal static readonly FileContentLoadAnalyzer Instance = new FileContentLoadAnalyzer();

private FileContentLoadAnalyzer()
{
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(WorkspaceDiagnosticDescriptors.ErrorReadingFileContent);

#pragma warning disable RS1026 // Enable concurrent execution
#pragma warning disable RS1025 // Configure generated code analysis
public sealed override void Initialize(AnalysisContext context) { }
#pragma warning restore RS1025 // Configure generated code analysis
#pragma warning restore RS1026 // Enable concurrent execution
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,7 @@ internal static class IDEDiagnosticIds
public const string NamingRuleId = "IDE1006";
public const string UnboundIdentifierId = "IDE1007";
public const string UnboundConstructorId = "IDE1008";

// Reserved for workspace error ids IDE1100-IDE1200 (see WorkspaceDiagnosticDescriptors)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ private async Task AnalyzeForKind(Document document, AnalysisKind kind, Cancella
private async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(
Document document, AnalysisKind kind, CancellationToken cancellationToken)
{
var loadDiagnostic = await document.State.GetLoadDiagnosticAsync(cancellationToken).ConfigureAwait(false);
if (loadDiagnostic != null)
{
return ImmutableArray.Create(DiagnosticData.Create(loadDiagnostic, document));
}

// given service must be DiagnosticAnalyzerService
var diagnosticService = (DiagnosticAnalyzerService)_service._analyzerService;

Expand All @@ -158,7 +164,7 @@ private async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(
foreach (var analyzer in analyzers)
{
builder.AddRange(await diagnosticService.ComputeDiagnosticsAsync(
compilationWithAnalyzers, document, analyzer, kind, spanOpt: null, logAggregator: null, cancellationToken).ConfigureAwait(false));
compilationWithAnalyzers, document, analyzer, kind, span: null, logAggregator: null, cancellationToken).ConfigureAwait(false));
}

return builder.ToImmutableAndFree();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysis

var others = diagnosticDataSerializer.ReadDiagnosticData(reader, project, document: null, cancellationToken);

var analysisResult = DiagnosticAnalysisResult.CreateFromSerialization(
var analysisResult = DiagnosticAnalysisResult.Create(
project,
version,
syntaxLocalMap,
Expand Down
Loading

0 comments on commit 94d1e80

Please sign in to comment.