-
-
Notifications
You must be signed in to change notification settings - Fork 256
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add analyzer "Unnecessary enum flag" (RCS1258) (#886)
- Loading branch information
1 parent
4fc79a0
commit f057b97
Showing
8 changed files
with
267 additions
and
0 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
58 changes: 58 additions & 0 deletions
58
src/Analyzers.CodeFixes/CSharp/CodeFixes/UnnecessaryEnumFlagCodeFixProvider.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,58 @@ | ||
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Roslynator.CodeFixes; | ||
|
||
namespace Roslynator.CSharp.CodeFixes; | ||
|
||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UnnecessaryEnumFlagCodeFixProvider))] | ||
[Shared] | ||
public sealed class UnnecessaryEnumFlagCodeFixProvider : BaseCodeFixProvider | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds | ||
{ | ||
get { return ImmutableArray.Create(DiagnosticIdentifiers.UnnecessaryEnumFlag); } | ||
} | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); | ||
|
||
if (!TryFindFirstAncestorOrSelf(root, context.Span, out MemberAccessExpressionSyntax memberAccessExpression)) | ||
return; | ||
|
||
Document document = context.Document; | ||
Diagnostic diagnostic = context.Diagnostics[0]; | ||
|
||
switch (diagnostic.Id) | ||
{ | ||
case DiagnosticIdentifiers.UnnecessaryEnumFlag: | ||
{ | ||
CodeAction codeAction = CodeAction.Create( | ||
"Remove unnecessary flag", | ||
ct => | ||
{ | ||
var bitwiseOr = (BinaryExpressionSyntax)memberAccessExpression.Parent; | ||
ExpressionSyntax newExpression = (bitwiseOr.Left == memberAccessExpression) | ||
? bitwiseOr.Right | ||
: bitwiseOr.Left; | ||
newExpression = newExpression.WithTriviaFrom(bitwiseOr); | ||
return document.ReplaceNodeAsync(bitwiseOr, newExpression, ct); | ||
}, | ||
GetEquivalenceKey(diagnostic)); | ||
|
||
context.RegisterCodeFix(codeAction, diagnostic); | ||
break; | ||
} | ||
} | ||
} | ||
} |
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
95 changes: 95 additions & 0 deletions
95
src/Analyzers/CSharp/Analysis/UnnecessaryEnumFlagAnalyzer.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,95 @@ | ||
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Roslynator.CSharp.Analysis; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class UnnecessaryEnumFlagAnalyzer : BaseDiagnosticAnalyzer | ||
{ | ||
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics; | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics | ||
{ | ||
get | ||
{ | ||
if (_supportedDiagnostics.IsDefault) | ||
Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.UnnecessaryEnumFlag); | ||
|
||
return _supportedDiagnostics; | ||
} | ||
} | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
base.Initialize(context); | ||
|
||
context.RegisterSyntaxNodeAction(c => AnalyzeBitwiseOrExpression(c), SyntaxKind.BitwiseOrExpression); | ||
} | ||
|
||
private static void AnalyzeBitwiseOrExpression(SyntaxNodeAnalysisContext context) | ||
{ | ||
var bitwiseAnd = (BinaryExpressionSyntax)context.Node; | ||
|
||
foreach (ExpressionSyntax expression in bitwiseAnd.AsChain()) | ||
{ | ||
if (!expression.IsKind(SyntaxKind.SimpleMemberAccessExpression)) | ||
return; | ||
} | ||
|
||
ITypeSymbol symbol = context.SemanticModel.GetTypeSymbol(bitwiseAnd, context.CancellationToken); | ||
|
||
if (!symbol.HasAttribute(MetadataNames.System_FlagsAttribute)) | ||
return; | ||
|
||
var enumSymbol = (INamedTypeSymbol)symbol; | ||
var values = new List<(ExpressionSyntax, ulong)>(); | ||
|
||
foreach (ExpressionSyntax expression in bitwiseAnd.AsChain()) | ||
{ | ||
Optional<object> constantValueOpt = context.SemanticModel.GetConstantValue(expression, context.CancellationToken); | ||
|
||
if (constantValueOpt.HasValue) | ||
{ | ||
ulong value = SymbolUtility.GetEnumValueAsUInt64(constantValueOpt.Value, enumSymbol); | ||
var addToValues = true; | ||
|
||
for (int i = values.Count - 1; i >= 0; i--) | ||
{ | ||
(ExpressionSyntax expression2, ulong value2) = values[i]; | ||
|
||
if ((value & value2) != 0) | ||
{ | ||
if (value <= value2) | ||
{ | ||
ReportDiagnostic(expression, expression2); | ||
addToValues = false; | ||
} | ||
else | ||
{ | ||
ReportDiagnostic(expression2, expression); | ||
values.RemoveAt(i); | ||
} | ||
} | ||
} | ||
|
||
if (addToValues) | ||
values.Add((expression, value)); | ||
} | ||
} | ||
|
||
void ReportDiagnostic(ExpressionSyntax expression, ExpressionSyntax expression2) | ||
{ | ||
context.ReportDiagnostic( | ||
DiagnosticRules.UnnecessaryEnumFlag, | ||
expression, | ||
context.SemanticModel.GetSymbol(expression, context.CancellationToken).Name, | ||
context.SemanticModel.GetSymbol(expression2, context.CancellationToken).Name); | ||
} | ||
} | ||
} |
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
82 changes: 82 additions & 0 deletions
82
src/Tests/Analyzers.Tests/RCS1258UnnecessaryEnumFlagTests.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,82 @@ | ||
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Roslynator.CSharp.CodeFixes; | ||
using Roslynator.Testing.CSharp; | ||
using Xunit; | ||
|
||
namespace Roslynator.CSharp.Analysis.Tests; | ||
|
||
public class RCS1258UnnecessaryEnumFlagTests : AbstractCSharpDiagnosticVerifier<UnnecessaryEnumFlagAnalyzer, UnnecessaryEnumFlagCodeFixProvider> | ||
{ | ||
public override DiagnosticDescriptor Descriptor { get; } = DiagnosticRules.UnnecessaryEnumFlag; | ||
|
||
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryEnumFlag)] | ||
public async Task Test() | ||
{ | ||
await VerifyDiagnosticAndFixAsync(@" | ||
using System.Globalization; | ||
class C | ||
{ | ||
void M() | ||
{ | ||
var styles = NumberStyles.Integer | [|NumberStyles.AllowLeadingWhite|]; | ||
} | ||
} | ||
", @" | ||
using System.Globalization; | ||
class C | ||
{ | ||
void M() | ||
{ | ||
var styles = NumberStyles.Integer; | ||
} | ||
} | ||
"); | ||
} | ||
|
||
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryEnumFlag)] | ||
public async Task Test2() | ||
{ | ||
await VerifyDiagnosticAndFixAsync(@" | ||
using System.Globalization; | ||
class C | ||
{ | ||
void M() | ||
{ | ||
var styles = [|NumberStyles.AllowLeadingWhite|] | [|NumberStyles.AllowTrailingWhite|] | NumberStyles.Integer; | ||
} | ||
} | ||
", @" | ||
using System.Globalization; | ||
class C | ||
{ | ||
void M() | ||
{ | ||
var styles = NumberStyles.Integer; | ||
} | ||
} | ||
"); | ||
} | ||
|
||
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryEnumFlag)] | ||
public async Task TestNoDiagnostic() | ||
{ | ||
await VerifyNoDiagnosticAsync(@" | ||
using System.Globalization; | ||
class C | ||
{ | ||
void M() | ||
{ | ||
var styles = NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite | NumberStyles.AllowParentheses | NumberStyles.AllowThousands | NumberStyles.AllowTrailingSign | NumberStyles.AllowTrailingWhite; | ||
} | ||
} | ||
"); | ||
} | ||
} |
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