提交 53ed0035 编写于 作者: M Manish Vasani

Merge pull request #4382 from mavasani/Issue3005

Report diagnostics for exceptions within the analyzer driver.
......@@ -950,7 +950,7 @@ public void TestReportingUnsupportedDiagnostic()
{
string source = @"";
var analyzers = new DiagnosticAnalyzer[] { new AnalyzerReportingUnsupportedDiagnostic() };
string message = new ArgumentException(string.Format(AnalyzerDriverResources.UnsupportedDiagnosticReported, AnalyzerReportingUnsupportedDiagnostic.UnsupportedDescriptor.Id), "diagnostic").Message;
string message = new ArgumentException(string.Format(CodeAnalysisResources.UnsupportedDiagnosticReported, AnalyzerReportingUnsupportedDiagnostic.UnsupportedDescriptor.Id), "diagnostic").Message;
CreateCompilationWithMscorlib45(source)
.VerifyDiagnostics()
......
......@@ -70,6 +70,34 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer Driver Failure.
/// </summary>
internal static string AnalyzerDriverFailure {
get {
return ResourceManager.GetString("AnalyzerDriverFailure", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer driver threw an exception of type &apos;{0}&apos; with message &apos;{1}&apos;..
/// </summary>
internal static string AnalyzerDriverThrows {
get {
return ResourceManager.GetString("AnalyzerDriverThrows", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer driver threw the following exception:
///&apos;{0}&apos;..
/// </summary>
internal static string AnalyzerDriverThrowsDescription {
get {
return ResourceManager.GetString("AnalyzerDriverThrowsDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Time (s).
/// </summary>
......@@ -278,7 +306,7 @@ internal class CodeAnalysisResources {
}
/// <summary>
/// Looks up a localized string similar to Compiler Analyzer Failure.
/// Looks up a localized string similar to Analyzer Failure.
/// </summary>
internal static string CompilerAnalyzerFailure {
get {
......@@ -287,7 +315,7 @@ internal class CodeAnalysisResources {
}
/// <summary>
/// Looks up a localized string similar to The Compiler Analyzer &apos;{0}&apos; threw an exception of type &apos;{1}&apos; with message &apos;{2}&apos;..
/// Looks up a localized string similar to Analyzer &apos;{0}&apos; threw an exception of type &apos;{1}&apos; with message &apos;{2}&apos;..
/// </summary>
internal static string CompilerAnalyzerThrows {
get {
......
......@@ -355,14 +355,24 @@
<value>An error occurred while loading the included rule set file {0} - {1}</value>
</data>
<data name="CompilerAnalyzerFailure" xml:space="preserve">
<value>Compiler Analyzer Failure</value>
<value>Analyzer Failure</value>
</data>
<data name="CompilerAnalyzerThrows" xml:space="preserve">
<value>The Compiler Analyzer '{0}' threw an exception of type '{1}' with message '{2}'.</value>
<value>Analyzer '{0}' threw an exception of type '{1}' with message '{2}'.</value>
</data>
<data name="CompilerAnalyzerThrowsDescription" xml:space="preserve">
<value>Analyzer '{0}' threw the following exception:
'{1}'.</value>
</data>
<data name="AnalyzerDriverFailure" xml:space="preserve">
<value>Analyzer Driver Failure</value>
</data>
<data name="AnalyzerDriverThrows" xml:space="preserve">
<value>Analyzer driver threw an exception of type '{0}' with message '{1}'.</value>
</data>
<data name="AnalyzerDriverThrowsDescription" xml:space="preserve">
<value>Analyzer driver threw the following exception:
'{0}'.</value>
</data>
<data name="PEImageDoesntContainManagedMetadata" xml:space="preserve">
<value>PE image doesn't contain managed metadata.</value>
......
......@@ -257,7 +257,11 @@ private async Task ExecutePrimaryAnalysisTaskAsync(AnalysisScope analysisScope,
await WhenInitializedTask.ConfigureAwait(false);
if (!WhenInitializedTask.IsCanceled)
if (WhenInitializedTask.IsFaulted)
{
OnDriverException(WhenInitializedTask, this.analyzerExecutor, analysisScope.Analyzers);
}
else if (!WhenInitializedTask.IsCanceled)
{
this.analyzerExecutor = this.analyzerExecutor.WithCancellationToken(cancellationToken);
......@@ -265,6 +269,25 @@ private async Task ExecutePrimaryAnalysisTaskAsync(AnalysisScope analysisScope,
}
}
private static void OnDriverException(Task faultedTask, AnalyzerExecutor analyzerExecutor, ImmutableArray<DiagnosticAnalyzer> analyzers)
{
Debug.Assert(faultedTask.IsFaulted);
var innerException = faultedTask.Exception?.InnerException;
if (innerException == null || innerException is OperationCanceledException)
{
return;
}
var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(innerException);
// Just pick the first analyzer from the scope for the onAnalyzerException callback.
// The exception diagnostic's message and description will not include the analyzer, but explicitly state its a driver exception.
var analyzer = analyzers[0];
analyzerExecutor.OnAnalyzerException(innerException, analyzer, diagnostic);
}
private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, AnalysisState analysisStateOpt)
{
if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis)
......@@ -357,6 +380,11 @@ public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync()
if (CompilationEventQueue.IsCompleted)
{
await this.WhenCompletedTask.ConfigureAwait(false);
if (this.WhenCompletedTask.IsFaulted)
{
OnDriverException(this.WhenCompletedTask, this.analyzerExecutor, this.analyzers);
}
}
Diagnostic d;
......@@ -805,7 +833,7 @@ internal async Task<ActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyze
{
var executor = analyzerExecutor.WithCancellationToken(cancellationToken);
var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, executor).ConfigureAwait(false);
return new ActionCounts(analyzerActions);
return ActionCounts.Create(analyzerActions);
}
/// <summary>
......@@ -1298,14 +1326,4 @@ private static IEnumerable<SyntaxNode> GetSyntaxNodesToAnalyze(SyntaxNode declar
}
}
}
internal static class AnalyzerDriverResources
{
internal static string AnalyzerFailure => CodeAnalysisResources.CompilerAnalyzerFailure;
internal static string AnalyzerThrows => CodeAnalysisResources.CompilerAnalyzerThrows;
internal static string AnalyzerThrowsDescription => CodeAnalysisResources.CompilerAnalyzerThrowsDescription;
internal static string ArgumentElementCannotBeNull => CodeAnalysisResources.ArgumentElementCannotBeNull;
internal static string ArgumentCannotBeEmpty => CodeAnalysisResources.ArgumentCannotBeEmpty;
internal static string UnsupportedDiagnosticReported => CodeAnalysisResources.UnsupportedDiagnosticReported;
}
}
......@@ -27,6 +27,7 @@ internal class AnalyzerExecutor
// internal for testing purposes only.
internal const string AnalyzerExceptionDiagnosticId = "AD0001";
internal const string AnalyzerDriverExceptionDiagnosticId = "AD0002";
private readonly Compilation _compilation;
private readonly AnalyzerOptions _analyzerOptions;
......@@ -783,25 +784,43 @@ internal static bool IsCanceled(Exception ex, CancellationToken cancellationToke
internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Exception e)
{
var analyzerName = analyzer.ToString();
var title = CodeAnalysisResources.CompilerAnalyzerFailure;
var messageFormat = CodeAnalysisResources.CompilerAnalyzerThrows;
var messageArguments = new[] { analyzerName, e.GetType().ToString(), e.Message };
var description = string.Format(CodeAnalysisResources.CompilerAnalyzerThrowsDescription, analyzerName, e.ToString());
return CreateExceptionDiagnostic(AnalyzerExceptionDiagnosticId, title, description, messageFormat, messageArguments);
}
internal static Diagnostic CreateDriverExceptionDiagnostic(Exception e)
{
var title = CodeAnalysisResources.AnalyzerDriverFailure;
var messageFormat = CodeAnalysisResources.AnalyzerDriverThrows;
var messageArguments = new[] { e.GetType().ToString(), e.Message };
var description = string.Format(CodeAnalysisResources.AnalyzerDriverThrowsDescription, e.ToString());
return CreateExceptionDiagnostic(AnalyzerDriverExceptionDiagnosticId, title, description, messageFormat, messageArguments);
}
private static Diagnostic CreateExceptionDiagnostic(string id, string title, string description, string messageFormat, string[] messageArguments)
{
// TODO: It is not ideal to create a new descriptor per analyzer exception diagnostic instance.
// However, until we add a LongMessage field to the Diagnostic, we are forced to park the instance specific description onto the Descriptor's Description field.
// This requires us to create a new DiagnosticDescriptor instance per diagnostic instance.
var descriptor = new DiagnosticDescriptor(AnalyzerExceptionDiagnosticId,
title: AnalyzerDriverResources.AnalyzerFailure,
messageFormat: AnalyzerDriverResources.AnalyzerThrows,
description: string.Format(AnalyzerDriverResources.AnalyzerThrowsDescription, analyzerName, e.ToString()),
var descriptor = new DiagnosticDescriptor(
id,
title,
messageFormat,
description: description,
category: DiagnosticCategory,
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.AnalyzerException);
return Diagnostic.Create(descriptor, Location.None, analyzerName, e.GetType(), e.Message);
return Diagnostic.Create(descriptor, Location.None, messageArguments);
}
internal static bool IsAnalyzerExceptionDiagnostic(Diagnostic diagnostic)
{
if (diagnostic.Id == AnalyzerExceptionDiagnosticId)
if (diagnostic.Id == AnalyzerExceptionDiagnosticId || diagnostic.Id == AnalyzerDriverExceptionDiagnosticId)
{
#pragma warning disable RS0013 // Its ok to realize the Descriptor for analyzer exception diagnostics, which are descriptor based and also rare.
foreach (var tag in diagnostic.Descriptor.CustomTags)
......
......@@ -9,9 +9,20 @@ public static partial class AnalyzerTelemetry
/// </summary>
public class ActionCounts
{
internal ActionCounts(AnalyzerActions analyzerActions)
internal static ActionCounts Empty = new ActionCounts(AnalyzerActions.Empty);
internal static ActionCounts Create(AnalyzerActions analyzerActions)
{
if (analyzerActions == null)
{
return Empty;
}
return new ActionCounts(analyzerActions);
}
private ActionCounts(AnalyzerActions analyzerActions)
{
analyzerActions = analyzerActions ?? AnalyzerActions.Empty;
CompilationStartActionsCount = analyzerActions.CompilationStartActionsCount;
CompilationEndActionsCount = analyzerActions.CompilationEndActionsCount;
CompilationActionsCount = analyzerActions.CompilationActionsCount;
......
......@@ -500,7 +500,7 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope,
// Driver must have been initialized.
Debug.Assert(driver.WhenInitializedTask != null);
Debug.Assert(driver.WhenInitializedTask.IsCompleted);
Debug.Assert(!driver.WhenInitializedTask.IsCanceled);
cancellationToken.ThrowIfCancellationRequested();
......@@ -619,7 +619,7 @@ private void FreeDriver(AnalyzerDriver driver)
/// </summary>
private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken)
{
Debug.Assert(driver.WhenInitializedTask.IsCompleted);
Debug.Assert(!driver.WhenInitializedTask.IsCanceled);
if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope))
{
......
......@@ -34,7 +34,7 @@ internal static void VerifyArguments(Diagnostic diagnostic, Func<Diagnostic, boo
if (!isSupportedDiagnostic(diagnostic))
{
throw new ArgumentException(string.Format(AnalyzerDriverResources.UnsupportedDiagnosticReported, diagnostic.Id), nameof(diagnostic));
throw new ArgumentException(string.Format(CodeAnalysisResources.UnsupportedDiagnosticReported, diagnostic.Id), nameof(diagnostic));
}
}
......@@ -55,7 +55,7 @@ private static void VerifySymbolKinds(ImmutableArray<SymbolKind> symbolKinds)
if (symbolKinds.IsEmpty)
{
throw new ArgumentException(AnalyzerDriverResources.ArgumentCannotBeEmpty, nameof(symbolKinds));
throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(symbolKinds));
}
}
......@@ -69,7 +69,7 @@ private static void VerifySyntaxKinds<TLanguageKindEnum>(ImmutableArray<TLanguag
if (syntaxKinds.IsEmpty)
{
throw new ArgumentException(AnalyzerDriverResources.ArgumentCannotBeEmpty, nameof(syntaxKinds));
throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(syntaxKinds));
}
}
}
......
......@@ -47,7 +47,7 @@ public class Class6<TTypeParameter>
AnalyzeDocumentCore(GetCSharpDiagnosticAnalyzer(), documentsAndSpan.Item1[0], diagnostics.Add, null, logAnalyzerExceptionAsDiagnostics: true);
Assert.True(diagnostics.Count > 0);
Assert.Equal(
$"info AD0001: The Compiler Analyzer '{GetCSharpDiagnosticAnalyzer().GetType()}' threw an exception of type 'System.NotImplementedException' with message 'The method or operation is not implemented.'.",
$"info AD0001: Analyzer '{GetCSharpDiagnosticAnalyzer().GetType()}' threw an exception of type 'System.NotImplementedException' with message 'The method or operation is not implemented.'.",
(new DiagnosticFormatter()).Format(diagnostics[0], EnsureEnglishUICulture.PreferredOrNull));
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal static class AnalyzerDriverResources
{
internal static string AnalyzerFailure => FeaturesResources.UserDiagnosticAnalyzerFailure;
internal static string AnalyzerThrows => FeaturesResources.UserDiagnosticAnalyzerThrows;
internal static string AnalyzerThrowsDescription => FeaturesResources.UserDiagnosticAnalyzerThrowsDescription;
internal static string ArgumentElementCannotBeNull => FeaturesResources.ArgumentElementCannotBeNull;
internal static string ArgumentCannotBeEmpty => FeaturesResources.ArgumentCannotBeEmpty;
internal static string UnsupportedDiagnosticReported => FeaturesResources.UnsupportedDiagnosticReported;
}
}
......@@ -104,9 +104,9 @@ internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer
// However, until we add a LongMessage field to the Diagnostic, we are forced to park the instance specific description onto the Descriptor's Description field.
// This requires us to create a new DiagnosticDescriptor instance per diagnostic instance.
var descriptor = new DiagnosticDescriptor(AnalyzerExceptionDiagnosticId,
title: AnalyzerDriverResources.AnalyzerFailure,
messageFormat: AnalyzerDriverResources.AnalyzerThrows,
description: string.Format(AnalyzerDriverResources.AnalyzerThrowsDescription, analyzerName, e.ToString()),
title: FeaturesResources.UserDiagnosticAnalyzerFailure,
messageFormat: FeaturesResources.UserDiagnosticAnalyzerThrows,
description: string.Format(FeaturesResources.UserDiagnosticAnalyzerThrowsDescription, analyzerName, e.ToString()),
category: AnalyzerExceptionDiagnosticCategory,
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
......
......@@ -178,7 +178,6 @@
<Compile Include="Completion\Providers\RecommendedKeyword.cs" />
<Compile Include="Completion\SuggestionMode\SuggestionModeCompletionItemRules.cs" />
<Compile Include="Completion\SuggestionMode\SuggestionModeCompletionProvider.cs" />
<Compile Include="Diagnostics\AnalyzerDriverResources.cs" />
<Compile Include="Diagnostics\AnalyzerHelper.cs" />
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource.cs" />
<Compile Include="Diagnostics\AnalyzerUpdateArgsId.cs" />
......
......@@ -2133,7 +2133,7 @@ internal class FeaturesResources {
}
/// <summary>
/// Looks up a localized string similar to The User Diagnostic Analyzer &apos;{0}&apos; threw an exception of type &apos;{1}&apos; with message &apos;{2}&apos;..
/// Looks up a localized string similar to Analyzer &apos;{0}&apos; threw an exception of type &apos;{1}&apos; with message &apos;{2}&apos;..
/// </summary>
internal static string UserDiagnosticAnalyzerThrows {
get {
......
......@@ -658,7 +658,7 @@ Do you want to continue?</value>
<value>User Diagnostic Analyzer Failure.</value>
</data>
<data name="UserDiagnosticAnalyzerThrows" xml:space="preserve">
<value>The User Diagnostic Analyzer '{0}' threw an exception of type '{1}' with message '{2}'.</value>
<value>Analyzer '{0}' threw an exception of type '{1}' with message '{2}'.</value>
</data>
<data name="UserDiagnosticAnalyzerThrowsDescription" xml:space="preserve">
<value>Analyzer '{0}' threw the following exception:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册