未验证 提交 f7ce2154 编写于 作者: M Manish Vasani 提交者: GitHub

Merge pull request #36067 from mavasani/DiagnosticSuppressorForCompilerAndAnalyzerDiagnostics

Add new analyzer API (DiagnosticSuppressor) to allow programmatic sup…
......@@ -2693,9 +2693,9 @@ internal override StrongNameKeys StrongNameKeys
filterOpt: filterOpt,
cancellationToken: cancellationToken);
bool hasMethodBodyErrorOrWarningAsError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag);
bool hasMethodBodyError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag);
if (hasDeclarationErrors || hasMethodBodyErrorOrWarningAsError)
if (hasDeclarationErrors || hasMethodBodyError)
{
return false;
}
......
......@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
......@@ -17,7 +16,6 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
......@@ -9141,7 +9139,8 @@ private static int OccurrenceCount(string source, string word)
string[] additionalFlags = null,
int expectedInfoCount = 0,
int expectedWarningCount = 0,
int expectedErrorCount = 0)
int expectedErrorCount = 0,
params DiagnosticAnalyzer[] analyzers)
{
var args = new[] {
"/nologo", "/preferreduilang:en", "/t:library",
......@@ -9156,7 +9155,7 @@ private static int OccurrenceCount(string source, string word)
args = args.Append(additionalFlags);
}
var csc = CreateCSharpCompiler(null, sourceDir.Path, args);
var csc = CreateCSharpCompiler(null, sourceDir.Path, args, analyzers: analyzers.ToImmutableArrayOrEmpty());
var outWriter = new StringWriter(CultureInfo.InvariantCulture);
var exitCode = csc.Run(outWriter);
var output = outWriter.ToString();
......@@ -10789,6 +10788,311 @@ public void InvalidPathCharacterInPdbPath()
Assert.Equal(1, exitCode);
Assert.Contains("error CS2021: File name 'test\\?.pdb' is empty, contains invalid characters, has a drive specification without an absolute path, or is too long", outWriter.ToString(), StringComparison.Ordinal);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/36215")]
public void TestSuppression_CompilerParserWarningAsError()
{
string source = @"
class C
{
long M(int i)
{
// warning CS0078 : The 'l' suffix is easily confused with the digit '1' -- use 'L' for clarity
return 0l;
}
}
";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that parser warning CS0078 is reported.
var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("warning CS0078", output, StringComparison.Ordinal);
// Verify that parser warning CS0078 is reported as error for /warnaserror.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("error CS0078", output, StringComparison.Ordinal);
// Verify that parser warning CS0078 is suppressed with diagnostic suppressor even with /warnaserror
// and info diagnostic is logged with programmatic suppression information.
var suppressor = new DiagnosticSuppressorForId("CS0078");
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
additionalFlags: new[] { "/warnAsError" },
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: suppressor);
Assert.DoesNotContain($"error CS0078", output, StringComparison.Ordinal);
Assert.DoesNotContain($"warning CS0078", output, StringComparison.Ordinal);
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_LowercaseEllSuffix, "l"), Location.None).GetMessage(CultureInfo.InvariantCulture),
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestSuppression_CompilerSyntaxWarning()
{
// warning CS1522: Empty switch block
// NOTE: Empty switch block warning is reported by the C# language parser
string source = @"
class C
{
void M(int i)
{
switch (i)
{
}
}
}";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that compiler warning CS1522 is reported.
var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("warning CS1522", output, StringComparison.Ordinal);
// Verify that compiler warning CS1522 is suppressed with diagnostic suppressor
// and info diagnostic is logged with programmatic suppression information.
var suppressor = new DiagnosticSuppressorForId("CS1522");
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_EmptySwitch), Location.None).GetMessage(CultureInfo.InvariantCulture),
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification);
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: suppressor);
Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal);
Assert.Contains($"info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
// Verify that compiler warning CS1522 is reported as error for /warnaserror.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("error CS1522", output, StringComparison.Ordinal);
// Verify that compiler warning CS1522 is suppressed with diagnostic suppressor even with /warnaserror
// and info diagnostic is logged with programmatic suppression information.
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
additionalFlags: new[] { "/warnAsError" },
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: suppressor);
Assert.DoesNotContain($"error CS1522", output, StringComparison.Ordinal);
Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestSuppression_CompilerSemanticWarning()
{
string source = @"
class C
{
// warning CS0169: The field 'C.f' is never used
private readonly int f;
}";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that compiler warning CS0169 is reported.
var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("warning CS0169", output, StringComparison.Ordinal);
// Verify that compiler warning CS0169 is suppressed with diagnostic suppressor
// and info diagnostic is logged with programmatic suppression information.
var suppressor = new DiagnosticSuppressorForId("CS0169");
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.WRN_UnreferencedField, "C.f"), Location.None).GetMessage(CultureInfo.InvariantCulture),
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification);
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: suppressor);
Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
// Verify that compiler warning CS0169 is reported as error for /warnaserror.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("error CS0169", output, StringComparison.Ordinal);
// Verify that compiler warning CS0169 is suppressed with diagnostic suppressor even with /warnaserror
// and info diagnostic is logged with programmatic suppression information.
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, expectedErrorCount: 0,
additionalFlags: new[] { "/warnAsError" },
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: suppressor);
Assert.DoesNotContain($"error CS0169", output, StringComparison.Ordinal);
Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestNoSuppression_CompilerSyntaxError()
{
// error CS1001: Identifier expected
string source = @"
class { }";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that compiler syntax error CS1001 is reported.
var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("error CS1001", output, StringComparison.Ordinal);
// Verify that compiler syntax error CS1001 cannot be suppressed with diagnostic suppressor.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: new DiagnosticSuppressorForId("CS1001"));
Assert.Contains("error CS1001", output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestNoSuppression_CompilerSemanticError()
{
// error CS0246: The type or namespace name 'UndefinedType' could not be found (are you missing a using directive or an assembly reference?)
string source = @"
class C
{
void M(UndefinedType x) { }
}";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that compiler error CS0246 is reported.
var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
Assert.Contains("error CS0246", output, StringComparison.Ordinal);
// Verify that compiler error CS0246 cannot be suppressed with diagnostic suppressor.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: new DiagnosticSuppressorForId("CS0246"));
Assert.Contains("error CS0246", output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestSuppression_AnalyzerWarning()
{
string source = @"
class C { }";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that analyzer warning is reported.
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1,
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzer);
Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
// Verify that analyzer warning is suppressed with diagnostic suppressor
// and info diagnostic is logged with programmatic suppression information.
var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var suppressionMessage = string.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
analyzer.Descriptor.MessageFormat,
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification);
var analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0,
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzerAndSuppressor);
Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
// Verify that analyzer warning is reported as error for /warnaserror.
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
additionalFlags: new[] { "/warnAsError" },
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzer);
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
// Verify that analyzer warning is suppressed with diagnostic suppressor even with /warnaserror
// and info diagnostic is logged with programmatic suppression information.
output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0,
additionalFlags: new[] { "/warnAsError" },
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzerAndSuppressor);
Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
Assert.Contains("info SP0001", output, StringComparison.Ordinal);
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal);
// Verify that "NotConfigurable" analyzer warning cannot be suppressed with diagnostic suppressor.
analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: false);
suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1,
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzerAndSuppressor);
Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[Fact]
public void TestNoSuppression_AnalyzerError()
{
string source = @"
class C { }";
var srcDirectory = Temp.CreateDirectory();
var srcFile = srcDirectory.CreateFile("a.cs");
srcFile.WriteAllText(source);
// Verify that analyzer error is reported.
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Error, configurable: true);
var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzer);
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
// Verify that analyzer error cannot be suppressed with diagnostic suppressor.
var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
var analyzerAndSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1,
includeCurrentAssemblyAsAnalyzerReference: false,
analyzers: analyzerAndSuppressor);
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal);
CleanupAllGeneratedFiles(srcFile.Path);
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
......
......@@ -2766,7 +2766,7 @@ public void TestSymbolStartAnalyzer_MultipleAnalyzers_NamedTypeAndMethods()
}
[Fact]
private void TestSymbolStartAnalyzer_MultipleAnalyzers_AllSymbolKinds()
public void TestSymbolStartAnalyzer_MultipleAnalyzers_AllSymbolKinds()
{
testCore("SymbolStartTopLevelRuleId", topLevel: true);
testCore("SymbolStartRuleId", topLevel: false);
......
......@@ -10,7 +10,6 @@
namespace Microsoft.CodeAnalysis {
using System;
using System.Reflection;
/// <summary>
......@@ -20,7 +19,7 @@ namespace Microsoft.CodeAnalysis {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class CodeAnalysisResources {
......@@ -40,7 +39,7 @@ internal class CodeAnalysisResources {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CodeAnalysis.CodeAnalysisResources", typeof(CodeAnalysisResources).GetTypeInfo().Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CodeAnalysis.CodeAnalysisResources", typeof(CodeAnalysisResources).Assembly);
resourceMan = temp;
}
return resourceMan;
......@@ -784,6 +783,15 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Suppressed diagnostic ID &apos;{0}&apos; does not match suppressable ID &apos;{1}&apos; for the given suppression descriptor..
/// </summary>
internal static string InvalidDiagnosticSuppressionReported {
get {
return ResourceManager.GetString("InvalidDiagnosticSuppressionReported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid hash..
/// </summary>
......@@ -1063,6 +1071,15 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Non-reported diagnostic with ID &apos;{0}&apos; cannot be suppressed..
/// </summary>
internal static string NonReportedDiagnosticCannotBeSuppressed {
get {
return ResourceManager.GetString("NonReportedDiagnosticCannotBeSuppressed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Given operation has a non-null parent..
/// </summary>
......@@ -1414,6 +1431,42 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Analyzer &apos;{0}&apos; contains a null descriptor in its &apos;SupportedSuppressions&apos;..
/// </summary>
internal static string SupportedSuppressionsHasNullDescriptor {
get {
return ResourceManager.GetString("SupportedSuppressionsHasNullDescriptor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Diagnostic &apos;{0}: {1}&apos; was programmatically suppressed by a DiagnosticSuppressor with suppresion ID &apos;{2}&apos; and justification &apos;{3}&apos;.
/// </summary>
internal static string SuppressionDiagnosticDescriptorMessage {
get {
return ResourceManager.GetString("SuppressionDiagnosticDescriptorMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Programmatic suppression of an analyzer diagnostic.
/// </summary>
internal static string SuppressionDiagnosticDescriptorTitle {
get {
return ResourceManager.GetString("SuppressionDiagnosticDescriptorTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space..
/// </summary>
internal static string SuppressionIdCantBeNullOrWhitespace {
get {
return ResourceManager.GetString("SuppressionIdCantBeNullOrWhitespace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Windows PDB writer doesn&apos;t support SourceLink feature: &apos;{0}&apos;.
/// </summary>
......@@ -1594,6 +1647,15 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to Reported suppression with ID &apos;{0}&apos; is not supported by the suppressor..
/// </summary>
internal static string UnsupportedSuppressionReported {
get {
return ResourceManager.GetString("UnsupportedSuppressionReported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Value too large to be represented as a 30 bit unsigned integer..
/// </summary>
......
......@@ -419,6 +419,9 @@
<data name="DiagnosticIdCantBeNullOrWhitespace" xml:space="preserve">
<value>A DiagnosticDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</value>
</data>
<data name="SuppressionIdCantBeNullOrWhitespace" xml:space="preserve">
<value>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</value>
</data>
<data name="RuleSetHasDuplicateRules" xml:space="preserve">
<value>The rule set file has duplicate rules for '{0}' with differing actions '{1}' and '{2}'.</value>
</data>
......@@ -443,6 +446,15 @@
<data name="UnsupportedDiagnosticReported" xml:space="preserve">
<value>Reported diagnostic with ID '{0}' is not supported by the analyzer.</value>
</data>
<data name="UnsupportedSuppressionReported" xml:space="preserve">
<value>Reported suppression with ID '{0}' is not supported by the suppressor.</value>
</data>
<data name="InvalidDiagnosticSuppressionReported" xml:space="preserve">
<value>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</value>
</data>
<data name="NonReportedDiagnosticCannotBeSuppressed" xml:space="preserve">
<value>Non-reported diagnostic with ID '{0}' cannot be suppressed.</value>
</data>
<data name="InvalidDiagnosticIdReported" xml:space="preserve">
<value>Reported diagnostic has an ID '{0}', which is not a valid identifier.</value>
</data>
......@@ -452,6 +464,9 @@
<data name="SupportedDiagnosticsHasNullDescriptor" xml:space="preserve">
<value>Analyzer '{0}' contains a null descriptor in its 'SupportedDiagnostics'.</value>
</data>
<data name="SupportedSuppressionsHasNullDescriptor" xml:space="preserve">
<value>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</value>
</data>
<data name="The_type_0_is_not_understood_by_the_serialization_binder" xml:space="preserve">
<value>The type '{0}' is not understood by the serialization binder.</value>
</data>
......@@ -645,4 +660,10 @@
<data name="WRN_InvalidSeverityInAnalyzerConfig_Title" xml:space="preserve">
<value>Invalid severity in analyzer config file.</value>
</data>
<data name="SuppressionDiagnosticDescriptorTitle" xml:space="preserve">
<value>Programmatic suppression of an analyzer diagnostic</value>
</data>
<data name="SuppressionDiagnosticDescriptorMessage" xml:space="preserve">
<value>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
internal abstract partial class CommonCompiler
{
/// <summary>
/// Special informational diagnostic for each programmatic <see cref="Diagnostics.Suppression"/> reported by a <see cref="Diagnostics.DiagnosticSuppressor"/>.
/// </summary>
private sealed class SuppressionDiagnostic : Diagnostic
{
private static readonly DiagnosticDescriptor s_suppressionDiagnosticDescriptor = new DiagnosticDescriptor(
"SP0001",
CodeAnalysisResources.SuppressionDiagnosticDescriptorTitle,
CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
"ProgrammaticSuppression",
DiagnosticSeverity.Info,
isEnabledByDefault: true);
private readonly Diagnostic _originalDiagnostic;
private readonly string _suppressionId;
private readonly LocalizableString _suppressionJustification;
public SuppressionDiagnostic(
Diagnostic originalDiagnostic,
string suppressionId,
LocalizableString suppressionJustification)
{
Debug.Assert(originalDiagnostic != null);
Debug.Assert(originalDiagnostic.ProgrammaticSuppressionInfo != null);
Debug.Assert(!string.IsNullOrEmpty(suppressionId));
Debug.Assert(suppressionJustification != null);
_originalDiagnostic = originalDiagnostic;
_suppressionId = suppressionId;
_suppressionJustification = suppressionJustification;
}
public override DiagnosticDescriptor Descriptor => s_suppressionDiagnosticDescriptor;
public override string Id => Descriptor.Id;
public override string GetMessage(IFormatProvider formatProvider = null)
{
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var localizableMessageFormat = s_suppressionDiagnosticDescriptor.MessageFormat.ToString(formatProvider);
return string.Format(formatProvider,
localizableMessageFormat,
_originalDiagnostic.Id,
_originalDiagnostic.GetMessage(formatProvider),
_suppressionId,
_suppressionJustification.ToString(formatProvider));
}
public override DiagnosticSeverity Severity => DiagnosticSeverity.Info;
public override bool IsSuppressed => false;
public override int WarningLevel => GetDefaultWarningLevel(DiagnosticSeverity.Info);
public override Location Location => _originalDiagnostic.Location;
public override IReadOnlyList<Location> AdditionalLocations => _originalDiagnostic.AdditionalLocations;
public override ImmutableDictionary<string, string> Properties => ImmutableDictionary<string, string>.Empty;
public override bool Equals(Diagnostic obj)
{
var other = obj as SuppressionDiagnostic;
if (other == null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(_originalDiagnostic, other._originalDiagnostic) &&
Equals(_suppressionId, other._suppressionId) &&
Equals(_suppressionJustification, other._suppressionJustification);
}
public override bool Equals(object obj)
{
return this.Equals(obj as Diagnostic);
}
public override int GetHashCode()
{
return Hash.Combine(_originalDiagnostic.GetHashCode(),
Hash.Combine(_suppressionId.GetHashCode(), _suppressionJustification.GetHashCode()));
}
internal override Diagnostic WithLocation(Location location)
{
throw new NotSupportedException();
}
internal override Diagnostic WithSeverity(DiagnosticSeverity severity)
{
throw new NotSupportedException();
}
internal override Diagnostic WithIsSuppressed(bool isSuppressed)
{
throw new NotSupportedException();
}
}
}
}
......@@ -485,6 +485,14 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter
{
bool hasErrors = false;
foreach (var diag in diagnostics)
{
reportDiagnostic(diag);
}
return hasErrors;
// Local functions
void reportDiagnostic(Diagnostic diag)
{
if (_reportedDiagnostics.Contains(diag))
{
......@@ -496,20 +504,38 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter
// we previously created.
//this assert isn't valid if we change the design to not bail out after each phase.
//System.Diagnostics.Debug.Assert(diag.Severity != DiagnosticSeverity.Error);
continue;
return;
}
else if (diag.Severity == DiagnosticSeverity.Hidden)
{
// Not reported from the command-line compiler.
continue;
return;
}
// We want to report diagnostics with source suppression in the error log file.
// However, these diagnostics should not be reported on the console output.
errorLoggerOpt?.LogDiagnostic(diag);
// If the diagnostic was suppressed by one or more DiagnosticSuppressor(s), then we report info diagnostics for each suppression
// so that the suppression information is available in the binary logs and verbose build logs.
if (diag.ProgrammaticSuppressionInfo != null)
{
foreach (var (id, justification) in diag.ProgrammaticSuppressionInfo.Suppressions)
{
var suppressionDiag = new SuppressionDiagnostic(diag, id, justification);
if (_reportedDiagnostics.Add(suppressionDiag))
{
PrintError(suppressionDiag, consoleOutput);
}
}
_reportedDiagnostics.Add(diag);
return;
}
if (diag.IsSuppressed)
{
continue;
return;
}
// Diagnostics that aren't suppressed will be reported to the console output and, if they are errors,
......@@ -523,8 +549,6 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter
_reportedDiagnostics.Add(diag);
}
return hasErrors;
}
/// <summary>Returns true if there were any errors, false otherwise.</summary>
......@@ -1022,9 +1046,16 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancellat
// TryComplete, we may miss diagnostics.
var hostDiagnostics = analyzerDriver.GetDiagnosticsAsync(compilation).Result;
diagnostics.AddRange(hostDiagnostics);
if (hostDiagnostics.Any(IsReportedError))
if (!diagnostics.IsEmptyWithoutResolution)
{
success = false;
// Apply diagnostic suppressions for analyzer and/or compiler diagnostics from diagnostic suppressors.
analyzerDriver.ApplyProgrammaticSuppressions(diagnostics, compilation);
if (HasUnsuppressedErrors(diagnostics))
{
success = false;
}
}
}
}
......
......@@ -1379,7 +1379,7 @@ internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable<
continue;
}
else if (filtered.Severity == DiagnosticSeverity.Error &&
!filtered.IsSuppressed)
!filtered.IsSuppressed)
{
hasError = true;
}
......
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
public abstract partial class Diagnostic
{
private sealed class DiagnosticWithProgrammaticSuppression : Diagnostic
{
private readonly Diagnostic _originalUnsuppressedDiagnostic;
private readonly ProgrammaticSuppressionInfo _programmaticSuppressionInfo;
public DiagnosticWithProgrammaticSuppression(
Diagnostic originalUnsuppressedDiagnostic,
ProgrammaticSuppressionInfo programmaticSuppressionInfo)
{
Debug.Assert(!originalUnsuppressedDiagnostic.IsSuppressed);
Debug.Assert(originalUnsuppressedDiagnostic.ProgrammaticSuppressionInfo == null);
Debug.Assert(programmaticSuppressionInfo != null);
_originalUnsuppressedDiagnostic = originalUnsuppressedDiagnostic;
_programmaticSuppressionInfo = programmaticSuppressionInfo;
}
public override DiagnosticDescriptor Descriptor
{
get { return _originalUnsuppressedDiagnostic.Descriptor; }
}
public override string Id
{
get { return Descriptor.Id; }
}
public override string GetMessage(IFormatProvider formatProvider = null)
=> _originalUnsuppressedDiagnostic.GetMessage(formatProvider);
internal override IReadOnlyList<object> Arguments
{
get { return _originalUnsuppressedDiagnostic.Arguments; }
}
public override DiagnosticSeverity Severity
{
get { return _originalUnsuppressedDiagnostic.Severity; }
}
public override bool IsSuppressed
{
get { return true; }
}
internal override ProgrammaticSuppressionInfo ProgrammaticSuppressionInfo
{
get { return _programmaticSuppressionInfo; }
}
public override int WarningLevel
{
get { return _originalUnsuppressedDiagnostic.WarningLevel; }
}
public override Location Location
{
get { return _originalUnsuppressedDiagnostic.Location; }
}
public override IReadOnlyList<Location> AdditionalLocations
{
get { return _originalUnsuppressedDiagnostic.AdditionalLocations; }
}
public override ImmutableDictionary<string, string> Properties
{
get { return _originalUnsuppressedDiagnostic.Properties; }
}
public override bool Equals(Diagnostic obj)
{
var other = obj as DiagnosticWithProgrammaticSuppression;
if (other == null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(_originalUnsuppressedDiagnostic, other._originalUnsuppressedDiagnostic) &&
Equals(_programmaticSuppressionInfo, other._programmaticSuppressionInfo);
}
public override bool Equals(object obj)
{
return this.Equals(obj as Diagnostic);
}
public override int GetHashCode()
{
return Hash.Combine(_originalUnsuppressedDiagnostic.GetHashCode(), _programmaticSuppressionInfo.GetHashCode());
}
internal override Diagnostic WithLocation(Location location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
if (this.Location != location)
{
return new DiagnosticWithProgrammaticSuppression(_originalUnsuppressedDiagnostic.WithLocation(location), _programmaticSuppressionInfo);
}
return this;
}
internal override Diagnostic WithSeverity(DiagnosticSeverity severity)
{
if (this.Severity != severity)
{
return new DiagnosticWithProgrammaticSuppression(_originalUnsuppressedDiagnostic.WithSeverity(severity), _programmaticSuppressionInfo);
}
return this;
}
internal override Diagnostic WithIsSuppressed(bool isSuppressed)
{
// We do not support toggling suppressed diagnostic to unsuppressed.
if (!isSuppressed)
{
throw new ArgumentException(nameof(isSuppressed));
}
return this;
}
}
}
}
......@@ -445,6 +445,19 @@ private string GetDebuggerDisplay()
/// </summary>
internal abstract Diagnostic WithIsSuppressed(bool isSuppressed);
/// <summary>
/// Create a new instance of this diagnostic with the given programmatic suppression info.
/// </summary>
internal Diagnostic WithProgrammaticSuppression(ProgrammaticSuppressionInfo programmaticSuppressionInfo)
{
Debug.Assert(this.ProgrammaticSuppressionInfo == null);
Debug.Assert(programmaticSuppressionInfo != null);
return new DiagnosticWithProgrammaticSuppression(this, programmaticSuppressionInfo);
}
internal virtual ProgrammaticSuppressionInfo ProgrammaticSuppressionInfo { get { return null; } }
// compatibility
internal virtual int Code { get { return 0; } }
......
......@@ -215,7 +215,8 @@ public ReportDiagnostic GetEffectiveSeverity(CompilationOptions compilationOptio
return effectiveDiagnostic != null ? MapSeverityToReport(effectiveDiagnostic.Severity) : ReportDiagnostic.Suppress;
}
private static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severity)
// internal for testing purposes.
internal static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severity)
{
switch (severity)
{
......
// 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.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Contains information about the source of a programmatic diagnostic suppression produced by an <see cref="DiagnosticSuppressor"/>.
/// </summary>
internal sealed class ProgrammaticSuppressionInfo : IEquatable<ProgrammaticSuppressionInfo>
{
public ImmutableHashSet<(string Id, LocalizableString Justification)> Suppressions { get; }
internal ProgrammaticSuppressionInfo(ImmutableHashSet<(string Id, LocalizableString Justification)> suppressions)
{
Suppressions = suppressions;
}
public bool Equals(ProgrammaticSuppressionInfo other)
{
if (ReferenceEquals(this, other))
{
return true;
}
return other != null &&
this.Suppressions.SetEquals(other.Suppressions);
}
public override bool Equals(object obj)
{
return Equals(obj as ProgrammaticSuppressionInfo);
}
public override int GetHashCode()
{
return Suppressions.Count;
}
}
}
// 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 Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Provides a description about a programmatic suppression of a <see cref="Diagnostic"/> by a <see cref="DiagnosticSuppressor"/>.
/// </summary>
public sealed class SuppressionDescriptor : IEquatable<SuppressionDescriptor>
{
/// <summary>
/// An unique identifier for the suppression.
/// </summary>
public string Id { get; }
/// <summary>
/// Identifier of the suppressed diagnostic, i.e. <see cref="Diagnostic.Id"/>.
/// </summary>
public string SuppressedDiagnosticId { get; }
/// <summary>
/// A localizable justification about the suppression.
/// </summary>
public LocalizableString Justification { get; }
/// <summary>
/// Create a SuppressionDescriptor, which provides a justification about a programmatic suppression of a <see cref="Diagnostic"/>.
/// NOTE: For localizable <paramref name="justification"/>,
/// use constructor overload <see cref="SuppressionDescriptor(string, string, LocalizableString)"/>.
/// </summary>
/// <param name="id">A unique identifier for the suppression. For example, suppression ID "SP1001".</param>
/// <param name="suppressedDiagnosticId">Identifier of the suppressed diagnostic, i.e. <see cref="Diagnostic.Id"/>. For example, compiler warning Id "CS0649".</param>
/// <param name="justification">Justification for the suppression. For example: "Suppress CS0649 on fields marked with YYY attribute as they are implicitly assigned.".</param>
public SuppressionDescriptor(
string id,
string suppressedDiagnosticId,
string justification)
: this(id, suppressedDiagnosticId, (LocalizableString)justification)
{
}
/// <summary>
/// Create a SuppressionDescriptor, which provides a localizable justification about a programmatic suppression of a <see cref="Diagnostic"/>.
/// </summary>
/// <param name="id">A unique identifier for the suppression. For example, suppression ID "SP1001".</param>
/// <param name="suppressedDiagnosticId">Identifier of the suppressed diagnostic, i.e. <see cref="Diagnostic.Id"/>. For example, compiler warning Id "CS0649".</param>
/// <param name="justification">Justification for the suppression. For example: "Suppress CS0649 on fields marked with YYY attribute as they are implicitly assigned.".</param>
public SuppressionDescriptor(
string id,
string suppressedDiagnosticId,
LocalizableString justification)
{
if (string.IsNullOrWhiteSpace(id))
{
throw new ArgumentException(CodeAnalysisResources.SuppressionIdCantBeNullOrWhitespace, nameof(id));
}
if (string.IsNullOrWhiteSpace(suppressedDiagnosticId))
{
throw new ArgumentException(CodeAnalysisResources.DiagnosticIdCantBeNullOrWhitespace, nameof(suppressedDiagnosticId));
}
this.Id = id;
this.SuppressedDiagnosticId = suppressedDiagnosticId;
this.Justification = justification ?? throw new ArgumentNullException(nameof(justification));
}
public bool Equals(SuppressionDescriptor other)
{
if (ReferenceEquals(this, other))
{
return true;
}
return
other != null &&
this.Id == other.Id &&
this.SuppressedDiagnosticId == other.SuppressedDiagnosticId &&
this.Justification.Equals(other.Justification);
}
public override bool Equals(object obj)
{
return Equals(obj as SuppressionDescriptor);
}
public override int GetHashCode()
{
return Hash.Combine(this.Id.GetHashCode(),
Hash.Combine(this.SuppressedDiagnosticId.GetHashCode(), this.Justification.GetHashCode()));
}
/// <summary>
/// Returns a flag indicating if the suppression is disabled for the given <see cref="CompilationOptions"/>.
/// </summary>
/// <param name="compilationOptions">Compilation options</param>
internal bool IsDisabled(CompilationOptions compilationOptions)
{
if (compilationOptions == null)
{
throw new ArgumentNullException(nameof(compilationOptions));
}
return compilationOptions.SpecificDiagnosticOptions.TryGetValue(Id, out var reportDiagnostic) &&
reportDiagnostic == ReportDiagnostic.Suppress;
}
}
}
......@@ -73,16 +73,16 @@ internal ImmutableArray<DiagnosticAnalyzer> GetPendingAnalyzers(ImmutableArray<D
}
}
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope)
internal void ApplySuppressionsAndStoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope)
{
Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis.");
foreach (var analyzer in analysisScope.Analyzers)
{
// Dequeue reported analyzer diagnostics from the driver and store them in our maps.
var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation);
var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation);
var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation);
var syntaxDiagnostics = driver.DequeueLocalDiagnosticsAndApplySuppressions(analyzer, syntax: true, compilation: compilation);
var semanticDiagnostics = driver.DequeueLocalDiagnosticsAndApplySuppressions(analyzer, syntax: false, compilation: compilation);
var compilationDiagnostics = driver.DequeueNonLocalDiagnosticsAndApplySuppressions(analyzer, compilation);
lock (_gate)
{
......@@ -348,8 +348,9 @@ internal AnalysisResult ToAnalysisResult(ImmutableArray<DiagnosticAnalyzer> anal
foreach (var analyzer in analyzers)
{
var actionCounts = _analyzerActionCounts[analyzer];
var suppressionActionCounts = analyzer is DiagnosticSuppressor ? 1 : 0;
var executionTime = _analyzerExecutionTimeOpt != null ? _analyzerExecutionTimeOpt[analyzer] : default(TimeSpan);
var telemetryInfo = new AnalyzerTelemetryInfo(actionCounts, executionTime);
var telemetryInfo = new AnalyzerTelemetryInfo(actionCounts, suppressionActionCounts, executionTime);
builder.Add(analyzer, telemetryInfo);
}
}
......
......@@ -30,10 +30,21 @@ internal abstract partial class AnalyzerDriver : IDisposable
private readonly Func<SyntaxTree, CancellationToken, bool> _isGeneratedCode;
/// <summary>
/// Set of diagnostic suppressions that are suppressed via analyzer suppression actions.
/// </summary>
private readonly ConcurrentSet<Suppression> _programmaticSuppressions;
/// <summary>
/// Flag indicating if the <see cref="Analyzers"/> include any <see cref="DiagnosticSuppressor"/>
/// which can suppress reported analyzer/compiler diagnostics.
/// </summary>
private readonly bool _hasDiagnosticSuppressors;
// Lazy fields/properties
private CancellationTokenRegistration _queueRegistration;
protected ImmutableArray<DiagnosticAnalyzer> Analyzers { get; private set; }
protected AnalyzerManager AnalyzerManager { get; private set; }
protected ImmutableArray<DiagnosticAnalyzer> Analyzers { get; }
protected AnalyzerManager AnalyzerManager { get; }
protected AnalyzerExecutor AnalyzerExecutor { get; private set; }
protected CompilationData CurrentCompilationData { get; private set; }
protected AnalyzerActions AnalyzerActions { get; private set; }
......@@ -155,6 +166,8 @@ protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerM
this.Analyzers = analyzers;
this.AnalyzerManager = analyzerManager;
_isGeneratedCode = (tree, ct) => GeneratedCodeUtilities.IsGeneratedCode(tree, isComment, ct);
_hasDiagnosticSuppressors = this.Analyzers.Any(a => a is DiagnosticSuppressor);
_programmaticSuppressions = _hasDiagnosticSuppressors ? new ConcurrentSet<Suppression>() : null;
}
/// <summary>
......@@ -272,7 +285,8 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn
var analyzerExecutor = AnalyzerExecutor.Create(
compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnosticOpt, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter,
IsCompilerAnalyzer, AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, GetAnalyzerGate,
analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken);
getSemanticModel: tree => CurrentCompilationData.GetOrCreateCachedSemanticModel(tree, compilation, cancellationToken),
analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, s => _programmaticSuppressions.Add(s), cancellationToken);
Initialize(analyzerExecutor, diagnosticQueue, compilationData, cancellationToken);
}
......@@ -588,19 +602,156 @@ public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync(Compilation co
return allDiagnostics.ToReadOnlyAndFree();
}
public ImmutableArray<Diagnostic> DequeueLocalDiagnostics(DiagnosticAnalyzer analyzer, bool syntax, Compilation compilation)
public void ApplyProgrammaticSuppressions(DiagnosticBag reportedDiagnostics, Compilation compilation)
{
Debug.Assert(!reportedDiagnostics.IsEmptyWithoutResolution);
if (!_hasDiagnosticSuppressors)
{
return;
}
var newDiagnostics = ApplyProgrammaticSuppressionsCore(reportedDiagnostics.ToReadOnly(), compilation);
reportedDiagnostics.Clear();
reportedDiagnostics.AddRange(newDiagnostics);
}
public ImmutableArray<Diagnostic> ApplyProgrammaticSuppressions(ImmutableArray<Diagnostic> reportedDiagnostics, Compilation compilation)
{
if (reportedDiagnostics.IsEmpty ||
!_hasDiagnosticSuppressors)
{
return reportedDiagnostics;
}
return ApplyProgrammaticSuppressionsCore(reportedDiagnostics, compilation);
}
private ImmutableArray<Diagnostic> ApplyProgrammaticSuppressionsCore(ImmutableArray<Diagnostic> reportedDiagnostics, Compilation compilation)
{
Debug.Assert(_hasDiagnosticSuppressors);
Debug.Assert(!reportedDiagnostics.IsEmpty);
Debug.Assert(_programmaticSuppressions != null);
// We do not allow analyzer based suppressions for following category of diagnostics:
// 1. Diagnostics which are already suppressed in source via pragma/suppress message attribute.
// 2. Diagnostics explicitly tagged as not configurable by analyzer authors - this includes compiler error diagnostics.
// 3. Diagnostics which are marked as error by default by diagnostic authors.
var suppressableDiagnostics = reportedDiagnostics.Where(d => !d.IsSuppressed &&
!d.IsNotConfigurable() &&
d.DefaultSeverity != DiagnosticSeverity.Error);
if (suppressableDiagnostics.IsEmpty())
{
return reportedDiagnostics;
}
executeSuppressionActions(suppressableDiagnostics, concurrent: compilation.Options.ConcurrentBuild);
if (_programmaticSuppressions.IsEmpty)
{
return reportedDiagnostics;
}
var builder = ArrayBuilder<Diagnostic>.GetInstance(reportedDiagnostics.Length);
ImmutableDictionary<Diagnostic, ProgrammaticSuppressionInfo> programmaticSuppressionsByDiagnostic = createProgrammaticSuppressionsByDiagnosticMap(_programmaticSuppressions);
foreach (var diagnostic in reportedDiagnostics)
{
if (programmaticSuppressionsByDiagnostic.TryGetValue(diagnostic, out var programmaticSuppressionInfo))
{
Debug.Assert(suppressableDiagnostics.Contains(diagnostic));
Debug.Assert(!diagnostic.IsSuppressed);
builder.Add(diagnostic.WithProgrammaticSuppression(programmaticSuppressionInfo));
}
else
{
builder.Add(diagnostic);
}
}
return builder.ToImmutableAndFree();
void executeSuppressionActions(IEnumerable<Diagnostic> reportedDiagnostics, bool concurrent)
{
var suppressors = this.Analyzers.OfType<DiagnosticSuppressor>();
if (concurrent)
{
Parallel.ForEach(suppressors, suppressor =>
{
AnalyzerExecutor.ExecuteSuppressionAction(suppressor, getSuppressableDiagnostics(suppressor));
});
}
else
{
foreach (var suppressor in suppressors)
{
AnalyzerExecutor.ExecuteSuppressionAction(suppressor, getSuppressableDiagnostics(suppressor));
}
}
return;
ImmutableArray<Diagnostic> getSuppressableDiagnostics(DiagnosticSuppressor suppressor)
{
var supportedSuppressions = AnalyzerManager.GetSupportedSuppressionDescriptors(suppressor, AnalyzerExecutor);
if (supportedSuppressions.IsEmpty)
{
return ImmutableArray<Diagnostic>.Empty;
}
var builder = ArrayBuilder<Diagnostic>.GetInstance();
foreach (var diagnostic in reportedDiagnostics)
{
if (supportedSuppressions.Contains(s => s.SuppressedDiagnosticId == diagnostic.Id))
{
builder.Add(diagnostic);
}
}
return builder.ToImmutableAndFree();
}
}
static ImmutableDictionary<Diagnostic, ProgrammaticSuppressionInfo> createProgrammaticSuppressionsByDiagnosticMap(ConcurrentSet<Suppression> programmaticSuppressions)
{
var programmaticSuppressionsBuilder = PooledDictionary<Diagnostic, ImmutableHashSet<(string, LocalizableString)>.Builder>.GetInstance();
foreach (var programmaticSuppression in programmaticSuppressions)
{
if (!programmaticSuppressionsBuilder.TryGetValue(programmaticSuppression.SuppressedDiagnostic, out var set))
{
set = ImmutableHashSet.CreateBuilder<(string, LocalizableString)>();
programmaticSuppressionsBuilder.Add(programmaticSuppression.SuppressedDiagnostic, set);
}
set.Add((programmaticSuppression.Descriptor.Id, programmaticSuppression.Descriptor.Justification));
}
var mapBuilder = ImmutableDictionary.CreateBuilder<Diagnostic, ProgrammaticSuppressionInfo>();
foreach (var (diagnostic, set) in programmaticSuppressionsBuilder)
{
mapBuilder.Add(diagnostic, new ProgrammaticSuppressionInfo(set.ToImmutable()));
}
return mapBuilder.ToImmutable();
}
}
public ImmutableArray<Diagnostic> DequeueLocalDiagnosticsAndApplySuppressions(DiagnosticAnalyzer analyzer, bool syntax, Compilation compilation)
{
var diagnostics = syntax ? DiagnosticQueue.DequeueLocalSyntaxDiagnostics(analyzer) : DiagnosticQueue.DequeueLocalSemanticDiagnostics(analyzer);
return FilterDiagnosticsSuppressedInSource(diagnostics, compilation, CurrentCompilationData.SuppressMessageAttributeState);
return FilterDiagnosticsSuppressedInSourceOrByAnalyzers(diagnostics, compilation);
}
public ImmutableArray<Diagnostic> DequeueNonLocalDiagnostics(DiagnosticAnalyzer analyzer, Compilation compilation)
public ImmutableArray<Diagnostic> DequeueNonLocalDiagnosticsAndApplySuppressions(DiagnosticAnalyzer analyzer, Compilation compilation)
{
var diagnostics = DiagnosticQueue.DequeueNonLocalDiagnostics(analyzer);
return FilterDiagnosticsSuppressedInSource(diagnostics, compilation, CurrentCompilationData.SuppressMessageAttributeState);
return FilterDiagnosticsSuppressedInSourceOrByAnalyzers(diagnostics, compilation);
}
private ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSourceOrByAnalyzers(ImmutableArray<Diagnostic> diagnostics, Compilation compilation)
{
diagnostics = FilterDiagnosticsSuppressedInSource(diagnostics, compilation, CurrentCompilationData.SuppressMessageAttributeState);
return ApplyProgrammaticSuppressions(diagnostics, compilation);
}
private ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSource(ImmutableArray<Diagnostic> diagnostics, Compilation compilation, SuppressMessageAttributeState suppressMessageState)
private static ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSource(ImmutableArray<Diagnostic> diagnostics, Compilation compilation, SuppressMessageAttributeState suppressMessageState)
{
if (diagnostics.IsEmpty)
{
......
......@@ -36,11 +36,13 @@ internal class AnalyzerExecutor
private readonly Action<Diagnostic> _addNonCategorizedDiagnosticOpt;
private readonly Action<Diagnostic, DiagnosticAnalyzer, bool> _addCategorizedLocalDiagnosticOpt;
private readonly Action<Diagnostic, DiagnosticAnalyzer> _addCategorizedNonLocalDiagnosticOpt;
private readonly Action<Suppression> _addSuppressionOpt;
private readonly Action<Exception, DiagnosticAnalyzer, Diagnostic> _onAnalyzerException;
private readonly Func<Exception, bool> _analyzerExceptionFilter;
private readonly AnalyzerManager _analyzerManager;
private readonly Func<DiagnosticAnalyzer, bool> _isCompilerAnalyzer;
private readonly Func<DiagnosticAnalyzer, object> _getAnalyzerGateOpt;
private readonly Func<SyntaxTree, SemanticModel> _getSemanticModelOpt;
private readonly Func<DiagnosticAnalyzer, bool> _shouldSkipAnalysisOnGeneratedCode;
private readonly Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> _shouldSuppressGeneratedCodeDiagnostic;
private readonly Func<SyntaxTree, TextSpan, bool> _isGeneratedCodeLocation;
......@@ -75,12 +77,14 @@ internal class AnalyzerExecutor
/// It should return a unique gate object for the given analyzer instance for non-concurrent analyzers, and null otherwise.
/// All analyzer callbacks for non-concurrent analyzers will be guarded with a lock on the gate.
/// </param>
/// <param name="getSemanticModel">Delegate to get a semantic model for the given syntax tree which can be shared across analyzers.</param>
/// <param name="shouldSkipAnalysisOnGeneratedCode">Delegate to identify if analysis should be skipped on generated code.</param>
/// <param name="shouldSuppressGeneratedCodeDiagnostic">Delegate to identify if diagnostic reported while analyzing generated code should be suppressed.</param>
/// <param name="isGeneratedCodeLocation">Delegate to identify if the given location is in generated code.</param>
/// <param name="logExecutionTime">Flag indicating whether we need to log analyzer execution time.</param>
/// <param name="addCategorizedLocalDiagnosticOpt">Optional delegate to add categorized local analyzer diagnostics.</param>
/// <param name="addCategorizedNonLocalDiagnosticOpt">Optional delegate to add categorized non-local analyzer diagnostics.</param>
/// <param name="addSuppressionOpt">Optional thread-safe delegate to add diagnostic suppressions from suppressors.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static AnalyzerExecutor Create(
Compilation compilation,
......@@ -94,9 +98,11 @@ internal class AnalyzerExecutor
Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic,
Func<SyntaxTree, TextSpan, bool> isGeneratedCodeLocation,
Func<DiagnosticAnalyzer, object> getAnalyzerGate,
Func<SyntaxTree, SemanticModel> getSemanticModel,
bool logExecutionTime = false,
Action<Diagnostic, DiagnosticAnalyzer, bool> addCategorizedLocalDiagnosticOpt = null,
Action<Diagnostic, DiagnosticAnalyzer> addCategorizedNonLocalDiagnosticOpt = null,
Action<Suppression> addSuppressionOpt = null,
CancellationToken cancellationToken = default(CancellationToken))
{
// We can either report categorized (local/non-local) diagnostics or non-categorized diagnostics.
......@@ -107,7 +113,8 @@ internal class AnalyzerExecutor
return new AnalyzerExecutor(compilation, analyzerOptions, addNonCategorizedDiagnosticOpt, onAnalyzerException, analyzerExceptionFilter,
isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation,
getAnalyzerGate, analyzerExecutionTimeMapOpt, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken);
getAnalyzerGate, getSemanticModel, analyzerExecutionTimeMapOpt, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt,
addSuppressionOpt, cancellationToken);
}
/// <summary>
......@@ -133,12 +140,14 @@ internal class AnalyzerExecutor
shouldSuppressGeneratedCodeDiagnostic: (diagnostic, analyzer, compilation, ct) => false,
isGeneratedCodeLocation: (_1, _2) => false,
getAnalyzerGateOpt: null,
getSemanticModelOpt: null,
onAnalyzerException: onAnalyzerException,
analyzerExceptionFilter: null,
analyzerManager: analyzerManager,
analyzerExecutionTimeMapOpt: null,
addCategorizedLocalDiagnosticOpt: null,
addCategorizedNonLocalDiagnosticOpt: null,
addSuppressionOpt: null,
cancellationToken: cancellationToken);
}
......@@ -154,9 +163,11 @@ internal class AnalyzerExecutor
Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic,
Func<SyntaxTree, TextSpan, bool> isGeneratedCodeLocation,
Func<DiagnosticAnalyzer, object> getAnalyzerGateOpt,
Func<SyntaxTree, SemanticModel> getSemanticModelOpt,
ConcurrentDictionary<DiagnosticAnalyzer, StrongBox<long>> analyzerExecutionTimeMapOpt,
Action<Diagnostic, DiagnosticAnalyzer, bool> addCategorizedLocalDiagnosticOpt,
Action<Diagnostic, DiagnosticAnalyzer> addCategorizedNonLocalDiagnosticOpt,
Action<Suppression> addSuppressionOpt,
CancellationToken cancellationToken)
{
_compilation = compilation;
......@@ -170,9 +181,11 @@ internal class AnalyzerExecutor
_shouldSuppressGeneratedCodeDiagnostic = shouldSuppressGeneratedCodeDiagnostic;
_isGeneratedCodeLocation = isGeneratedCodeLocation;
_getAnalyzerGateOpt = getAnalyzerGateOpt;
_getSemanticModelOpt = getSemanticModelOpt;
_analyzerExecutionTimeMapOpt = analyzerExecutionTimeMapOpt;
_addCategorizedLocalDiagnosticOpt = addCategorizedLocalDiagnosticOpt;
_addCategorizedNonLocalDiagnosticOpt = addCategorizedNonLocalDiagnosticOpt;
_addSuppressionOpt = addSuppressionOpt;
_cancellationToken = cancellationToken;
_compilationAnalysisValueProviderFactory = new CompilationAnalysisValueProviderFactory();
......@@ -187,7 +200,8 @@ public AnalyzerExecutor WithCancellationToken(CancellationToken cancellationToke
return new AnalyzerExecutor(_compilation, _analyzerOptions, _addNonCategorizedDiagnosticOpt, _onAnalyzerException, _analyzerExceptionFilter,
_isCompilerAnalyzer, _analyzerManager, _shouldSkipAnalysisOnGeneratedCode, _shouldSuppressGeneratedCodeDiagnostic, _isGeneratedCodeLocation,
_getAnalyzerGateOpt, _analyzerExecutionTimeMapOpt, _addCategorizedLocalDiagnosticOpt, _addCategorizedNonLocalDiagnosticOpt, cancellationToken);
_getAnalyzerGateOpt, _getSemanticModelOpt, _analyzerExecutionTimeMapOpt, _addCategorizedLocalDiagnosticOpt, _addCategorizedNonLocalDiagnosticOpt,
_addSuppressionOpt, cancellationToken);
}
internal Compilation Compilation => _compilation;
......@@ -266,6 +280,35 @@ public void ExecuteCompilationStartActions(ImmutableArray<CompilationStartAnalyz
}
}
/// <summary>
/// Executes the given diagnostic suppressor.
/// </summary>
/// <param name="suppressor">Suppressor to be executed.</param>
/// <param name="reportedDiagnostics">Reported analyzer/compiler diagnostics that can be suppressed.</param>
public void ExecuteSuppressionAction(DiagnosticSuppressor suppressor, ImmutableArray<Diagnostic> reportedDiagnostics)
{
Debug.Assert(_addSuppressionOpt != null);
if (reportedDiagnostics.IsEmpty)
{
return;
}
_cancellationToken.ThrowIfCancellationRequested();
var supportedSuppressions = _analyzerManager.GetSupportedSuppressionDescriptors(suppressor, this);
Func<SuppressionDescriptor, bool> isSupportedSuppression = supportedSuppressions.Contains;
Action<SuppressionAnalysisContext> action = suppressor.ReportSuppressions;
var context = new SuppressionAnalysisContext(_compilation, _analyzerOptions,
reportedDiagnostics, _addSuppressionOpt, isSupportedSuppression, _getSemanticModelOpt, _cancellationToken);
ExecuteAndCatchIfThrows(
suppressor,
data => data.action(data.context),
(action, context),
new AnalysisContextInfo(_compilation));
}
/// <summary>
/// Tries to executes compilation actions or compilation end actions.
/// </summary>
......
......@@ -48,9 +48,14 @@ public AnalyzerExecutionContext(DiagnosticAnalyzer analyzer)
private Dictionary<ISymbol, Task<HostSymbolStartAnalysisScope>> _lazySymbolScopeTasks;
/// <summary>
/// Supported descriptors for diagnostic analyzer.
/// Supported diagnostic descriptors for diagnostic analyzer, if any.
/// </summary>
private ImmutableArray<DiagnosticDescriptor> _lazyDescriptors = default(ImmutableArray<DiagnosticDescriptor>);
private ImmutableArray<DiagnosticDescriptor> _lazyDiagnosticDescriptors = default(ImmutableArray<DiagnosticDescriptor>);
/// <summary>
/// Supported suppression descriptors for diagnostic suppressor, if any.
/// </summary>
private ImmutableArray<SuppressionDescriptor> _lazySuppressionDescriptors = default(ImmutableArray<SuppressionDescriptor>);
public Task<HostSessionStartAnalysisScope> GetSessionAnalysisScopeTask(AnalyzerExecutor analyzerExecutor)
{
......@@ -217,23 +222,36 @@ public void ClearSymbolScopeTask(ISymbol symbol)
}
}
public ImmutableArray<DiagnosticDescriptor> GetOrComputeDescriptors(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor)
public ImmutableArray<DiagnosticDescriptor> GetOrComputeDiagnosticDescriptors(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor)
=> GetOrComputeDescriptors(ref _lazyDiagnosticDescriptors, ComputeDiagnosticDescriptors, _gate, analyzer, analyzerExecutor);
public ImmutableArray<SuppressionDescriptor> GetOrComputeSuppressionDescriptors(DiagnosticSuppressor suppressor, AnalyzerExecutor analyzerExecutor)
=> GetOrComputeDescriptors(ref _lazySuppressionDescriptors, ComputeSuppressionDescriptors, _gate, suppressor, analyzerExecutor);
private static ImmutableArray<TDescriptor> GetOrComputeDescriptors<TDescriptor>(
ref ImmutableArray<TDescriptor> lazyDescriptors,
Func<DiagnosticAnalyzer, AnalyzerExecutor, ImmutableArray<TDescriptor>> computeDescriptors,
object gate,
DiagnosticAnalyzer analyzer,
AnalyzerExecutor analyzerExecutor)
{
if (!_lazyDescriptors.IsDefault)
if (!lazyDescriptors.IsDefault)
{
return _lazyDescriptors;
return lazyDescriptors;
}
// Otherwise, compute the value.
var descriptors = ComputeDescriptors(analyzer, analyzerExecutor);
ImmutableInterlocked.InterlockedInitialize(ref _lazyDescriptors, descriptors);
return _lazyDescriptors;
// We do so outside the lock statement as we are calling into user code, which may be a long running operation.
var descriptors = computeDescriptors(analyzer, analyzerExecutor);
ImmutableInterlocked.InterlockedInitialize(ref lazyDescriptors, descriptors);
return lazyDescriptors;
}
/// <summary>
/// Compute <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> and exception handler for the given <paramref name="analyzer"/>.
/// </summary>
private static ImmutableArray<DiagnosticDescriptor> ComputeDescriptors(
private static ImmutableArray<DiagnosticDescriptor> ComputeDiagnosticDescriptors(
DiagnosticAnalyzer analyzer,
AnalyzerExecutor analyzerExecutor)
{
......@@ -282,6 +300,40 @@ public ImmutableArray<DiagnosticDescriptor> GetOrComputeDescriptors(DiagnosticAn
return supportedDiagnostics;
}
private static ImmutableArray<SuppressionDescriptor> ComputeSuppressionDescriptors(
DiagnosticAnalyzer analyzer,
AnalyzerExecutor analyzerExecutor)
{
var descriptors = ImmutableArray<SuppressionDescriptor>.Empty;
if (analyzer is DiagnosticSuppressor suppressor)
{
// Catch Exception from suppressor.SupportedSuppressions
analyzerExecutor.ExecuteAndCatchIfThrows(
analyzer,
_ =>
{
var descriptorsLocal = suppressor.SupportedSuppressions;
if (!descriptorsLocal.IsDefaultOrEmpty)
{
foreach (var descriptor in descriptorsLocal)
{
if (descriptor == null)
{
// Disallow null descriptors.
throw new ArgumentException(string.Format(CodeAnalysisResources.SupportedSuppressionsHasNullDescriptor, analyzer.ToString()), nameof(DiagnosticSuppressor.SupportedSuppressions));
}
}
descriptors = descriptorsLocal;
}
},
argument: default(object));
}
return descriptors;
}
public bool TryProcessCompletedMemberAndGetPendingSymbolEndActionsForContainer(
ISymbol containingSymbol,
ISymbol processedMemberSymbol,
......
......@@ -235,7 +235,18 @@ private static void ForceLocalizableStringExceptions(LocalizableString localizab
AnalyzerExecutor analyzerExecutor)
{
var analyzerExecutionContext = GetAnalyzerExecutionContext(analyzer);
return analyzerExecutionContext.GetOrComputeDescriptors(analyzer, analyzerExecutor);
return analyzerExecutionContext.GetOrComputeDiagnosticDescriptors(analyzer, analyzerExecutor);
}
/// <summary>
/// Return <see cref="DiagnosticSuppressor.SupportedSuppressions"/> of given <paramref name="suppressor"/>.
/// </summary>
public ImmutableArray<SuppressionDescriptor> GetSupportedSuppressionDescriptors(
DiagnosticSuppressor suppressor,
AnalyzerExecutor analyzerExecutor)
{
var analyzerExecutionContext = GetAnalyzerExecutionContext(suppressor);
return analyzerExecutionContext.GetOrComputeSuppressionDescriptors(suppressor, analyzerExecutor);
}
internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer, AnalyzerExecutor analyzerExecutor)
......@@ -309,6 +320,17 @@ internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diag
}
}
if (analyzer is DiagnosticSuppressor suppressor)
{
foreach (var suppressionDescriptor in GetSupportedSuppressionDescriptors(suppressor, analyzerExecutor))
{
if (!suppressionDescriptor.IsDisabled(options))
{
return false;
}
}
}
return true;
}
......
......@@ -89,6 +89,13 @@ public sealed class AnalyzerTelemetryInfo
/// </summary>
public int OperationBlockActionsCount { get; set; } = 0;
/// <summary>
/// Count of registered suppression actions.
/// This is the same as count of <see cref="DiagnosticSuppressor"/>s as each suppressor
/// has a single suppression action, i.e. <see cref="DiagnosticSuppressor.ReportSuppressions(SuppressionAnalysisContext)"/>.
/// </summary>
public int SuppressionActionsCount { get; set; } = 0;
/// <summary>
/// Total execution time.
/// </summary>
......@@ -99,7 +106,7 @@ public sealed class AnalyzerTelemetryInfo
/// </summary>
public bool Concurrent { get; set; }
internal AnalyzerTelemetryInfo(AnalyzerActionCounts actionCounts, TimeSpan executionTime)
internal AnalyzerTelemetryInfo(AnalyzerActionCounts actionCounts, int suppressionActionCounts, TimeSpan executionTime)
{
CompilationStartActionsCount = actionCounts.CompilationStartActionsCount;
CompilationEndActionsCount = actionCounts.CompilationEndActionsCount;
......@@ -121,6 +128,8 @@ internal AnalyzerTelemetryInfo(AnalyzerActionCounts actionCounts, TimeSpan execu
OperationBlockEndActionsCount = actionCounts.OperationBlockEndActionsCount;
OperationBlockActionsCount = actionCounts.OperationBlockActionsCount;
SuppressionActionsCount = suppressionActionCounts;
ExecutionTime = executionTime;
Concurrent = actionCounts.Concurrent;
}
......
......@@ -420,7 +420,7 @@ private async Task ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(Cancellat
}
Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts = analyzer => analyzerActionCounts[analyzer];
_analysisResultBuilder.StoreAnalysisResult(analysisScope, driver, compilation, getAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: true);
_analysisResultBuilder.ApplySuppressionsAndStoreAnalysisResult(analysisScope, driver, compilation, getAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: true);
}
finally
{
......@@ -450,7 +450,8 @@ private async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsWithoutStateTrac
// Force compilation diagnostics and wait for analyzer execution to complete.
var compDiags = compilation.GetDiagnostics(cancellationToken);
var analyzerDiags = await driver.GetDiagnosticsAsync(compilation).ConfigureAwait(false);
return compDiags.AddRange(analyzerDiags);
var reportedDiagnostics = compDiags.AddRange(analyzerDiags);
return driver.ApplyProgrammaticSuppressions(reportedDiagnostics, compilation);
}
finally
{
......@@ -898,7 +899,7 @@ private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, As
finally
{
// Update the diagnostic results based on the diagnostics reported on the driver.
_analysisResultBuilder.StoreAnalysisResult(analysisScope, driver, _compilation, _analysisState.GetAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: false);
_analysisResultBuilder.ApplySuppressionsAndStoreAnalysisResult(analysisScope, driver, _compilation, _analysisState.GetAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: false);
}
}
}
......@@ -1227,8 +1228,9 @@ public async Task<AnalyzerTelemetryInfo> GetAnalyzerTelemetryInfoAsync(Diagnosti
try
{
var actionCounts = await GetAnalyzerActionCountsAsync(analyzer, cancellationToken).ConfigureAwait(false);
var suppressionActionCounts = analyzer is DiagnosticSuppressor ? 1 : 0;
var executionTime = GetAnalyzerExecutionTime(analyzer);
return new AnalyzerTelemetryInfo(actionCounts, executionTime);
return new AnalyzerTelemetryInfo(actionCounts, suppressionActionCounts, executionTime);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
......@@ -1453,4 +1454,92 @@ public void ReportDiagnostic(Diagnostic diagnostic)
/// </summary>
public ControlFlowGraph GetControlFlowGraph() => DiagnosticAnalysisContextHelpers.GetControlFlowGraph(Operation, _getControlFlowGraphOpt, _cancellationToken);
}
/// <summary>
/// Context for suppressing analyzer and/or compiler non-error diagnostics reported for the compilation.
/// </summary>
public struct SuppressionAnalysisContext
{
private readonly Action<Suppression> _addSuppression;
private readonly Func<SuppressionDescriptor, bool> _isSupportedSuppressionDescriptor;
private readonly Func<SyntaxTree, SemanticModel> _getSemanticModel;
/// <summary>
/// Analyzer and/or compiler non-error diagnostics reported for the compilation.
/// Each <see cref="DiagnosticSuppressor"/> only receives diagnostics whose IDs were declared suppressible in its <see cref="DiagnosticSuppressor.SupportedSuppressions"/>.
/// This may be a subset of the full set of reported diagnostics, as an optimization for
/// supporting incremental and partial analysis scenarios.
/// A diagnostic is considered suppressible by a DiagnosticSuppressor if *all* of the following conditions are met:
/// 1. Diagnostic is not already suppressed in source via pragma/suppress message attribute.
/// 2. Diagnostic's <see cref="Diagnostic.DefaultSeverity"/> is not <see cref="DiagnosticSeverity.Error"/>.
/// 3. Diagnostic is not tagged with <see cref="WellKnownDiagnosticTags.NotConfigurable"/> custom tag.
/// </summary>
public ImmutableArray<Diagnostic> ReportedDiagnostics { get; }
/// <summary>
/// <see cref="CodeAnalysis.Compilation"/> for the context.
/// </summary>
public Compilation Compilation { get; }
/// <summary>
/// Options specified for the analysis.
/// </summary>
public AnalyzerOptions Options { get; }
/// <summary>
/// Token to check for requested cancellation of the analysis.
/// </summary>
public CancellationToken CancellationToken { get; }
internal SuppressionAnalysisContext(
Compilation compilation,
AnalyzerOptions options,
ImmutableArray<Diagnostic> reportedDiagnostics,
Action<Suppression> suppressDiagnostic,
Func<SuppressionDescriptor, bool> isSupportedSuppressionDescriptor,
Func<SyntaxTree, SemanticModel> getSemanticModel,
CancellationToken cancellationToken)
{
Compilation = compilation;
Options = options;
ReportedDiagnostics = reportedDiagnostics;
_addSuppression = suppressDiagnostic;
_isSupportedSuppressionDescriptor = isSupportedSuppressionDescriptor;
_getSemanticModel = getSemanticModel;
CancellationToken = cancellationToken;
}
/// <summary>
/// Report a <see cref="Suppression"/> for a reported diagnostic.
/// </summary>
public void ReportSuppression(Suppression suppression)
{
if (!ReportedDiagnostics.Contains(suppression.SuppressedDiagnostic))
{
// Non-reported diagnostic with ID '{0}' cannot be suppressed.
var message = string.Format(CodeAnalysisResources.NonReportedDiagnosticCannotBeSuppressed, suppression.SuppressedDiagnostic.Id);
throw new ArgumentException(message);
}
if (!_isSupportedSuppressionDescriptor(suppression.Descriptor))
{
// Reported suppression with ID '{0}' is not supported by the suppressor.
var message = string.Format(CodeAnalysisResources.UnsupportedSuppressionReported, suppression.Descriptor.Id);
throw new ArgumentException(message);
}
if (suppression.Descriptor.IsDisabled(Compilation.Options))
{
// Suppression has been disabled by the end user through compilation options.
return;
}
_addSuppression(suppression);
}
/// <summary>
/// Gets a <see cref="SemanticModel"/> for the given <see cref="SyntaxTree"/>, which is shared across all analyzers.
/// </summary>
public SemanticModel GetSemanticModel(SyntaxTree syntaxTree) => _getSemanticModel(syntaxTree);
}
}
// 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>
/// The base type for diagnostic suppressors that can programmatically suppress analyzer and/or compiler non-error diagnostics.
/// </summary>
public abstract class DiagnosticSuppressor : DiagnosticAnalyzer
{
// Disallow suppressors from reporting diagnostics or registering analysis actions.
public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray<DiagnosticDescriptor>.Empty;
public sealed override void Initialize(AnalysisContext context) { }
/// <summary>
/// Returns a set of descriptors for the suppressions that this suppressor is capable of producing.
/// </summary>
public abstract ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; }
/// <summary>
/// Suppress analyzer and/or compiler non-error diagnostics reported for the compilation.
/// This may be a subset of the full set of reported diagnostics, as an optimization for
/// supporting incremental and partial analysis scenarios.
/// A diagnostic is considered suppressible by a DiagnosticSuppressor if *all* of the following conditions are met:
/// 1. Diagnostic is not already suppressed in source via pragma/suppress message attribute.
/// 2. Diagnostic's <see cref="Diagnostic.DefaultSeverity"/> is not <see cref="DiagnosticSeverity.Error"/>.
/// 3. Diagnostic is not tagged with <see cref="WellKnownDiagnosticTags.NotConfigurable"/> custom tag.
/// </summary>
public abstract void ReportSuppressions(SuppressionAnalysisContext context);
}
}
// 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;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Programmatic suppression of a <see cref="Diagnostic"/> by a <see cref="DiagnosticSuppressor"/>.
/// </summary>
public struct Suppression
{
private Suppression(SuppressionDescriptor descriptor, Diagnostic suppressedDiagnostic)
{
Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
SuppressedDiagnostic = suppressedDiagnostic ?? throw new ArgumentNullException(nameof(suppressedDiagnostic));
Debug.Assert(suppressedDiagnostic.ProgrammaticSuppressionInfo == null);
if (descriptor.SuppressedDiagnosticId != suppressedDiagnostic.Id)
{
// Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.
var message = string.Format(CodeAnalysisResources.InvalidDiagnosticSuppressionReported, suppressedDiagnostic.Id, descriptor.SuppressedDiagnosticId);
throw new ArgumentException(message);
}
}
/// <summary>
/// Creates a suppression of a <see cref="Diagnostic"/> with the given <see cref="SuppressionDescriptor"/>.
/// </summary>
/// <param name="descriptor">
/// Descriptor for the suppression, which must be from <see cref="DiagnosticSuppressor.SupportedSuppressions"/>
/// for the <see cref="DiagnosticSuppressor"/> creating this suppression.
/// </param>
/// <param name="suppressedDiagnostic">
/// <see cref="Diagnostic"/> to be suppressed, which must be from <see cref="SuppressionAnalysisContext.ReportedDiagnostics"/>
/// for the suppression context in which this suppression is being created.</param>
public static Suppression Create(SuppressionDescriptor descriptor, Diagnostic suppressedDiagnostic)
=> new Suppression(descriptor, suppressedDiagnostic);
/// <summary>
/// Descriptor for this suppression.
/// </summary>
public SuppressionDescriptor Descriptor { get; }
/// <summary>
/// Diagnostic suppressed by this suppression.
/// </summary>
public Diagnostic SuppressedDiagnostic { get; }
}
}
......@@ -3,6 +3,20 @@
Microsoft.CodeAnalysis.CommandLineArguments.EmitPdbFile.get -> bool
Microsoft.CodeAnalysis.CommandLineArguments.GetOutputFilePath(string outputFileName) -> string
Microsoft.CodeAnalysis.CommandLineArguments.GetPdbFilePath(string outputFileName) -> string
Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor
Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor.DiagnosticSuppressor() -> void
Microsoft.CodeAnalysis.Diagnostics.Suppression
Microsoft.CodeAnalysis.Diagnostics.Suppression.Descriptor.get -> Microsoft.CodeAnalysis.SuppressionDescriptor
Microsoft.CodeAnalysis.Diagnostics.Suppression.SuppressedDiagnostic.get -> Microsoft.CodeAnalysis.Diagnostic
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.Compilation.get -> Microsoft.CodeAnalysis.Compilation
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.GetSemanticModel(Microsoft.CodeAnalysis.SyntaxTree syntaxTree) -> Microsoft.CodeAnalysis.SemanticModel
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.ReportSuppression(Microsoft.CodeAnalysis.Diagnostics.Suppression suppression) -> void
Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext.ReportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>
Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetryInfo.SuppressionActionsCount.get -> int
Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetryInfo.SuppressionActionsCount.set -> void
Microsoft.CodeAnalysis.IArrayTypeSymbol.ElementNullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
Microsoft.CodeAnalysis.IDiscardSymbol.NullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
Microsoft.CodeAnalysis.IEventSymbol.NullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
......@@ -33,6 +47,13 @@ Microsoft.CodeAnalysis.NullableFlowState
Microsoft.CodeAnalysis.NullableFlowState.MaybeNull = 2 -> Microsoft.CodeAnalysis.NullableFlowState
Microsoft.CodeAnalysis.NullableFlowState.NotApplicable = 0 -> Microsoft.CodeAnalysis.NullableFlowState
Microsoft.CodeAnalysis.NullableFlowState.NotNull = 1 -> Microsoft.CodeAnalysis.NullableFlowState
Microsoft.CodeAnalysis.SuppressionDescriptor
Microsoft.CodeAnalysis.SuppressionDescriptor.Equals(Microsoft.CodeAnalysis.SuppressionDescriptor other) -> bool
Microsoft.CodeAnalysis.SuppressionDescriptor.Id.get -> string
Microsoft.CodeAnalysis.SuppressionDescriptor.Justification.get -> Microsoft.CodeAnalysis.LocalizableString
Microsoft.CodeAnalysis.SuppressionDescriptor.SuppressedDiagnosticId.get -> string
Microsoft.CodeAnalysis.SuppressionDescriptor.SuppressionDescriptor(string id, string suppressedDiagnosticId, Microsoft.CodeAnalysis.LocalizableString justification) -> void
Microsoft.CodeAnalysis.SuppressionDescriptor.SuppressionDescriptor(string id, string suppressedDiagnosticId, string justification) -> void
Microsoft.CodeAnalysis.TypeInfo.ConvertedNullability.get -> Microsoft.CodeAnalysis.NullabilityInfo
Microsoft.CodeAnalysis.TypeInfo.Nullability.get -> Microsoft.CodeAnalysis.NullabilityInfo
abstract Microsoft.CodeAnalysis.Compilation.ClassifyCommonConversion(Microsoft.CodeAnalysis.ITypeSymbol source, Microsoft.CodeAnalysis.ITypeSymbol destination) -> Microsoft.CodeAnalysis.Operations.CommonConversion
......@@ -41,6 +62,8 @@ abstract Microsoft.CodeAnalysis.Compilation.GetSymbolsWithName(string name, Micr
abstract Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions.TryGetValue(string key, out string value) -> bool
abstract Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider.GetOptions(Microsoft.CodeAnalysis.AdditionalText textFile) -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions
abstract Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider.GetOptions(Microsoft.CodeAnalysis.SyntaxTree tree) -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions
abstract Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void
abstract Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.SuppressionDescriptor>
abstract Microsoft.CodeAnalysis.Diagnostics.SymbolStartAnalysisContext.RegisterCodeBlockAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext> action) -> void
abstract Microsoft.CodeAnalysis.Diagnostics.SymbolStartAnalysisContext.RegisterCodeBlockStartAction<TLanguageKindEnum>(System.Action<Microsoft.CodeAnalysis.Diagnostics.CodeBlockStartAnalysisContext<TLanguageKindEnum>> action) -> void
abstract Microsoft.CodeAnalysis.Diagnostics.SymbolStartAnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.OperationKind> operationKinds) -> void
......@@ -259,6 +282,10 @@ Microsoft.CodeAnalysis.SymbolDisplayPartKind.ExtensionMethodName = 29 -> Microso
const Microsoft.CodeAnalysis.WellKnownMemberNames.SliceMethodName = "Slice" -> string
override Microsoft.CodeAnalysis.FlowAnalysis.CaptureId.Equals(object obj) -> bool
override Microsoft.CodeAnalysis.FlowAnalysis.CaptureId.GetHashCode() -> int
override Microsoft.CodeAnalysis.SuppressionDescriptor.Equals(object obj) -> bool
override Microsoft.CodeAnalysis.SuppressionDescriptor.GetHashCode() -> int
override sealed Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context) -> void
override sealed Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DiagnosticDescriptor>
static Microsoft.CodeAnalysis.AnalyzerConfig.Parse(Microsoft.CodeAnalysis.Text.SourceText text, string pathToFile) -> Microsoft.CodeAnalysis.AnalyzerConfig
static Microsoft.CodeAnalysis.AnalyzerConfig.Parse(string text, string pathToFile) -> Microsoft.CodeAnalysis.AnalyzerConfig
static Microsoft.CodeAnalysis.AnalyzerConfig.ReservedKeys.get -> System.Collections.Immutable.ImmutableHashSet<string>
......@@ -270,6 +297,7 @@ static Microsoft.CodeAnalysis.AnalyzerConfigSet.Create<TList>(TList analyzerConf
static Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions.KeyComparer.get -> System.StringComparer
override Microsoft.CodeAnalysis.NullabilityInfo.Equals(object other) -> bool
override Microsoft.CodeAnalysis.NullabilityInfo.GetHashCode() -> int
static Microsoft.CodeAnalysis.Diagnostics.Suppression.Create(Microsoft.CodeAnalysis.SuppressionDescriptor descriptor, Microsoft.CodeAnalysis.Diagnostic suppressedDiagnostic) -> Microsoft.CodeAnalysis.Diagnostics.Suppression
static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IBlockOperation body, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph
static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IConstructorBodyOperation constructorBody, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph
static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IFieldInitializerOperation initializer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph
......
......@@ -19,6 +19,11 @@
{1}.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Blok dané operace nepatří do aktuálního analytického kontextu.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Parametr {0} musí být symbol z této kompilace nebo některé odkazované sestavení.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">Daná operace má nadřazenou položku, která není null.</target>
......@@ -74,6 +84,26 @@
<target state="translated">Analyzátor {0} má v SupportedDiagnostics deskriptor s hodnotou null.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Nevyřešeno: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() musí vracet instanci {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Hodnota je moc velká, než aby se dala vyjádřit jako 30bitové nepodepsané celé číslo.</target>
......
......@@ -19,6 +19,11 @@
"{1}".</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Der angegebene Operationsblock gehört nicht zum aktuellen Analysekontext.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Der Parameter "{0}" muss ein Symbol aus dieser Zusammenstellung oder eine referenzierte Assembly sein.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">Die angegebene Operation weist ein übergeordnetes Element ungleich NULL auf.</target>
......@@ -74,6 +84,26 @@
<target state="translated">Der Analyzer "{0}" enthält einen NULL-Deskriptor in "SupportedDiagnostics".</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Nicht aufgelöst: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() muss eine Instanz von {1} zurückgeben.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Der Wert ist zu groß, um als ganze 30-Bit-Zahl ohne Vorzeichen dargestellt zu werden.</target>
......
......@@ -19,6 +19,11 @@
'{1}'.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">El bloque de operaciones dado no pertenece al contexto de análisis actual.</target>
......@@ -34,6 +39,11 @@
<target state="translated">El parámetro "{0}" debe ser un símbolo de esta compilación o algún ensamble al que se hace referencia.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">La operación dada tiene un elemento primario no nulo.</target>
......@@ -74,6 +84,26 @@
<target state="translated">El analizador de "{0}" contiene un descriptor nulo en "SupportedDiagnostics".</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Sin resolver: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() debe devolver una instancia de {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Valor demasiado largo para representarse como entero sin signo de 30 bits.</target>
......
......@@ -19,6 +19,11 @@
'{1}'.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Le bloc d'opérations donné n'appartient pas au contexte d'analyse actuel.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Le paramètre '{0}' doit être un symbole de cette compilation ou un assembly référencé.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">L'opération donnée a un parent non-null.</target>
......@@ -74,6 +84,26 @@
<target state="translated">L'analyseur '{0}' contient un descripteur null dans 'SupportedDiagnostics'.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Non résolu : </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() doit retourner une instance de {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">La valeur est trop grande pour être représentée comme un entier non signé 30 bits.</target>
......
......@@ -19,6 +19,11 @@
'{1}'.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Il blocco operazioni specificato non appartiene al contesto di analisi corrente.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Il parametro '{0}' deve essere un simbolo di questa compilazione oppure un qualsiasi assembly cui viene fatto riferimento.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">L'operazione specificata contiene un elemento padre non Null.</target>
......@@ -74,6 +84,26 @@
<target state="translated">L'analizzatore '{0}' contiene un descrittore Null nel relativo elemento 'SupportedDiagnostics'.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Non risolto: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() deve restituire un'istanza di {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Il valore è troppo grande per essere rappresentato come intero senza segno a 30 bit.</target>
......
......@@ -19,6 +19,11 @@
'{1}'。</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">指定した操作ブロックが現在の分析コンテストに属していません。</target>
......@@ -34,6 +39,11 @@
<target state="translated">パラメーター '{0}' は、このコンパイルまたはいくつかの参照アセンブリのシンボルにする必要があります。</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">指定した操作の親が null 以外です。</target>
......@@ -74,6 +84,26 @@
<target state="translated">アナライザー '{0}' の 'SupportedDiagnostics' に null 記述子が含まれています。</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">未解決: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() は {1} のインスタンスを返す必要があります。</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">値が大きすぎるため、30 ビットの符号なし整数として表すことができません。</target>
......
......@@ -19,6 +19,11 @@
'{1}'</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">지정한 작업 블록이 현재 분석 컨텍스트에 속하지 않습니다.</target>
......@@ -34,6 +39,11 @@
<target state="translated">'{0}' 매개 변수는 이 컴파일 또는 일부 참조된 어셈블리의 기호여야 합니다.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">지정한 작업에 null이 아닌 부모가 있습니다.</target>
......@@ -74,6 +84,26 @@
<target state="translated">'{0}' 분석기의 'SupportedDiagnostics'에 null 설명자가 포함되어 있습니다.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">확인되지 않음: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata()는 {1}의 인스턴스를 반환해야 합니다.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">값이 너무 커서 30비트 정수로 표시할 수 없습니다.</target>
......
......@@ -19,6 +19,11 @@
„{1}”.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Dany blok operacji nie należy do bieżącego kontekstu analizy.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Parametr „{0}” musi być symbolem z tej kompilacji lub przywoływanym zestawem.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">Dana operacja ma element nadrzędny inny niż null.</target>
......@@ -74,6 +84,26 @@
<target state="translated">Analizator „{0}” zawiera deskryptor null we właściwości „SupportedDiagnostics”.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Nierozpoznane: </target>
......@@ -204,6 +234,11 @@
<target state="translated">Metoda {0}.GetMetadata() musi zwrócić wystąpienie {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Wartość jest zbyt duża, dlatego nie może być reprezentowana jako 30-bitowa liczba całkowita bez znaku.</target>
......
......@@ -19,6 +19,11 @@
'{1}'.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">O bloqueio de operação fornecido não pertence ao contexto de análise atual.</target>
......@@ -34,6 +39,11 @@
<target state="translated">O parâmetro '{0}' deve ser um símbolo desta compilação ou um assembly referenciado.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">A operação especificada tem um pai não nulo.</target>
......@@ -74,6 +84,26 @@
<target state="translated">O analisador '{0}' contém um descritor nulo em seu 'SupportedDiagnostics'.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Não resolvido: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() deve retornar uma instância de {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Valor muito grande para ser representado como um inteiro não assinado de 30 bits.</target>
......
......@@ -19,6 +19,11 @@
"{1}".</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Заданный блок операции не принадлежит текущему контексту анализа.</target>
......@@ -34,6 +39,11 @@
<target state="translated">Параметр "{0}" должен быть символом из этой компиляции или из другой сборки, на которую она ссылается.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">Заданная операция имеет родительский элемент, отличный от NULL.</target>
......@@ -74,6 +84,26 @@
<target state="translated">Анализатор "{0}" содержит дескриптор null в разделе "SupportedDiagnostics".</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Не разрешено:</target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() должен возвращать экземпляр {1}.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Слишком большое значение для представления в виде 30-разрядного целого числа без знака.</target>
......
......@@ -19,6 +19,11 @@
'{1}'.</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">Belirtilen işlem bloğu geçerli analiz bağlamına ait değil.</target>
......@@ -34,6 +39,11 @@
<target state="translated">'{0}' parametresi bu derleme veya bazı başvurulan derleme bir sembol olması gerekir.</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">Belirtilen işlemin null olmayan bir üst öğesi var.</target>
......@@ -74,6 +84,26 @@
<target state="translated">Çözümleyicisi '{0}', 'SupportedDiagnostics' boş bir tanımlayıcısı içerir.</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">Çözümlenmemiş: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() bir {1} örneği döndürmelidir.</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">Değer, 30 bit işaretsiz tamsayı olarak temsil edilemeyecek kadar büyük.</target>
......
......@@ -19,6 +19,11 @@
“{1}”。</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">给定操作块不属于当前的分析上下文。</target>
......@@ -34,6 +39,11 @@
<target state="translated">参数“{0}”必须是此编译或某些引用程序集的符号。</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">给定操作具有一个非 null 父级。</target>
......@@ -74,6 +84,26 @@
<target state="translated">分析器“{0}”在其 "SupportedDiagnostics" 中包含 null 描述符。</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">未解析:</target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() 必须返回 {1} 的实例。</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">值太大,无法表示为 30 位无符号整数。</target>
......
......@@ -19,6 +19,11 @@
'{1}'。</target>
<note />
</trans-unit>
<trans-unit id="InvalidDiagnosticSuppressionReported">
<source>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</source>
<target state="new">Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</target>
<note />
</trans-unit>
<trans-unit id="InvalidOperationBlockForAnalysisContext">
<source>Given operation block does not belong to the current analysis context.</source>
<target state="translated">指定的作業區塊不屬於目前的分析內容。</target>
......@@ -34,6 +39,11 @@
<target state="translated">參數 '{0}' 必須是此編譯或某些參考組件中的符號。</target>
<note />
</trans-unit>
<trans-unit id="NonReportedDiagnosticCannotBeSuppressed">
<source>Non-reported diagnostic with ID '{0}' cannot be suppressed.</source>
<target state="new">Non-reported diagnostic with ID '{0}' cannot be suppressed.</target>
<note />
</trans-unit>
<trans-unit id="NotARootOperation">
<source>Given operation has a non-null parent.</source>
<target state="translated">指定的作業有非 null 的父代。</target>
......@@ -74,6 +84,26 @@
<target state="translated">分析器 '{0}' 在其 'SupportedDiagnostics' 中包含一個 null 描述項。</target>
<note />
</trans-unit>
<trans-unit id="SupportedSuppressionsHasNullDescriptor">
<source>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</source>
<target state="new">Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorMessage">
<source>Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</source>
<target state="new">Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</target>
<note />
</trans-unit>
<trans-unit id="SuppressionDiagnosticDescriptorTitle">
<source>Programmatic suppression of an analyzer diagnostic</source>
<target state="new">Programmatic suppression of an analyzer diagnostic</target>
<note />
</trans-unit>
<trans-unit id="SuppressionIdCantBeNullOrWhitespace">
<source>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</source>
<target state="new">A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</target>
<note />
</trans-unit>
<trans-unit id="Unresolved">
<source>Unresolved: </source>
<target state="translated">未解析: </target>
......@@ -204,6 +234,11 @@
<target state="translated">{0}.GetMetadata() 必須傳回 {1} 的執行個體。</target>
<note />
</trans-unit>
<trans-unit id="UnsupportedSuppressionReported">
<source>Reported suppression with ID '{0}' is not supported by the suppressor.</source>
<target state="new">Reported suppression with ID '{0}' is not supported by the suppressor.</target>
<note />
</trans-unit>
<trans-unit id="Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer">
<source>Value too large to be represented as a 30 bit unsigned integer.</source>
<target state="translated">值太大,無法呈現為 30 位元不帶正負號的整數。</target>
......
......@@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Linq;
using Roslyn.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities
......
' 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.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
Imports Roslyn.Test.Utilities
......
......@@ -7601,7 +7601,8 @@ BC2006: option 'analyzerconfig' requires ':<file_list>']]>
Optional additionalFlags As String() = Nothing,
Optional expectedInfoCount As Integer = 0,
Optional expectedWarningCount As Integer = 0,
Optional expectedErrorCount As Integer = 0) As String
Optional expectedErrorCount As Integer = 0,
Optional analyzers As ImmutableArray(Of DiagnosticAnalyzer) = Nothing) As String
Dim args = {
"/nologo", "/preferreduilang:en", "/t:library",
sourceFile.Path
......@@ -7613,7 +7614,7 @@ BC2006: option 'analyzerconfig' requires ':<file_list>']]>
args = args.Append(additionalFlags)
End If
Dim vbc = New MockVisualBasicCompiler(Nothing, sourceDir.Path, args)
Dim vbc = New MockVisualBasicCompiler(Nothing, sourceDir.Path, args, analyzers)
Dim outWriter = New StringWriter(CultureInfo.InvariantCulture)
Dim exitCode = vbc.Run(outWriter, Nothing)
Dim output = outWriter.ToString()
......@@ -9338,6 +9339,224 @@ End Class").Path
Assert.Equal(1, exitCode)
Assert.Contains("vbc : error BC37253: The pathmap option was incorrectly formatted.", outWriter.ToString(), StringComparison.Ordinal)
End Sub
<WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")>
<Fact>
Public Sub TestSuppression_CompilerWarning()
' warning BC40008 : 'C' is obsolete
Dim source = "
Imports System
<Obsolete>
Class C
End Class
Class D
Inherits C
End Class"
Dim dir = Temp.CreateDirectory()
Dim file = dir.CreateFile("a.vb")
file.WriteAllText(source)
' Verify that compiler warning BC40008 is reported.
Dim output = VerifyOutput(dir, file, expectedWarningCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False)
Assert.Contains("warning BC40008", output, StringComparison.Ordinal)
' Verify that compiler warning BC40008 is suppressed with diagnostic suppressor
' and info diagnostic is logged with programmatic suppression information.
Dim suppressor = New DiagnosticSuppressorForId("BC40008")
' Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
Dim suppressionMessage = String.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_UseOfObsoleteSymbolNoMessage1, "C"), Location.None).GetMessage(CultureInfo.InvariantCulture),
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification)
Dim suppressors = ImmutableArray.Create(Of DiagnosticAnalyzer)(suppressor)
output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=suppressors)
Assert.DoesNotContain("warning BC40008", output, StringComparison.Ordinal)
Assert.Contains("info SP0001", output, StringComparison.Ordinal)
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(file.Path)
End Sub
<WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")>
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/36215")>
Public Sub TestSuppression_CompilerWarningAsError()
' warning BC40008 : 'C' is obsolete
Dim source = "
Imports System
<Obsolete>
Class C
End Class
Class D
Inherits C
End Class"
Dim dir = Temp.CreateDirectory()
Dim file = dir.CreateFile("a.vb")
file.WriteAllText(source)
' Verify that compiler warning BC40008 is reported.
Dim output = VerifyOutput(dir, file, expectedWarningCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False)
Assert.Contains("warning BC40008", output, StringComparison.Ordinal)
' Verify that compiler warning BC40008 is reported as error for /warnaserror.
output = VerifyOutput(dir, file, expectedErrorCount:=1, additionalFlags:={"/warnaserror+"},
includeCurrentAssemblyAsAnalyzerReference:=False)
Assert.Contains("error BC40008", output, StringComparison.Ordinal)
' Verify that compiler warning BC40008 is suppressed with diagnostic suppressor even with /warnaserror
' and info diagnostic is logged with programmatic suppression information.
Dim suppressor = New DiagnosticSuppressorForId("BC40008")
Dim suppressors = ImmutableArray.Create(Of DiagnosticAnalyzer)(suppressor)
output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0, expectedErrorCount:=0,
additionalFlags:={"/warnaserror+"},
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=suppressors)
Assert.DoesNotContain($"warning BC40008", output, StringComparison.Ordinal)
Assert.DoesNotContain($"error BC40008", output, StringComparison.Ordinal)
' Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
Dim suppressionMessage = String.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_UseOfObsoleteSymbolNoMessage1, "C"), Location.None).GetMessage(CultureInfo.InvariantCulture),
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification)
Assert.Contains("info SP0001", output, StringComparison.Ordinal)
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(file.Path)
End Sub
<WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")>
<Fact>
Public Sub TestNoSuppression_CompilerError()
' warning BC30203 : Identifier expected
Dim source = "
Class
End Class"
Dim dir = Temp.CreateDirectory()
Dim file = dir.CreateFile("a.vb")
file.WriteAllText(source)
' Verify that compiler error BC30203 is reported.
Dim output = VerifyOutput(dir, file, expectedErrorCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False)
Assert.Contains("error BC30203", output, StringComparison.Ordinal)
' Verify that compiler error BC30203 cannot be suppressed with diagnostic suppressor.
Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(New DiagnosticSuppressorForId("BC30203"))
output = VerifyOutput(dir, file, expectedErrorCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzers)
Assert.Contains("error BC30203", output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(file.Path)
End Sub
<WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")>
<Fact>
Public Sub TestSuppression_AnalyzerWarning()
Dim source = "
Class C
End Class"
Dim dir = Temp.CreateDirectory()
Dim file = dir.CreateFile("a.vb")
file.WriteAllText(source)
' Verify that analyzer warning is reported.
Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable:=True)
Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)
Dim output = VerifyOutput(dir, file, expectedWarningCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzers)
Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
' Verify that analyzer warning is suppressed with diagnostic suppressor
' and info diagnostic is logged with programmatic suppression information.
Dim suppressor = New DiagnosticSuppressorForId(analyzer.Descriptor.Id)
' Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
Dim suppressionMessage = String.Format(CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
suppressor.SuppressionDescriptor.SuppressedDiagnosticId,
analyzer.Descriptor.MessageFormat,
suppressor.SuppressionDescriptor.Id,
suppressor.SuppressionDescriptor.Justification)
Dim analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor)
output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzerAndSuppressor)
Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
Assert.Contains("info SP0001", output, StringComparison.Ordinal)
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal)
' Verify that analyzer warning is reported as error for /warnaserror.
output = VerifyOutput(dir, file, expectedErrorCount:=1,
additionalFlags:={"/warnaserror+"},
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzers)
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
' Verify that analyzer warning is suppressed with diagnostic suppressor even with /warnaserror
' and info diagnostic is logged with programmatic suppression information.
output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0, expectedErrorCount:=0,
additionalFlags:={"/warnaserror+"},
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzerAndSuppressor)
Assert.DoesNotContain($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
Assert.Contains("info SP0001", output, StringComparison.Ordinal)
Assert.Contains(suppressionMessage, output, StringComparison.Ordinal)
' Verify that "NotConfigurable" analyzer warning cannot be suppressed with diagnostic suppressor even with /warnaserror.
analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable:=False)
suppressor = New DiagnosticSuppressorForId(analyzer.Descriptor.Id)
analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor)
output = VerifyOutput(dir, file, expectedWarningCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzerAndSuppressor)
Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(file.Path)
End Sub
<WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")>
<Fact>
Public Sub TestNoSuppression_AnalyzerError()
Dim source = "
Class C
End Class"
Dim dir = Temp.CreateDirectory()
Dim file = dir.CreateFile("a.vb")
file.WriteAllText(source)
' Verify that analyzer error is reported.
Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Error, configurable:=True)
Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)
Dim output = VerifyOutput(dir, file, expectedErrorCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzers)
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
' Verify that analyzer error cannot be suppressed with diagnostic suppressor.
Dim suppressor = New DiagnosticSuppressorForId(analyzer.Descriptor.Id)
Dim analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor)
output = VerifyOutput(dir, file, expectedErrorCount:=1,
includeCurrentAssemblyAsAnalyzerReference:=False,
analyzers:=analyzerAndSuppressor)
Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal)
CleanupAllGeneratedFiles(file.Path)
End Sub
End Class
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
......
......@@ -1989,7 +1989,7 @@ class MyClass
Dim project = workspace.CurrentSolution.Projects.Single()
' Add analyzer
Dim analyzer = New HiddenDiagnosticsCompilationAnalyzer()
Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Hidden, configurable:=False)
Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer))
project = project.AddAnalyzerReference(analyzerReference)
......@@ -2002,7 +2002,7 @@ class MyClass
Assert.Equal(1, descriptorsMap.Count)
Dim descriptors = descriptorsMap.First().Value
Assert.Equal(1, descriptors.Length)
Assert.Equal(HiddenDiagnosticsCompilationAnalyzer.Descriptor.Id, descriptors.Single().Id)
Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id)
' Force project analysis
incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged:=True, reasons:=InvocationReasons.Empty, cancellationToken:=CancellationToken.None).Wait()
......@@ -2018,7 +2018,7 @@ class MyClass
' Get diagnostics explicitly
Dim hiddenDiagnostics = diagnosticService.GetDiagnosticsAsync(project.Solution, project.Id).WaitAndGetResult(CancellationToken.None)
Assert.Equal(1, hiddenDiagnostics.Count())
Assert.Equal(HiddenDiagnosticsCompilationAnalyzer.Descriptor.Id, hiddenDiagnostics.Single().Id)
Assert.Equal(analyzer.Descriptor.Id, hiddenDiagnostics.Single().Id)
End Using
End Sub
......
......@@ -357,6 +357,9 @@ private static IEnumerable<AnalyzerPerformanceInfo> Convert(IEnumerable<(Diagnos
Contract.ThrowIfFalse(project.SupportsCompilation);
AssertCompilation(project, compilation);
// Always run diagnostic suppressors.
analyzers = AppendDiagnosticSuppressors(analyzers, allAnalyzersAndSuppressors: service.GetDiagnosticAnalyzers(project));
// Create driver that holds onto compilation and associated analyzers
return compilation.WithAnalyzers(filteredAnalyzers, GetAnalyzerOptions());
......@@ -788,6 +791,15 @@ IEnumerable<DiagnosticData> ConvertToLocalDiagnosticsWithCompilation()
}
}
public static IEnumerable<DiagnosticAnalyzer> AppendDiagnosticSuppressors(IEnumerable<DiagnosticAnalyzer> analyzers, IEnumerable<DiagnosticAnalyzer> allAnalyzersAndSuppressors)
{
// Append while ensuring no duplicates are added.
var diagnosticSuppressors = allAnalyzersAndSuppressors.OfType<DiagnosticSuppressor>();
return !diagnosticSuppressors.Any()
? analyzers
: analyzers.Concat(diagnosticSuppressors).Distinct();
}
/// <summary>
/// Right now, there is no API compiler will tell us whether DiagnosticAnalyzer has compilation end analysis or not
///
......
......@@ -180,6 +180,7 @@ private static void Serialize(ObjectWriter writer, AnalyzerTelemetryInfo telemet
writer.WriteInt32(telemetryInfo.OperationBlockActionsCount);
writer.WriteInt32(telemetryInfo.OperationBlockStartActionsCount);
writer.WriteInt32(telemetryInfo.OperationBlockEndActionsCount);
writer.WriteInt32(telemetryInfo.SuppressionActionsCount);
writer.WriteInt64(telemetryInfo.ExecutionTime.Ticks);
writer.WriteBoolean(telemetryInfo.Concurrent);
}
......@@ -204,6 +205,7 @@ private static AnalyzerTelemetryInfo Deserialize(ObjectReader reader, Cancellati
var operationBlockActionsCount = reader.ReadInt32();
var operationBlockStartActionsCount = reader.ReadInt32();
var operationBlockEndActionsCount = reader.ReadInt32();
var suppressionActionsCount = reader.ReadInt32();
var executionTime = new TimeSpan(reader.ReadInt64());
var concurrent = reader.ReadBoolean();
......@@ -229,6 +231,8 @@ private static AnalyzerTelemetryInfo Deserialize(ObjectReader reader, Cancellati
OperationBlockEndActionsCount = operationBlockEndActionsCount,
OperationBlockActionsCount = operationBlockActionsCount,
SuppressionActionsCount = suppressionActionsCount,
ExecutionTime = executionTime,
Concurrent = concurrent
......
......@@ -28,6 +28,7 @@ internal class DiagnosticLogAggregator : LogAggregator
"Analyzer.OperationBlockStart",
"Analyzer.SymbolEnd",
"Analyzer.SymbolStart",
"Analyzer.Suppression",
};
private readonly DiagnosticAnalyzerService _owner;
......@@ -87,6 +88,7 @@ public void SetAnalyzerTypeCount(AnalyzerTelemetryInfo analyzerTelemetryInfo)
Counts[13] = analyzerTelemetryInfo.OperationBlockStartActionsCount;
Counts[14] = analyzerTelemetryInfo.SymbolStartActionsCount;
Counts[15] = analyzerTelemetryInfo.SymbolEndActionsCount;
Counts[16] = analyzerTelemetryInfo.SuppressionActionsCount;
}
}
}
......
......@@ -703,16 +703,24 @@ public override void Initialize(AnalysisContext context)
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class HiddenDiagnosticsCompilationAnalyzer : DiagnosticAnalyzer
public class CompilationAnalyzerWithSeverity : DiagnosticAnalyzer
{
public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
"ID1000",
"Description1",
string.Empty,
"Analysis",
DiagnosticSeverity.Hidden,
true,
customTags: WellKnownDiagnosticTags.NotConfigurable);
public CompilationAnalyzerWithSeverity(
DiagnosticSeverity severity,
bool configurable)
{
var customTags = !configurable ? new[] { WellKnownDiagnosticTags.NotConfigurable } : Array.Empty<string>();
Descriptor = new DiagnosticDescriptor(
"ID1000",
"Description1",
string.Empty,
"Analysis",
severity,
true,
customTags: customTags);
}
public DiagnosticDescriptor Descriptor { get; }
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
......@@ -723,7 +731,7 @@ public override void Initialize(AnalysisContext context)
private void OnCompilation(CompilationAnalysisContext context)
{
// Report the hidden diagnostic on all trees in compilation.
// Report the diagnostic on all trees in compilation.
foreach (var tree in context.Compilation.SyntaxTrees)
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, tree.GetRoot().GetLocation()));
......@@ -1661,5 +1669,162 @@ void verifySymbolStartAndOperationOrdering(SymbolStartAnalysisContext symbolStar
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressorForId : DiagnosticSuppressor
{
public SuppressionDescriptor SuppressionDescriptor { get; }
public DiagnosticSuppressorForId(string suppressedDiagnosticId, string suppressionId = null)
{
SuppressionDescriptor = new SuppressionDescriptor(
id: suppressionId ?? "SPR0001",
suppressedDiagnosticId: suppressedDiagnosticId,
justification: $"Suppress {suppressedDiagnosticId}");
}
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> ImmutableArray.Create(SuppressionDescriptor);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (var diagnostic in context.ReportedDiagnostics)
{
Assert.Equal(SuppressionDescriptor.SuppressedDiagnosticId, diagnostic.Id);
context.ReportSuppression(Suppression.Create(SuppressionDescriptor, diagnostic));
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressorThrowsExceptionFromSupportedSuppressions : DiagnosticSuppressor
{
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> throw new NotImplementedException();
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressorThrowsExceptionFromReportedSuppressions : DiagnosticSuppressor
{
private readonly SuppressionDescriptor _descriptor;
public DiagnosticSuppressorThrowsExceptionFromReportedSuppressions(string suppressedDiagnosticId)
{
_descriptor = new SuppressionDescriptor(
"SPR0001",
suppressedDiagnosticId,
$"Suppress {suppressedDiagnosticId}");
}
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> ImmutableArray.Create(_descriptor);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
throw new NotImplementedException();
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressor_UnsupportedSuppressionReported : DiagnosticSuppressor
{
private readonly SuppressionDescriptor _supportedDescriptor;
private readonly SuppressionDescriptor _unsupportedDescriptor;
public DiagnosticSuppressor_UnsupportedSuppressionReported(string suppressedDiagnosticId, string supportedSuppressionId, string unsupportedSuppressionId)
{
_supportedDescriptor = new SuppressionDescriptor(
supportedSuppressionId,
suppressedDiagnosticId,
$"Suppress {suppressedDiagnosticId}");
_unsupportedDescriptor = new SuppressionDescriptor(
unsupportedSuppressionId,
suppressedDiagnosticId,
$"Suppress {suppressedDiagnosticId}");
}
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> ImmutableArray.Create(_supportedDescriptor);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (var diagnostic in context.ReportedDiagnostics)
{
Assert.Equal(_unsupportedDescriptor.SuppressedDiagnosticId, diagnostic.Id);
context.ReportSuppression(Suppression.Create(_unsupportedDescriptor, diagnostic));
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressor_InvalidDiagnosticSuppressionReported : DiagnosticSuppressor
{
private readonly SuppressionDescriptor _supportedDescriptor;
private readonly SuppressionDescriptor _unsupportedDescriptor;
public DiagnosticSuppressor_InvalidDiagnosticSuppressionReported(string suppressedDiagnosticId, string unsupportedSuppressedDiagnosticId)
{
_supportedDescriptor = new SuppressionDescriptor(
"SPR0001",
suppressedDiagnosticId,
$"Suppress {suppressedDiagnosticId}");
_unsupportedDescriptor = new SuppressionDescriptor(
"SPR0002",
unsupportedSuppressedDiagnosticId,
$"Suppress {unsupportedSuppressedDiagnosticId}");
}
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> ImmutableArray.Create(_supportedDescriptor);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (var diagnostic in context.ReportedDiagnostics)
{
Assert.Equal(_supportedDescriptor.SuppressedDiagnosticId, diagnostic.Id);
context.ReportSuppression(Suppression.Create(_unsupportedDescriptor, diagnostic));
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DiagnosticSuppressor_NonReportedDiagnosticCannotBeSuppressed : DiagnosticSuppressor
{
private readonly SuppressionDescriptor _descriptor1, _descriptor2;
private readonly string _nonReportedDiagnosticId;
public DiagnosticSuppressor_NonReportedDiagnosticCannotBeSuppressed(string reportedDiagnosticId, string nonReportedDiagnosticId)
{
_descriptor1 = new SuppressionDescriptor(
"SPR0001",
reportedDiagnosticId,
$"Suppress {reportedDiagnosticId}");
_descriptor2 = new SuppressionDescriptor(
"SPR0002",
nonReportedDiagnosticId,
$"Suppress {nonReportedDiagnosticId}");
_nonReportedDiagnosticId = nonReportedDiagnosticId;
}
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
=> ImmutableArray.Create(_descriptor1, _descriptor2);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
var nonReportedDiagnostic = Diagnostic.Create(
id: _nonReportedDiagnosticId,
category: "Category",
message: "Message",
severity: DiagnosticSeverity.Warning,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
warningLevel: 1);
context.ReportSuppression(Suppression.Create(_descriptor2, nonReportedDiagnostic));
}
}
}
}
......@@ -28,6 +28,8 @@ public sealed class DiagnosticDescription
private readonly bool _argumentOrderDoesNotMatter;
private readonly Type _errorCodeType;
private readonly bool _ignoreArgumentsWhenComparing;
private readonly DiagnosticSeverity? _defaultSeverityOpt;
private readonly DiagnosticSeverity? _effectiveSeverityOpt;
// fields for DiagnosticDescriptions constructed via factories
private readonly Func<SyntaxNode, bool> _syntaxPredicate;
......@@ -64,7 +66,9 @@ private IEnumerable<string> GetArgumentsAsStrings()
LinePosition? startLocation,
Func<SyntaxNode, bool> syntaxNodePredicate,
bool argumentOrderDoesNotMatter,
Type errorCodeType = null)
Type errorCodeType = null,
DiagnosticSeverity? defaultSeverityOpt = null,
DiagnosticSeverity? effectiveSeverityOpt = null)
{
_code = code;
_isWarningAsError = isWarningAsError;
......@@ -74,6 +78,8 @@ private IEnumerable<string> GetArgumentsAsStrings()
_syntaxPredicate = syntaxNodePredicate;
_argumentOrderDoesNotMatter = argumentOrderDoesNotMatter;
_errorCodeType = errorCodeType ?? code.GetType();
_defaultSeverityOpt = defaultSeverityOpt;
_effectiveSeverityOpt = effectiveSeverityOpt;
}
public DiagnosticDescription(
......@@ -83,7 +89,9 @@ private IEnumerable<string> GetArgumentsAsStrings()
LinePosition? startLocation,
Func<SyntaxNode, bool> syntaxNodePredicate,
bool argumentOrderDoesNotMatter,
Type errorCodeType = null)
Type errorCodeType = null,
DiagnosticSeverity? defaultSeverityOpt = null,
DiagnosticSeverity? effectiveSeverityOpt = null)
{
_code = code;
_isWarningAsError = false;
......@@ -93,13 +101,17 @@ private IEnumerable<string> GetArgumentsAsStrings()
_syntaxPredicate = syntaxNodePredicate;
_argumentOrderDoesNotMatter = argumentOrderDoesNotMatter;
_errorCodeType = errorCodeType ?? code.GetType();
_defaultSeverityOpt = defaultSeverityOpt;
_effectiveSeverityOpt = effectiveSeverityOpt;
}
public DiagnosticDescription(Diagnostic d, bool errorCodeOnly)
public DiagnosticDescription(Diagnostic d, bool errorCodeOnly, bool includeDefaultSeverity = false, bool includeEffectiveSeverity = false)
{
_code = d.Code;
_isWarningAsError = d.IsWarningAsError;
_location = d.Location;
_defaultSeverityOpt = includeDefaultSeverity ? d.DefaultSeverity : (DiagnosticSeverity?)null;
_effectiveSeverityOpt = includeEffectiveSeverity ? d.Severity : (DiagnosticSeverity?)null;
DiagnosticWithInfo dinfo = null;
if (d.Code == 0)
......@@ -160,17 +172,27 @@ public DiagnosticDescription(Diagnostic d, bool errorCodeOnly)
public DiagnosticDescription WithArguments(params string[] arguments)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, arguments, _startPosition, _syntaxPredicate, false, _errorCodeType);
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, arguments, _startPosition, _syntaxPredicate, false, _errorCodeType, _defaultSeverityOpt, _effectiveSeverityOpt);
}
public DiagnosticDescription WithArgumentsAnyOrder(params string[] arguments)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, arguments, _startPosition, _syntaxPredicate, true, _errorCodeType);
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, arguments, _startPosition, _syntaxPredicate, true, _errorCodeType, _defaultSeverityOpt, _effectiveSeverityOpt);
}
public DiagnosticDescription WithWarningAsError(bool isWarningAsError)
{
return new DiagnosticDescription(_code, isWarningAsError, _squiggledText, _arguments, _startPosition, _syntaxPredicate, true, _errorCodeType);
return new DiagnosticDescription(_code, isWarningAsError, _squiggledText, _arguments, _startPosition, _syntaxPredicate, true, _errorCodeType, _defaultSeverityOpt, _effectiveSeverityOpt);
}
public DiagnosticDescription WithDefaultSeverity(DiagnosticSeverity defaultSeverity)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, _startPosition, _syntaxPredicate, true, _errorCodeType, defaultSeverity, _effectiveSeverityOpt);
}
public DiagnosticDescription WithEffectiveSeverity(DiagnosticSeverity effectiveSeverity)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, _startPosition, _syntaxPredicate, true, _errorCodeType, _defaultSeverityOpt, effectiveSeverity);
}
/// <summary>
......@@ -178,7 +200,7 @@ public DiagnosticDescription WithWarningAsError(bool isWarningAsError)
/// </summary>
public DiagnosticDescription WithLocation(int line, int column)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, new LinePosition(line - 1, column - 1), _syntaxPredicate, _argumentOrderDoesNotMatter, _errorCodeType);
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, new LinePosition(line - 1, column - 1), _syntaxPredicate, _argumentOrderDoesNotMatter, _errorCodeType, _defaultSeverityOpt, _effectiveSeverityOpt);
}
/// <summary>
......@@ -187,11 +209,14 @@ public DiagnosticDescription WithLocation(int line, int column)
/// <param name="syntaxPredicate">The argument to syntaxPredicate will be the nearest SyntaxNode whose Span contains first squiggled character.</param>
public DiagnosticDescription WhereSyntax(Func<SyntaxNode, bool> syntaxPredicate)
{
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, _startPosition, syntaxPredicate, _argumentOrderDoesNotMatter, _errorCodeType);
return new DiagnosticDescription(_code, _isWarningAsError, _squiggledText, _arguments, _startPosition, syntaxPredicate, _argumentOrderDoesNotMatter, _errorCodeType, _defaultSeverityOpt, _effectiveSeverityOpt);
}
public object Code => _code;
public bool HasLocation => _startPosition != null;
public bool IsWarningAsError => _isWarningAsError;
public DiagnosticSeverity? DefaultSeverity => _defaultSeverityOpt;
public DiagnosticSeverity? EffectiveSeverity => _effectiveSeverityOpt;
public override bool Equals(object obj)
{
......@@ -280,6 +305,12 @@ public override bool Equals(object obj)
}
}
if (_defaultSeverityOpt != d._defaultSeverityOpt ||
_effectiveSeverityOpt != d._effectiveSeverityOpt)
{
return false;
}
return true;
}
......@@ -294,6 +325,10 @@ public override int GetHashCode()
hashCode = Hash.Combine(_arguments, hashCode);
if (_startPosition != null)
hashCode = Hash.Combine(hashCode, _startPosition.Value.GetHashCode());
if (_defaultSeverityOpt != null)
hashCode = Hash.Combine(hashCode, _defaultSeverityOpt.Value.GetHashCode());
if (_effectiveSeverityOpt != null)
hashCode = Hash.Combine(hashCode, _effectiveSeverityOpt.Value.GetHashCode());
return hashCode;
}
......@@ -362,6 +397,16 @@ public override string ToString()
sb.Append(".WithWarningAsError(true)");
}
if (_defaultSeverityOpt != null)
{
sb.Append($".WithDefaultSeverity(DiagnosticSeverity.{_defaultSeverityOpt.Value.ToString()})");
}
if (_effectiveSeverityOpt != null)
{
sb.Append($".WithEffectiveSeverity(DiagnosticSeverity.{_effectiveSeverityOpt.Value.ToString()})");
}
if (_syntaxPredicate != null && _showPredicate)
{
sb.Append(".WhereSyntax(...)");
......@@ -377,6 +422,8 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable
var language = actual.Any() && actual.First().Id.StartsWith("CS", StringComparison.Ordinal) ? CSharp : VisualBasic;
var includeDiagnosticMessagesAsComments = (language == CSharp);
int indentDepth = (language == CSharp) ? 4 : 1;
var includeDefaultSeverity = expected.Any() && expected.All(d => d.DefaultSeverity != null);
var includeEffectiveSeverity = expected.Any() && expected.All(d => d.EffectiveSeverity != null);
if (IsSortedOrEmpty(expected))
{
......@@ -430,7 +477,7 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable
}
}
var description = new DiagnosticDescription(d, errorCodeOnly: false);
var description = new DiagnosticDescription(d, errorCodeOnly: false, includeDefaultSeverity, includeEffectiveSeverity);
var diffDescription = description;
var idx = Array.IndexOf(expected, description);
if (idx != -1)
......
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
......@@ -69,7 +70,10 @@ private static void Verify(IEnumerable<Diagnostic> actual, DiagnosticDescription
throw new ArgumentException("Must specify expected errors.", nameof(expected));
}
var unmatched = actual.Select(d => new DiagnosticDescription(d, errorCodeOnly)).ToList();
var includeDefaultSeverity = expected.Any() && expected.All(e => e.DefaultSeverity != null);
var includeEffectiveSeverity = expected.Any() && expected.All(e => e.EffectiveSeverity != null);
var unmatched = actual.Select(d => new DiagnosticDescription(d, errorCodeOnly, includeDefaultSeverity, includeEffectiveSeverity))
.ToList();
// Try to match each of the 'expected' errors to one of the 'actual' ones.
// If any of the expected errors don't appear, fail test.
......@@ -162,7 +166,9 @@ public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c,
params DiagnosticDescription[] expected)
where TCompilation : Compilation
{
c = c.GetAnalyzerDiagnostics(analyzers, options, onAnalyzerException, logAnalyzerExceptionAsDiagnostics, reportSuppressedDiagnostics, diagnostics: out var diagnostics);
c = c.GetCompilationWithAnalyzerDiagnostics(analyzers, options, onAnalyzerException,
logAnalyzerExceptionAsDiagnostics, reportSuppressedDiagnostics,
includeCompilerDiagnostics: false, diagnostics: out var diagnostics);
diagnostics.Verify(expected);
return c; // note this is a new compilation
}
......@@ -187,18 +193,107 @@ public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c,
bool logAnalyzerExceptionAsDiagnostics = true)
where TCompilation : Compilation
{
c = GetAnalyzerDiagnostics(c, analyzers, options, onAnalyzerException, logAnalyzerExceptionAsDiagnostics, reportSuppressedDiagnostics, out var diagnostics);
c = GetCompilationWithAnalyzerDiagnostics(c, analyzers, options, onAnalyzerException,
logAnalyzerExceptionAsDiagnostics, reportSuppressedDiagnostics,
includeCompilerDiagnostics: false, out var diagnostics);
return diagnostics;
}
private static TCompilation GetAnalyzerDiagnostics<TCompilation>(
this TCompilation c,
DiagnosticAnalyzer[] analyzers,
AnalyzerOptions options,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
bool logAnalyzerExceptionAsDiagnostics,
bool reportSuppressedDiagnostics,
out ImmutableArray<Diagnostic> diagnostics)
public static TCompilation VerifySuppressedDiagnostics<TCompilation>(
this TCompilation c,
DiagnosticAnalyzer[] analyzers,
AnalyzerOptions options = null,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null,
bool logAnalyzerExceptionAsDiagnostics = true,
params DiagnosticDescription[] expected)
where TCompilation : Compilation
{
// Verify suppression is unaffected by toggling /warnaserror.
// Only perform this additional verification if the caller hasn't
// explicitly overridden specific or general diagnostic options.
if (c.Options.GeneralDiagnosticOption == ReportDiagnostic.Default &&
c.Options.SpecificDiagnosticOptions.IsEmpty)
{
_ = c.VerifySuppressedDiagnostics(toggleWarnAsError: true,
analyzers, options, onAnalyzerException, logAnalyzerExceptionAsDiagnostics,
expected);
}
return c.VerifySuppressedDiagnostics(toggleWarnAsError: false,
analyzers, options, onAnalyzerException, logAnalyzerExceptionAsDiagnostics,
expected);
}
private static TCompilation VerifySuppressedDiagnostics<TCompilation>(
this TCompilation c,
bool toggleWarnAsError,
DiagnosticAnalyzer[] analyzers,
AnalyzerOptions options,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
bool logAnalyzerExceptionAsDiagnostics,
DiagnosticDescription[] expectedDiagnostics)
where TCompilation : Compilation
{
if (toggleWarnAsError)
{
var toggledOption = c.Options.GeneralDiagnosticOption == ReportDiagnostic.Error ?
ReportDiagnostic.Default :
ReportDiagnostic.Error;
c = (TCompilation)c.WithOptions(c.Options.WithGeneralDiagnosticOption(toggledOption));
var builder = ArrayBuilder<DiagnosticDescription>.GetInstance(expectedDiagnostics.Length);
foreach (var expected in expectedDiagnostics)
{
// Toggle warnaserror and effective severity if following are true:
// 1. Default severity is not specified or specified as Warning
// 2. Effective severity is not specified or specified as Warning or Error
var defaultSeverityCheck = !expected.DefaultSeverity.HasValue ||
expected.DefaultSeverity.Value == DiagnosticSeverity.Warning;
var effectiveSeverityCheck = !expected.EffectiveSeverity.HasValue ||
expected.EffectiveSeverity.Value == DiagnosticSeverity.Warning ||
expected.EffectiveSeverity.Value == DiagnosticSeverity.Error;
DiagnosticDescription newExpected;
if (defaultSeverityCheck && effectiveSeverityCheck)
{
newExpected = expected.WithWarningAsError(!expected.IsWarningAsError);
if (expected.EffectiveSeverity.HasValue)
{
var newEffectiveSeverity = expected.EffectiveSeverity.Value == DiagnosticSeverity.Error ?
DiagnosticSeverity.Warning :
DiagnosticSeverity.Error;
newExpected = newExpected.WithEffectiveSeverity(newEffectiveSeverity);
}
}
else
{
newExpected = expected;
}
builder.Add(newExpected);
}
expectedDiagnostics = builder.ToArrayAndFree();
}
c = c.GetCompilationWithAnalyzerDiagnostics(analyzers, options, onAnalyzerException,
logAnalyzerExceptionAsDiagnostics, reportSuppressedDiagnostics: true,
includeCompilerDiagnostics: true, diagnostics: out var diagnostics);
diagnostics = diagnostics.WhereAsArray(d => d.IsSuppressed);
diagnostics.Verify(expectedDiagnostics);
return c; // note this is a new compilation
}
private static TCompilation GetCompilationWithAnalyzerDiagnostics<TCompilation>(
this TCompilation c,
DiagnosticAnalyzer[] analyzers,
AnalyzerOptions options,
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
bool logAnalyzerExceptionAsDiagnostics,
bool reportSuppressedDiagnostics,
bool includeCompilerDiagnostics,
out ImmutableArray<Diagnostic> diagnostics)
where TCompilation : Compilation
{
var analyzersArray = analyzers.ToImmutableArray();
......@@ -228,8 +323,13 @@ public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c,
var analyzerManager = new AnalyzerManager(analyzersArray);
var driver = AnalyzerDriver.CreateAndAttachToCompilation(c, analyzersArray, options, analyzerManager, onAnalyzerException, null, false, out var newCompilation, CancellationToken.None);
var discarded = newCompilation.GetDiagnostics();
diagnostics = driver.GetDiagnosticsAsync(newCompilation).Result.AddRange(exceptionDiagnostics);
var compilerDiagnostics = newCompilation.GetDiagnostics();
var analyzerDiagnostics = driver.GetDiagnosticsAsync(newCompilation).Result;
var allDiagnostics = includeCompilerDiagnostics ?
compilerDiagnostics.AddRange(analyzerDiagnostics) :
analyzerDiagnostics;
diagnostics = driver.ApplyProgrammaticSuppressions(allDiagnostics, newCompilation).AddRange(exceptionDiagnostics);
if (!reportSuppressedDiagnostics)
{
......
......@@ -427,6 +427,8 @@ private static void WriteTelemetry(string analyzerName, AnalyzerTelemetryInfo te
WriteLine($"Symbol End Actions: {telemetry.SymbolEndActionsCount}", ConsoleColor.White);
WriteLine($"Syntax Node Actions: {telemetry.SyntaxNodeActionsCount}", ConsoleColor.White);
WriteLine($"Syntax Tree Actions: {telemetry.SyntaxTreeActionsCount}", ConsoleColor.White);
WriteLine($"Suppression Actions: {telemetry.SuppressionActionsCount}", ConsoleColor.White);
}
private static void WriteExecutionTimes(string analyzerName, int longestAnalyzerName, AnalyzerTelemetryInfo telemetry)
......
......@@ -24,6 +24,7 @@ internal static void Add(this AnalyzerTelemetryInfo analyzerTelemetryInfo, Analy
analyzerTelemetryInfo.SymbolEndActionsCount += addendum.SymbolEndActionsCount;
analyzerTelemetryInfo.SyntaxNodeActionsCount += addendum.SyntaxNodeActionsCount;
analyzerTelemetryInfo.SyntaxTreeActionsCount += addendum.SyntaxTreeActionsCount;
analyzerTelemetryInfo.SuppressionActionsCount += addendum.SuppressionActionsCount;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册