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

implemented analyzer exception description to contain context information

上级 deeb46ec
......@@ -67,6 +67,7 @@
<Compile Include="Compilation\ScriptCompilationInfo.cs" />
<Compile Include="DiagnosticAnalyzer\AnalyzerDriver.GeneratedCodeUtilities.cs" />
<Compile Include="DiagnosticAnalyzer\AnalyzerDriver.CompilationData.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisContextInfo.cs" />
<Compile Include="DiagnosticAnalyzer\SuppressMessageInfo.cs" />
<Compile Include="Diagnostic\SuppressionInfo.cs" />
<Compile Include="InternalUtilities\SetWithInsertionOrder.cs" />
......
......@@ -432,6 +432,16 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Exception occurred with following context:
///{0}.
/// </summary>
internal static string ExceptionContext {
get {
return ResourceManager.GetString("ExceptionContext", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Expected non-empty public key.
/// </summary>
......
......@@ -506,4 +506,8 @@
<data name="SharedArgumentMissing" xml:space="preserve">
<value>Value for argument '/shared:' must not be empty</value>
</data>
<data name="ExceptionContext" xml:space="preserve">
<value>Exception occurred with following context:
{0}</value>
</data>
</root>
\ No newline at end of file
// 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.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.Semantics;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Diagnostics
{
internal struct AnalysisContextInfo
{
private readonly Compilation _compilation;
private readonly IOperation _operation;
private readonly ISymbol _symbol;
private readonly SyntaxTree _tree;
private readonly SyntaxNode _node;
public AnalysisContextInfo(Compilation compilation) :
this(compilation: compilation, operation: null, symbol: null, tree: null, node: null)
{
}
public AnalysisContextInfo(SemanticModel model) :
this(model.Compilation, model.SyntaxTree)
{
}
public AnalysisContextInfo(Compilation compilation, ISymbol symbol) :
this(compilation: compilation, operation: null, symbol: symbol, tree: null, node: null)
{
}
public AnalysisContextInfo(Compilation compilation, SyntaxTree tree) :
this(compilation: compilation, operation: null, symbol: null, tree: tree, node: null)
{
}
public AnalysisContextInfo(Compilation compilation, SyntaxNode node) :
this(compilation: compilation, operation: null, symbol: null, tree: node.SyntaxTree, node: node)
{
}
public AnalysisContextInfo(Compilation compilation, IOperation operation) :
this(compilation: compilation, operation: operation, symbol: null, tree: operation.Syntax.SyntaxTree, node: operation.Syntax)
{
}
public AnalysisContextInfo(Compilation compilation, ISymbol symbol, SyntaxNode node) :
this(compilation: compilation, operation: null, symbol: symbol, tree: node.SyntaxTree, node: node)
{
}
public AnalysisContextInfo(
Compilation compilation,
IOperation operation,
ISymbol symbol,
SyntaxTree tree,
SyntaxNode node)
{
_compilation = compilation;
_operation = operation;
_symbol = symbol;
_tree = tree;
_node = node;
}
public string GetContext()
{
var sb = new StringBuilder();
if (_compilation?.AssemblyName != null)
{
sb.AppendLine($"{nameof(Compilation)}: {_compilation.AssemblyName}");
}
if (_operation != null)
{
sb.AppendLine($"{nameof(IOperation)}: {_operation.Kind}");
}
if (_symbol?.Name != null)
{
sb.AppendLine($"{nameof(ISymbol)}: {_symbol.Name} ({_symbol.Kind})");
}
if (_tree?.FilePath != null)
{
sb.AppendLine($"{nameof(SyntaxTree)}: {_tree.FilePath}");
}
if (_node != null)
{
// can't use Kind since that is language specific. instead will output typename.
sb.AppendLine($"{nameof(SyntaxNode)}: \"{GetValueText(_node)}\" {_node.GetType().Name}@{_node.Span.ToString()}");
}
return sb.ToString();
}
private string GetValueText(SyntaxNode node)
{
const int cutoff = 30;
if (node.Span.Length < cutoff)
{
return RemoveNewLines(node.ToString());
}
// get actual text without creating texts for all sub nodes.
return RemoveNewLines(node.GetText().ToString(new TextSpan(node.Span.Start - node.FullSpan.Start, cutoff))) + " ...";
}
private string RemoveNewLines(string text)
{
return text.Replace(Environment.NewLine, @"\r\n");
}
}
}
......@@ -196,7 +196,8 @@ public AnalyzerExecutor WithCancellationToken(CancellationToken cancellationToke
public void ExecuteInitializeMethod(DiagnosticAnalyzer analyzer, HostSessionStartAnalysisScope sessionScope)
{
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate.
ExecuteAndCatchIfThrows(analyzer, () => analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope)));
ExecuteAndCatchIfThrows(analyzer,
() => analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope)));
}
/// <summary>
......@@ -209,8 +210,10 @@ public void ExecuteCompilationStartActions(ImmutableArray<CompilationStartAnalyz
foreach (var startAction in actions)
{
_cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(startAction.Analyzer,
() => startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, _compilation, _analyzerOptions, _cancellationToken)));
() => startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, _compilation, _analyzerOptions, _cancellationToken)),
new AnalysisContextInfo(_compilation));
}
}
......@@ -258,8 +261,10 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
if (ShouldExecuteAction(analyzerStateOpt, endAction))
{
ExecuteAndCatchIfThrows(endAction.Analyzer,
() => endAction.Action(new CompilationAnalysisContext(_compilation, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(endAction.Analyzer, d), _cancellationToken)));
() => endAction.Action(new CompilationAnalysisContext(
_compilation, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(endAction.Analyzer, d), _cancellationToken)),
new AnalysisContextInfo(_compilation));
analyzerStateOpt?.ProcessedActions.Add(endAction);
}
......@@ -332,8 +337,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
_cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(symbolAction.Analyzer,
() => action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken)));
() => action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken)),
new AnalysisContextInfo(_compilation, symbol));
analyzerStateOpt?.ProcessedActions.Add(symbolAction);
}
......@@ -399,7 +405,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
// Catch Exception from action.
ExecuteAndCatchIfThrows(semanticModelAction.Analyzer,
() => semanticModelAction.Action(new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken)));
d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken)),
new AnalysisContextInfo(semanticModel));
analyzerStateOpt?.ProcessedActions.Add(semanticModelAction);
}
......@@ -462,7 +469,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
// Catch Exception from action.
ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer,
() => syntaxTreeAction.Action(new SyntaxTreeAnalysisContext(tree, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _compilation, _cancellationToken)));
d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _compilation, _cancellationToken)),
new AnalysisContextInfo(_compilation, tree));
analyzerStateOpt?.ProcessedActions.Add(syntaxTreeAction);
}
......@@ -483,7 +491,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
{
var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, semanticModel, _analyzerOptions, addDiagnostic,
d => IsSupportedDiagnostic(syntaxNodeAction.Analyzer, d), _cancellationToken);
ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer, () => syntaxNodeAction.Action(syntaxNodeContext));
ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer,
() => syntaxNodeAction.Action(syntaxNodeContext),
new AnalysisContextInfo(_compilation, node));
analyzerStateOpt?.ProcessedActions.Add(syntaxNodeAction);
}
......@@ -501,7 +511,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
if (ShouldExecuteAction(analyzerStateOpt, operationAction))
{
var operationContext = new OperationAnalysisContext(operation, _analyzerOptions, addDiagnostic, d => IsSupportedDiagnostic(operationAction.Analyzer, d), semanticModel, _cancellationToken);
ExecuteAndCatchIfThrows(operationAction.Analyzer, () => operationAction.Action(operationContext));
ExecuteAndCatchIfThrows(operationAction.Analyzer,
() => operationAction.Action(operationContext),
new AnalysisContextInfo(_compilation, operation));
analyzerStateOpt?.ProcessedActions.Add(operationAction);
}
......@@ -658,7 +670,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
codeBlockStartAction.Action(blockStartContext);
codeBlockEndActions.AddAll(codeBlockScope.CodeBlockEndActions);
syntaxNodeActions.AddRange(codeBlockScope.SyntaxNodeActions);
});
},
new AnalysisContextInfo(_compilation, declaredSymbol, declaredNode));
}
else
{
......@@ -675,7 +688,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
operationBlockStartAction.Action(operationStartContext);
operationBlockEndActions.AddAll(operationBlockScope.OperationBlockEndActions);
operationActions.AddRange(operationBlockScope.OperationActions);
});
},
new AnalysisContextInfo(_compilation, declaredSymbol));
}
}
......@@ -736,7 +750,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
{
ExecuteAndCatchIfThrows(
codeBlockAction.Analyzer,
() => codeBlockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken)));
() => codeBlockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken)),
new AnalysisContextInfo(_compilation, declaredSymbol, declaredNode));
}
else
{
......@@ -745,7 +760,8 @@ private void ExecuteCompilationActionsCore(ImmutableArray<CompilationAnalyzerAct
{
ExecuteAndCatchIfThrows(
operationBlockAction.Analyzer,
() => operationBlockAction.Action(new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, semanticModel, _cancellationToken)));
() => operationBlockAction.Action(new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, semanticModel, _cancellationToken)),
new AnalysisContextInfo(_compilation, declaredSymbol));
}
}
......@@ -1019,23 +1035,23 @@ internal static bool CanHaveExecutableCodeBlock(ISymbol symbol)
}
}
internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyze)
internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyze, AnalysisContextInfo? info = null)
{
object gate = _getAnalyzerGateOpt?.Invoke(analyzer);
if (gate != null)
{
lock (gate)
{
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze);
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, info);
}
}
else
{
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze);
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, info);
}
}
private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action analyze)
private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action analyze, AnalysisContextInfo? info)
{
try
{
......@@ -1057,7 +1073,7 @@ private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action
catch (Exception e) when (ExceptionFilter(e))
{
// Diagnostic for analyzer exception.
var diagnostic = CreateAnalyzerExceptionDiagnostic(analyzer, e);
var diagnostic = CreateAnalyzerExceptionDiagnostic(analyzer, e, info);
try
{
_onAnalyzerException(e, analyzer, diagnostic);
......@@ -1084,17 +1100,28 @@ internal bool ExceptionFilter(Exception ex)
return true;
}
internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Exception e)
internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Exception e, AnalysisContextInfo? info = null)
{
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.CreateDiagnosticDescription());
var description = string.Format(CodeAnalysisResources.CompilerAnalyzerThrowsDescription, analyzerName, CreateDiagnosticDescription(info, e));
var descriptor = GetAnalyzerExceptionDiagnosticDescriptor(AnalyzerExceptionDiagnosticId, title, description, messageFormat);
return Diagnostic.Create(descriptor, Location.None, messageArguments);
}
private static string CreateDiagnosticDescription(AnalysisContextInfo? info, Exception e)
{
if (info == null)
{
return e.CreateDiagnosticDescription();
}
return string.Join(Environment.NewLine,
string.Format(CodeAnalysisResources.ExceptionContext, info?.GetContext()), e.CreateDiagnosticDescription());
}
internal static Diagnostic CreateDriverExceptionDiagnostic(Exception e)
{
var title = CodeAnalysisResources.AnalyzerDriverFailure;
......
......@@ -164,7 +164,7 @@ public async Task<bool> IsConcurrentAnalyzerAsync(DiagnosticAnalyzer analyzer, A
public async Task<GeneratedCodeAnalysisFlags> GetGeneratedCodeAnalysisFlagsAsync(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor)
{
var sessionScope = await GetSessionAnalysisScopeAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
return sessionScope.GetGeneratedCodeAnalysisFlags(analyzer);
return sessionScope.GetGeneratedCodeAnalysisFlags(analyzer);
}
/// <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册