提交 b0b10dea 编写于 作者: M manishv

Fix for bug 1047892: Failed User Analyzer shouldn't log failure message multiple times.

Fix is to suppress duplicate analyzer exception diagnostics in each compilation. (changeset 1352343)
上级 f0a10528
......@@ -246,6 +246,35 @@ public override void Initialize(AnalysisContext analysisContext)
}
}
// Throws an exception on every AnalyzeSymbol on named types
protected class ThrowExceptionForEachNamedTypeAnalyzer : DiagnosticAnalyzer
{
public const string Id = "ThrowException";
private static DiagnosticDescriptor rule = GetRule(Id);
public ThrowExceptionForEachNamedTypeAnalyzer()
{
}
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(rule);
}
}
public override void Initialize(AnalysisContext analysisContext)
{
analysisContext.RegisterSymbolAction(
(context) =>
{
throw new Exception("ThrowExceptionAnalyzer exception");
},
SymbolKind.NamedType);
}
}
protected static DiagnosticDescriptor GetRule(string id)
{
return new DiagnosticDescriptor(
......
......@@ -1105,6 +1105,27 @@ End Class
Diagnostic("TypeDeclaration", "C").WithLocation(9, 7));
}
[Fact]
public void SuppressDuplicateAnalyzerExceptionDiagnostics()
{
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = (ex, a) => true;
VerifyCSharp(@"
public class C
{
}
public class C1
{
}
public class C2
{
}
",
new[] { new ThrowExceptionForEachNamedTypeAnalyzer() },
continueOnAnalyzerException,
Diagnostic("AnalyzerDriver", null).WithLocation(1, 1));
}
#endregion
protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, params DiagnosticDescription[] diagnostics)
......@@ -1112,6 +1133,11 @@ protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, param
Verify(source, LanguageNames.CSharp, analyzers, diagnostics);
}
protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, params DiagnosticDescription[] diagnostics)
{
Verify(source, LanguageNames.CSharp, analyzers, diagnostics, continueOnAnalyzerException);
}
protected void VerifyTokenDiagnosticsCSharp(string markup, params DiagnosticDescription[] diagnostics)
{
VerifyTokenDiagnostics(markup, LanguageNames.CSharp, diagnostics);
......@@ -1120,7 +1146,7 @@ protected void VerifyTokenDiagnosticsCSharp(string markup, params DiagnosticDesc
protected void VerifyBasic(string source, string rootNamespace, DiagnosticAnalyzer[] analyzers, params DiagnosticDescription[] diagnostics)
{
Assert.False(string.IsNullOrWhiteSpace(rootNamespace), string.Format("Invalid root namespace '{0}'", rootNamespace));
Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, rootNamespace);
Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, continueOnAnalyzerException: null, rootNamespace: rootNamespace);
}
protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, params DiagnosticDescription[] diagnostics)
......@@ -1128,16 +1154,21 @@ protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, params
Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics);
}
protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, params DiagnosticDescription[] diagnostics)
{
Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, continueOnAnalyzerException);
}
protected void VerifyTokenDiagnosticsBasic(string markup, params DiagnosticDescription[] diagnostics)
{
VerifyTokenDiagnostics(markup, LanguageNames.VisualBasic, diagnostics);
}
protected virtual void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] diagnostics, string rootNamespace = null)
protected virtual void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] diagnostics, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = null, string rootNamespace = null)
{
Assert.True(analyzers != null && analyzers.Length > 0, "Must specify at least one diagnostic analyzer to test suppression");
var compilation = CreateCompilation(source, language, analyzers, rootNamespace);
compilation.VerifyAnalyzerDiagnostics(analyzers, expected: diagnostics);
compilation.VerifyAnalyzerDiagnostics(analyzers, continueOnAnalyzerException: continueOnAnalyzerException, expected: diagnostics);
}
// Generate a diagnostic on every token in the specified spans, and verify that only the specified diagnostics are not suppressed
......
namespace Microsoft.CodeAnalysis
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis
{
public static class WellKnownDiagnosticTags
{
......@@ -26,5 +28,10 @@ public static class WellKnownDiagnosticTags
/// Indicates that the diagnostic is not configurable in the ruleset editor.
/// </summary>
public const string NotConfigurable = "NotConfigurable";
/// <summary>
/// Indicates that the diagnostic is related to an exception thrown by a <see cref="DiagnosticAnalyzer"/>.
/// </summary>
public const string AnalyzerException = "AnalyzerException";
}
}
......@@ -271,7 +271,8 @@ private async Task ProcessEventsAsync(CancellationToken cancellationToken)
"diagnostic analyzer worker threw an exception " + ex,
category: Diagnostic.CompilerDiagnosticCategory,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.AnalyzerException);
addDiagnostic(Diagnostic.Create(desc, Location.None));
}
}
......@@ -414,7 +415,7 @@ internal protected Action<Diagnostic> GetDiagnosticSinkWithSuppression(ISymbol s
if (d != null)
{
var suppressMessageState = suppressMessageStateByCompilation.GetValue(compilation, (c) => new SuppressMessageAttributeState(c));
if (!suppressMessageState.IsDiagnosticSuppressed(d.Id, locationOpt: d.Location, symbolOpt: symbolOpt))
if (!suppressMessageState.IsDiagnosticSuppressed(d, symbolOpt: symbolOpt))
{
DiagnosticQueue.Enqueue(d);
}
......@@ -447,7 +448,7 @@ public static IEnumerable<Diagnostic> GetEffectiveDiagnostics(IEnumerable<Diagno
if (diagnostic != null)
{
var effectiveDiagnostic = compilation.FilterDiagnostic(diagnostic);
if (effectiveDiagnostic != null && !suppressMessageState.IsDiagnosticSuppressed(effectiveDiagnostic.Id, effectiveDiagnostic.Location))
if (effectiveDiagnostic != null && !suppressMessageState.IsDiagnosticSuppressed(effectiveDiagnostic))
{
yield return effectiveDiagnostic;
}
......@@ -607,7 +608,8 @@ private static DiagnosticDescriptor GetDiagnosticDescriptor(string analyzerName,
string.Format(CodeAnalysisResources.CompilerAnalyzerThrows, analyzerName, message),
category: Diagnostic.CompilerDiagnosticCategory,
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true);
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.AnalyzerException);
}
public void Dispose()
......
......@@ -7,6 +7,7 @@
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System;
namespace Microsoft.CodeAnalysis.Diagnostics
{
......@@ -26,6 +27,7 @@ internal partial class SuppressMessageAttributeState
private GlobalSuppressions lazyGlobalSuppressions;
private ConcurrentDictionary<ISymbol, ImmutableArray<string>> localSuppressionsBySymbol = new ConcurrentDictionary<ISymbol, ImmutableArray<string>>();
private ISymbol lazySuppressMessageAttribute;
private ConcurrentSet<string> faultedAnalyzerMessages;
private class GlobalSuppressions
{
......@@ -71,16 +73,29 @@ public SuppressMessageAttributeState(Compilation compilation)
this.compilation = compilation;
}
public bool IsDiagnosticSuppressed(string id, Location locationOpt = null, ISymbol symbolOpt = null)
public bool IsDiagnosticSuppressed(Diagnostic diagnostic, ISymbol symbolOpt = null)
{
Debug.Assert(id != null);
// Suppress duplicate analyzer exception diagnostics from the analyzer driver.
if (diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.AnalyzerException))
{
if (faultedAnalyzerMessages == null)
{
Interlocked.CompareExchange(ref this.faultedAnalyzerMessages, new ConcurrentSet<string>(), null);
}
var message = diagnostic.GetMessage();
if (!faultedAnalyzerMessages.Add(message))
{
return true;
}
}
if (symbolOpt != null && IsDiagnosticSuppressed(id, symbolOpt))
if (symbolOpt != null && IsDiagnosticSuppressed(diagnostic.Id, symbolOpt))
{
return true;
}
return IsDiagnosticSuppressed(id, locationOpt ?? Location.None);
return IsDiagnosticSuppressed(diagnostic.Id, diagnostic.Location);
}
private bool IsDiagnosticSuppressed(string id, ISymbol symbol)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册