-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44364 from dotnet/merges/release/dev16.7-preview2…
…-to-release/dev16.7-preview2-vs-deps Merge release/dev16.7-preview2 to release/dev16.7-preview2-vs-deps
- Loading branch information
Showing
67 changed files
with
2,054 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
...rs/RemoveUnnecessarySuppressions/CSharpRemoveUnnecessarySuppressionsDiagnosticAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions; | ||
|
||
#nullable enable | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
internal sealed class CSharpRemoveUnnecessarySuppressionsDiagnosticAnalyzer | ||
: AbstractRemoveUnnecessarySuppressionsDiagnosticAnalyzer | ||
{ | ||
protected override void RegisterAttributeSyntaxAction(CompilationStartAnalysisContext context, CompilationAnalyzer compilationAnalyzer) | ||
{ | ||
context.RegisterSyntaxNodeAction(context => | ||
{ | ||
var attributeList = (AttributeListSyntax)context.Node; | ||
switch (attributeList.Target?.Identifier.Kind()) | ||
{ | ||
case SyntaxKind.AssemblyKeyword: | ||
case SyntaxKind.ModuleKeyword: | ||
foreach (var attribute in attributeList.Attributes) | ||
{ | ||
compilationAnalyzer.AnalyzeAssemblyOrModuleAttribute(attribute, context.SemanticModel, context.ReportDiagnostic, context.CancellationToken); | ||
} | ||
|
||
break; | ||
} | ||
}, SyntaxKind.AttributeList); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
...nalyzers/CSharp/Tests/RemoveUnnecessarySuppressions/RemoveUnnecessarySuppressionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
#nullable enable | ||
|
||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.Test.Utilities; | ||
using Roslyn.Test.Utilities; | ||
using Xunit; | ||
using VerifyCS = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.CSharpCodeFixVerifier< | ||
Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions.CSharpRemoveUnnecessarySuppressionsDiagnosticAnalyzer, | ||
Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions.RemoveUnnecessarySuppressionsCodeFixProvider>; | ||
|
||
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessarySuppressions | ||
{ | ||
[Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)] | ||
[WorkItem(44176, "https://github.com/dotnet/roslyn/issues/44176")] | ||
public class RemoveUnnecessarySuppressionsTests | ||
{ | ||
[Fact] | ||
public void TestStandardProperties() | ||
=> VerifyCS.VerifyStandardProperties(); | ||
|
||
[Theory] | ||
// Field | ||
[InlineData(@"Scope = ""member""", @"Target = ""~F:N.C.F""", "assembly")] | ||
// Property | ||
[InlineData(@"Scope = ""member""", @"Target = ""~P:N.C.P""", "assembly")] | ||
// Method | ||
[InlineData(@"Scope = ""member""", @"Target = ""~M:N.C.M()""", "assembly")] | ||
// Type | ||
[InlineData(@"Scope = ""member""", @"Target = ""~T:N.C""", "assembly")] | ||
// Namespace | ||
[InlineData(@"Scope = ""namespace""", @"Target = ""~N:N""", "assembly")] | ||
// NamespaceAndDescendants | ||
[InlineData(@"Scope = ""namespaceanddescendants""", @"Target = ""~N:N""", "assembly")] | ||
// Module - no scope, no target | ||
[InlineData(null, null, "assembly")] | ||
// Module - no target | ||
[InlineData(@"Scope = ""module""", null, "assembly")] | ||
// Module - null target | ||
[InlineData(@"Scope = ""module""", @"Target = null", "assembly")] | ||
// Resource - not handled | ||
[InlineData(@"Scope = ""resource""", @"Target = """"", "assembly")] | ||
// 'module' attribute target | ||
[InlineData(@"Scope = ""member""", @"Target = ""~M:N.C.M()""", "module")] | ||
// Member with non-matching scope (seems to be respected by suppression decoder) | ||
[InlineData(@"Scope = ""type""", @"Target = ""~M:N.C.M()""", "assembly")] | ||
[InlineData(@"Scope = ""namespace""", @"Target = ""~F:N.C.F""", "assembly")] | ||
// Case insensitive scope | ||
[InlineData(@"Scope = ""Member""", @"Target = ""~F:N.C.F""", "assembly")] | ||
[InlineData(@"Scope = ""MEMBER""", @"Target = ""~F:N.C.F""", "assembly")] | ||
public async Task ValidSuppressions(string? scope, string? target, string attributeTarget) | ||
{ | ||
var scopeString = scope != null ? $@", {scope}" : string.Empty; | ||
var targetString = target != null ? $@", {target}" : string.Empty; | ||
|
||
var input = $@" | ||
[{attributeTarget}: System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""Id: Title"", Justification = ""Pending""{scopeString}{targetString})] | ||
namespace N | ||
{{ | ||
class C | ||
{{ | ||
public int F; | ||
public int P {{ get; }} | ||
public void M() {{ }} | ||
}} | ||
}}"; | ||
await VerifyCS.VerifyCodeFixAsync(input, input); | ||
} | ||
|
||
[Theory] | ||
// Field - no matching symbol | ||
[InlineData(@"Scope = ""member""", @"Target = ""~F:N.C.F2""", "assembly")] | ||
// Field - no matching symbol (case insensitive) | ||
[InlineData(@"Scope = ""Member""", @"Target = ""~F:N.C.F2""", "assembly")] | ||
[InlineData(@"Scope = ""MEMBER""", @"Target = ""~F:N.C.F2""", "assembly")] | ||
// Property - invalid scope | ||
[InlineData(@"Scope = ""invalid""", @"Target = ""~P:N.C.P""", "assembly")] | ||
// Method - wrong signature | ||
[InlineData(@"Scope = ""member""", @"Target = ""~M:N.C.M(System.Int32)""", "assembly")] | ||
// Method - module scope | ||
[InlineData(@"Scope = ""module""", @"Target = ""~M:N.C.M()""", "assembly")] | ||
// Method - null scope | ||
[InlineData(@"Scope = null", @"Target = ""~M:N.C.M()""", "assembly")] | ||
// Method - no scope | ||
[InlineData(null, @"Target = ""~M:N.C.M()""", "assembly")] | ||
// Member scope - null target | ||
[InlineData(@"Scope = ""member""", @"Target = null", "assembly")] | ||
// Member scope - no target | ||
[InlineData(@"Scope = ""member""", null, "assembly")] | ||
// Type - no matching namespace | ||
[InlineData(@"Scope = ""type""", @"Target = ""~T:N2.C""", "assembly")] | ||
// Namespace - extra namespace qualification | ||
[InlineData(@"Scope = ""namespace""", @"Target = ""~N:N.N2""", "assembly")] | ||
// NamespaceAndDescendants - empty target | ||
[InlineData(@"Scope = ""namespaceanddescendants""", @"Target = """"", "assembly")] | ||
// Module - no scope, empty target | ||
[InlineData(null, @"Target = """"", "assembly")] | ||
// Module - no scope, non-empty target | ||
[InlineData(null, @"Target = ""~T:N.C""", "assembly")] | ||
// Module scope, empty target | ||
[InlineData(@"Scope = ""module""", @"Target = """"", "assembly")] | ||
// Module no scope, non-empty target | ||
[InlineData(@"Scope = ""module""", @"Target = ""~T:N.C""", "assembly")] | ||
public async Task InvalidSuppressions(string? scope, string? target, string attributeTarget) | ||
{ | ||
var scopeString = scope != null ? $@", {scope}" : string.Empty; | ||
var targetString = target != null ? $@", {target}" : string.Empty; | ||
|
||
var input = $@" | ||
[{attributeTarget}: [|System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""Id: Title"", Justification = ""Pending""{scopeString}{targetString})|]] | ||
namespace N | ||
{{ | ||
class C | ||
{{ | ||
public int F; | ||
public int P {{ get; }} | ||
public void M() {{ }} | ||
}} | ||
}}"; | ||
|
||
var fixedCode = $@" | ||
namespace N | ||
{{ | ||
class C | ||
{{ | ||
public int F; | ||
public int P {{ get; }} | ||
public void M() {{ }} | ||
}} | ||
}}"; | ||
await VerifyCS.VerifyCodeFixAsync(input, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task ValidAndInvalidSuppressions() | ||
{ | ||
var attributePrefix = @"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""Id: Title"", Justification = ""Pending"""; | ||
var validSuppression = $@"{attributePrefix}, Scope = ""member"", Target = ""~T:C"")"; | ||
var invalidSuppression = $@"[|{attributePrefix}, Scope = ""member"", Target = """")|]"; | ||
|
||
var input = $@" | ||
[assembly: {validSuppression}] | ||
[assembly: {invalidSuppression}] | ||
[assembly: {validSuppression}, {validSuppression}] | ||
[assembly: {invalidSuppression}, {invalidSuppression}] | ||
[assembly: {validSuppression}, {invalidSuppression}] | ||
[assembly: {invalidSuppression}, {validSuppression}] | ||
[assembly: {invalidSuppression}, {validSuppression}, {invalidSuppression}, {validSuppression}] | ||
class C {{ }} | ||
"; | ||
|
||
var fixedCode = $@" | ||
[assembly: {validSuppression}] | ||
[assembly: {validSuppression}, {validSuppression}] | ||
[assembly: {validSuppression}] | ||
[assembly: {validSuppression}] | ||
[assembly: {validSuppression}, {validSuppression}] | ||
class C {{ }} | ||
"; | ||
await VerifyCS.VerifyCodeFixAsync(input, fixedCode); | ||
} | ||
|
||
[Theory] | ||
[InlineData("")] | ||
[InlineData(@", Scope = ""member"", Target = ""~M:C.M()""")] | ||
[InlineData(@", Scope = ""invalid"", Target = ""invalid""")] | ||
public async Task LocalSuppressions(string scopeAndTarget) | ||
{ | ||
var input = $@" | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""Id: Title"", Justification = ""Pending""{scopeAndTarget})] | ||
class C | ||
{{ | ||
public void M() {{ }} | ||
}}"; | ||
await VerifyCS.VerifyCodeFixAsync(input, input); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
.../RemoveUnnecessarySuppressions/AbstractRemoveUnnecessarySuppressionsDiagnosticAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis.CodeQuality; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||
|
||
namespace Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions | ||
{ | ||
internal abstract class AbstractRemoveUnnecessarySuppressionsDiagnosticAnalyzer | ||
: AbstractCodeQualityDiagnosticAnalyzer | ||
{ | ||
private static readonly LocalizableResourceString s_localizableTitle = new LocalizableResourceString( | ||
nameof(AnalyzersResources.Invalid_global_SuppressMessageAttribute), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)); | ||
private static readonly LocalizableResourceString s_localizableInvalidScopeMessage = new LocalizableResourceString( | ||
nameof(AnalyzersResources.Invalid_scope_for_SuppressMessageAttribute), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)); | ||
private static readonly LocalizableResourceString s_localizableInvalidOrMissingTargetMessage = new LocalizableResourceString( | ||
nameof(AnalyzersResources.Invalid_or_missing_target_for_SuppressMessageAttribute), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)); | ||
|
||
private static readonly DiagnosticDescriptor s_invalidScopeDescriptor = CreateDescriptor( | ||
IDEDiagnosticIds.InvalidSuppressMessageAttributeDiagnosticId, s_localizableTitle, s_localizableInvalidScopeMessage, isUnnecessary: true); | ||
private static readonly DiagnosticDescriptor s_invalidOrMissingTargetDescriptor = CreateDescriptor( | ||
IDEDiagnosticIds.InvalidSuppressMessageAttributeDiagnosticId, s_localizableTitle, s_localizableInvalidOrMissingTargetMessage, isUnnecessary: true); | ||
|
||
public AbstractRemoveUnnecessarySuppressionsDiagnosticAnalyzer() | ||
: base(ImmutableArray.Create(s_invalidScopeDescriptor, s_invalidOrMissingTargetDescriptor), GeneratedCodeAnalysisFlags.None) | ||
{ | ||
} | ||
|
||
protected abstract void RegisterAttributeSyntaxAction(CompilationStartAnalysisContext context, CompilationAnalyzer compilationAnalyzer); | ||
public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; | ||
|
||
protected sealed override void InitializeWorker(AnalysisContext context) | ||
{ | ||
context.RegisterCompilationStartAction(context => | ||
{ | ||
var suppressMessageAttributeType = context.Compilation.SuppressMessageAttributeType(); | ||
if (suppressMessageAttributeType == null) | ||
{ | ||
return; | ||
} | ||
|
||
RegisterAttributeSyntaxAction(context, new CompilationAnalyzer(context.Compilation, suppressMessageAttributeType)); | ||
}); | ||
} | ||
|
||
protected sealed class CompilationAnalyzer | ||
{ | ||
private readonly SuppressMessageAttributeState _state; | ||
|
||
public CompilationAnalyzer(Compilation compilation, INamedTypeSymbol suppressMessageAttributeType) | ||
{ | ||
_state = new SuppressMessageAttributeState(compilation, suppressMessageAttributeType); | ||
} | ||
|
||
public void AnalyzeAssemblyOrModuleAttribute(SyntaxNode attributeSyntax, SemanticModel model, Action<Diagnostic> reportDiagnostic, CancellationToken cancellationToken) | ||
{ | ||
if (!_state.IsSuppressMessageAttributeWithNamedArguments(attributeSyntax, model, cancellationToken, out var namedAttributeArguments)) | ||
{ | ||
return; | ||
} | ||
|
||
DiagnosticDescriptor rule; | ||
if (_state.HasInvalidScope(namedAttributeArguments, out var targetScope)) | ||
{ | ||
rule = s_invalidScopeDescriptor; | ||
} | ||
else if (_state.HasInvalidOrMissingTarget(namedAttributeArguments, targetScope)) | ||
{ | ||
rule = s_invalidOrMissingTargetDescriptor; | ||
} | ||
else | ||
{ | ||
return; | ||
} | ||
|
||
reportDiagnostic(Diagnostic.Create(rule, attributeSyntax.GetLocation())); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.