提交 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 @@ ...@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions
{ {
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer internal sealed class CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
: AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer : AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
{ {
protected override string CompilerErrorCodePrefix => "CS"; protected override string CompilerErrorCodePrefix => "CS";
protected override int CompilerErrorCodeDigitCount => 4; protected override int CompilerErrorCodeDigitCount => 4;
protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance;
protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance;
protected override (Assembly assembly, string typeName) GetCompilerDiagnosticAnalyzerInfo() protected override (Assembly assembly, string typeName) GetCompilerDiagnosticAnalyzerInfo()
=> (typeof(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.CSharpCompilerAnalyzerTypeName); => (typeof(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.CSharpCompilerAnalyzerTypeName);
} }
......
...@@ -15,17 +15,18 @@ ...@@ -15,17 +15,18 @@
using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions namespace Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions
{ {
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryPragmaSuppressions), Shared] [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryPragmaSuppressions), Shared]
internal sealed class RemoveUnnecessaryPragmaSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider internal sealed class RemoveUnnecessaryInlineSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{ {
[ImportingConstructor] [ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] [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 ...@@ -38,10 +39,12 @@ internal override CodeFixCategory CodeFixCategory
public override async Task RegisterCodeFixesAsync(CodeFixContext context) public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{ {
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxFacts = context.Document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in context.Diagnostics) foreach (var diagnostic in context.Diagnostics)
{ {
// Defensive check that we are operating on the diagnostic on a pragma. // 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( context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, diagnostic, c)), new MyCodeAction(c => FixAsync(context.Document, diagnostic, c)),
...@@ -58,22 +61,36 @@ protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic ...@@ -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. // 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. // 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); using var _ = PooledHashSet<SyntaxNode>.GetInstance(out var processedNodes);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in diagnostics) foreach (var diagnostic in diagnostics)
{ {
RemoveNode(diagnostic.Location, editor, processedNodes); RemoveNode(diagnostic.Location, editor, processedNodes, syntaxFacts);
foreach (var location in diagnostic.AdditionalLocations) foreach (var location in diagnostic.AdditionalLocations)
{ {
RemoveNode(location, editor, processedNodes); RemoveNode(location, editor, processedNodes, syntaxFacts);
} }
} }
return Task.CompletedTask; 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)
{
SyntaxNode node;
if (editor.OriginalRoot.FindNode(location.SourceSpan) is { } attribute &&
syntaxFacts.IsAttribute(attribute))
{
node = attribute;
}
else
{ {
var node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!; node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!;
}
if (processedNodes.Add(node)) if (processedNodes.Add(node))
{ {
editor.RemoveNode(node); editor.RemoveNode(node);
...@@ -84,7 +101,7 @@ static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNod ...@@ -84,7 +101,7 @@ static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNod
private class MyCodeAction : CustomCodeActions.DocumentChangeAction private class MyCodeAction : CustomCodeActions.DocumentChangeAction
{ {
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) 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 ...@@ -11,8 +11,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
<DiagnosticAnalyzer(LanguageNames.VisualBasic)> <DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer Friend NotInheritable Class VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Inherits AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer Inherits AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Protected Overrides ReadOnly Property CompilerErrorCodePrefix As String = "BC" Protected Overrides ReadOnly Property CompilerErrorCodePrefix As String = "BC"
...@@ -20,6 +20,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions ...@@ -20,6 +20,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance 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) Protected Overrides Function GetCompilerDiagnosticAnalyzerInfo() As (assembly As Assembly, typeName As String)
Return (GetType(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.VisualBasicCompilerAnalyzerTypeName) Return (GetType(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.VisualBasicCompilerAnalyzerTypeName)
End Function End Function
......
...@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics ...@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
public abstract class AbstractUnncessarySuppressionDiagnosticTest : AbstractUserDiagnosticTest public abstract class AbstractUnncessarySuppressionDiagnosticTest : AbstractUserDiagnosticTest
{ {
internal abstract CodeFixProvider CodeFixProvider { get; } internal abstract CodeFixProvider CodeFixProvider { get; }
internal abstract AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; } internal abstract AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; }
internal abstract ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers { get; } internal abstract ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers { get; }
private void AddAnalyzersToWorkspace(TestWorkspace workspace) private void AddAnalyzersToWorkspace(TestWorkspace workspace)
......
...@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove ...@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
<Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)> <Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)>
<WorkItem(44177, "https://github.com/dotnet/roslyn/issues/44177")> <WorkItem(44177, "https://github.com/dotnet/roslyn/issues/44177")>
Public NotInheritable Class RemoveUnnecessaryPragmaSuppressionsTests Public NotInheritable Class RemoveUnnecessaryInlineSuppressionsTests
Inherits AbstractUnncessarySuppressionDiagnosticTest Inherits AbstractUnncessarySuppressionDiagnosticTest
Protected Overrides Function GetScriptOptions() As ParseOptions Protected Overrides Function GetScriptOptions() As ParseOptions
...@@ -37,13 +37,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove ...@@ -37,13 +37,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
Friend Overrides ReadOnly Property CodeFixProvider As CodeFixProvider Friend Overrides ReadOnly Property CodeFixProvider As CodeFixProvider
Get Get
Return New RemoveUnnecessaryPragmaSuppressionsCodeFixProvider() Return New RemoveUnnecessaryInlineSuppressionsCodeFixProvider()
End Get End Get
End Property End Property
Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Get Get
Return New VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer() Return New VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer()
End Get End Get
End Property End Property
...@@ -123,6 +123,68 @@ Class C ...@@ -123,6 +123,68 @@ Class C
Dim x As Integer Dim x As Integer
x = 1 x = 1
End Sub 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 Class")
End Function End Function
End Class End Class
......
...@@ -210,10 +210,10 @@ private async Task<ImmutableArray<Diagnostic>> GetSemanticDiagnosticsAsync(Seman ...@@ -210,10 +210,10 @@ private async Task<ImmutableArray<Diagnostic>> GetSemanticDiagnosticsAsync(Seman
return await _compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, adjustedSpan, ImmutableArray.Create(analyzer), cancellationToken).ConfigureAwait(false); return await _compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, adjustedSpan, ImmutableArray.Create(analyzer), cancellationToken).ConfigureAwait(false);
} }
// We specially handle IPragmaSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers' // We specially handle IInlineSourceSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers'
// context to compute unnecessary pragma suppression diagnostics. // context to compute unnecessary inline source suppression diagnostics.
// This is required because this analyzer relies on reported compiler + analyzer 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 && if (analyzer is IPragmaSuppressionsAnalyzer suppressionsAnalyzer &&
!AnalysisScope.Span.HasValue) !AnalysisScope.Span.HasValue)
{ {
......
...@@ -13,12 +13,13 @@ ...@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.Diagnostics namespace Microsoft.CodeAnalysis.Diagnostics
{ {
/// <summary> /// <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> /// </summary>
internal interface IPragmaSuppressionsAnalyzer internal interface IPragmaSuppressionsAnalyzer
{ {
/// <summary> /// <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> /// </summary>
Task AnalyzeAsync( Task AnalyzeAsync(
SemanticModel semanticModel, SemanticModel semanticModel,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册