提交 73fd51e1 编写于 作者: M Manish Vasani

Add support to detect unnecessary local SuppressMessageAttribute suppressions

Fixes #44178

Builds on top of #44848, which added support to detect unnecessary pragma suppressions.
上级 1d4a9ce3
......@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
: AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
internal sealed class CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
: AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
{
protected override string CompilerErrorCodePrefix => "CS";
protected override int CompilerErrorCodeDigitCount => 4;
protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance;
protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance;
protected override (Assembly assembly, string typeName) GetCompilerDiagnosticAnalyzerInfo()
=> (typeof(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.CSharpCompilerAnalyzerTypeName);
}
......
......@@ -15,17 +15,18 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryPragmaSuppressions), Shared]
internal sealed class RemoveUnnecessaryPragmaSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
internal sealed class RemoveUnnecessaryInlineSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public RemoveUnnecessaryPragmaSuppressionsCodeFixProvider()
public RemoveUnnecessaryInlineSuppressionsCodeFixProvider()
{
}
......@@ -38,10 +39,12 @@ internal override CodeFixCategory CodeFixCategory
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxFacts = context.Document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in context.Diagnostics)
{
// Defensive check that we are operating on the diagnostic on a pragma.
if (root.FindTrivia(diagnostic.Location.SourceSpan.Start).HasStructure)
if (root.FindNode(diagnostic.Location.SourceSpan) is { } node && syntaxFacts.IsAttribute(node) ||
root.FindTrivia(diagnostic.Location.SourceSpan.Start).HasStructure)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, diagnostic, c)),
......@@ -58,22 +61,36 @@ protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic
// Our code fix ensures that we remove both the disable and restore directives with a single code fix application.
// So, we need to ensure that we do not attempt to remove the same node multiple times when performing a FixAll in document operation.
using var _ = PooledHashSet<SyntaxNode>.GetInstance(out var processedNodes);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in diagnostics)
{
RemoveNode(diagnostic.Location, editor, processedNodes);
RemoveNode(diagnostic.Location, editor, processedNodes, syntaxFacts);
foreach (var location in diagnostic.AdditionalLocations)
{
RemoveNode(location, editor, processedNodes);
RemoveNode(location, editor, processedNodes, syntaxFacts);
}
}
return Task.CompletedTask;
static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNode> processedNodes)
static void RemoveNode(
Location location,
SyntaxEditor editor,
HashSet<SyntaxNode> processedNodes,
ISyntaxFacts syntaxFacts)
{
var node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!;
SyntaxNode node;
if (editor.OriginalRoot.FindNode(location.SourceSpan) is { } attribute &&
syntaxFacts.IsAttribute(attribute))
{
node = attribute;
}
else
{
node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!;
}
if (processedNodes.Add(node))
{
editor.RemoveNode(node);
......@@ -84,7 +101,7 @@ static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNod
private class MyCodeAction : CustomCodeActions.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(AnalyzersResources.Remove_unnecessary_suppression, createChangedDocument, nameof(RemoveUnnecessaryPragmaSuppressionsCodeFixProvider))
: base(AnalyzersResources.Remove_unnecessary_suppression, createChangedDocument, nameof(RemoveUnnecessaryInlineSuppressionsCodeFixProvider))
{
}
}
......
......@@ -11,8 +11,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Inherits AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Friend NotInheritable Class VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Inherits AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Protected Overrides ReadOnly Property CompilerErrorCodePrefix As String = "BC"
......@@ -20,6 +20,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance
Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance
Protected Overrides Function GetCompilerDiagnosticAnalyzerInfo() As (assembly As Assembly, typeName As String)
Return (GetType(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.VisualBasicCompilerAnalyzerTypeName)
End Function
......
......@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
public abstract class AbstractUnncessarySuppressionDiagnosticTest : AbstractUserDiagnosticTest
{
internal abstract CodeFixProvider CodeFixProvider { get; }
internal abstract AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; }
internal abstract AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; }
internal abstract ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers { get; }
private void AddAnalyzersToWorkspace(TestWorkspace workspace)
......
......@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
<Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)>
<WorkItem(44177, "https://github.com/dotnet/roslyn/issues/44177")>
Public NotInheritable Class RemoveUnnecessaryPragmaSuppressionsTests
Public NotInheritable Class RemoveUnnecessaryInlineSuppressionsTests
Inherits AbstractUnncessarySuppressionDiagnosticTest
Protected Overrides Function GetScriptOptions() As ParseOptions
......@@ -37,13 +37,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
Friend Overrides ReadOnly Property CodeFixProvider As CodeFixProvider
Get
Return New RemoveUnnecessaryPragmaSuppressionsCodeFixProvider()
Return New RemoveUnnecessaryInlineSuppressionsCodeFixProvider()
End Get
End Property
Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Get
Return New VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer()
Return New VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer()
End Get
End Property
......@@ -123,6 +123,68 @@ Class C
Dim x As Integer
x = 1
End Sub
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Method() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Sub Method()
Dim x As Integer
x = 1
End Sub
End Class", $"
Imports System
Class C
Sub Method()
Dim x As Integer
x = 1
End Sub
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Field() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Dim f As Integer
End Class", $"
Imports System
Class C
Dim f As Integer
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Property() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Public ReadOnly Property P As Integer
End Class", $"
Imports System
Class C
Public ReadOnly Property P As Integer
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Event() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Public Event SampleEvent As EventHandler
End Class", $"
Imports System
Class C
Public Event SampleEvent As EventHandler
End Class")
End Function
End Class
......
......@@ -210,10 +210,10 @@ private async Task<ImmutableArray<Diagnostic>> GetSemanticDiagnosticsAsync(Seman
return await _compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, adjustedSpan, ImmutableArray.Create(analyzer), cancellationToken).ConfigureAwait(false);
}
// We specially handle IPragmaSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers'
// context to compute unnecessary pragma suppression diagnostics.
// We specially handle IInlineSourceSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers'
// context to compute unnecessary inline source suppression diagnostics.
// This is required because this analyzer relies on reported compiler + analyzer diagnostics
// for unnecessary pragma analysis.
// for unnecessary inline source suppression analysis.
if (analyzer is IPragmaSuppressionsAnalyzer suppressionsAnalyzer &&
!AnalysisScope.Span.HasValue)
{
......
......@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Special IDE analyzer to flag unnecessary pragma suppressions.
/// Special IDE analyzer to flag unnecessary inline source suppressions,
/// i.e. pragma and local SuppressMessageAttribute suppressions.
/// </summary>
internal interface IPragmaSuppressionsAnalyzer
{
/// <summary>
/// Analyzes the tree, with an optional span scope, and report unnecessary pragma suppressions.
/// Analyzes the tree, with an optional span scope, and report unnecessary inline suppressions.
/// </summary>
Task AnalyzeAsync(
SemanticModel semanticModel,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册