-
Notifications
You must be signed in to change notification settings - Fork 468
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create DoNotCompareSpanToNullAnalyzer (#6838)
* Add DoNotCompareSpanToNullAnalyzer * Specify FixAllProvider. * Update analyzer ID. * Make fixer work for ArgumentSyntax as well. * Improve message and description * Also flag span comparisons to 'default'. * Update messaging and add more tests * Update RulesMissingDocumentation.md
- Loading branch information
1 parent
59b2d9d
commit 3e60082
Showing
24 changed files
with
853 additions
and
2 deletions.
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
...tAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpDoNotCompareSpanToNull.Fixer.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,56 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Composition; | ||
using System.Threading.Tasks; | ||
using Analyzer.Utilities; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.NetCore.Analyzers; | ||
using Microsoft.NetCore.Analyzers.Usage; | ||
|
||
namespace Microsoft.NetCore.CSharp.Analyzers.Usage | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp), Shared] | ||
public sealed class CSharpDoNotCompareSpanToNullFixer : DoNotCompareSpanToNullFixer | ||
{ | ||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
var condition = root.FindNode(context.Span, getInnermostNodeForTie: true); | ||
if (condition is not BinaryExpressionSyntax binaryExpression) | ||
{ | ||
return; | ||
} | ||
|
||
var useIsEmptyCodeAction = CodeAction.Create( | ||
MicrosoftNetCoreAnalyzersResources.DoNotCompareSpanToNullIsEmptyCodeFixTitle, | ||
_ => Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(binaryExpression, MakeIsEmptyCheck(binaryExpression)))), | ||
MicrosoftNetCoreAnalyzersResources.DoNotCompareSpanToNullIsEmptyCodeFixTitle | ||
); | ||
context.RegisterCodeFix(useIsEmptyCodeAction, context.Diagnostics); | ||
} | ||
|
||
private static SyntaxNode MakeIsEmptyCheck(BinaryExpressionSyntax binaryExpression) | ||
{ | ||
ExpressionSyntax memberAccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, GetComparatorExpression(binaryExpression), SyntaxFactory.IdentifierName(IsEmpty)); | ||
if (binaryExpression.IsKind(SyntaxKind.NotEqualsExpression)) | ||
{ | ||
return SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, memberAccess); | ||
} | ||
|
||
return memberAccess; | ||
} | ||
|
||
private static ExpressionSyntax GetComparatorExpression(BinaryExpressionSyntax binaryExpression) | ||
{ | ||
return binaryExpression.Left.IsKind(SyntaxKind.NullLiteralExpression) | ||
|| binaryExpression.Left.IsKind(SyntaxKind.DefaultLiteralExpression) | ||
|| binaryExpression.Left.IsKind(SyntaxKind.DefaultExpression) | ||
? binaryExpression.Right | ||
: binaryExpression.Left; | ||
} | ||
} | ||
} |
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
16 changes: 16 additions & 0 deletions
16
src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/DoNotCompareSpanToNull.Fixer.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,16 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Microsoft.NetCore.Analyzers.Usage | ||
{ | ||
public abstract class DoNotCompareSpanToNullFixer : CodeFixProvider | ||
{ | ||
protected const string IsEmpty = nameof(IsEmpty); | ||
|
||
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DoNotCompareSpanToNullAnalyzer.RuleId); | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/DoNotCompareSpanToNull.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,85 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using Analyzer.Utilities; | ||
using Analyzer.Utilities.Extensions; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace Microsoft.NetCore.Analyzers.Usage | ||
{ | ||
using static MicrosoftNetCoreAnalyzersResources; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] | ||
public sealed class DoNotCompareSpanToNullAnalyzer : DiagnosticAnalyzer | ||
{ | ||
internal const string RuleId = "CA2265"; | ||
|
||
internal static readonly DiagnosticDescriptor DoNotCompareSpanToNullRule = DiagnosticDescriptorHelper.Create( | ||
RuleId, | ||
CreateLocalizableResourceString(nameof(DoNotCompareSpanToNullOrDefaultTitle)), | ||
CreateLocalizableResourceString(nameof(DoNotCompareSpanToNullMessage)), | ||
DiagnosticCategory.Usage, | ||
RuleLevel.BuildWarning, | ||
description: CreateLocalizableResourceString(nameof(DoNotCompareSpanToNullOrDefaultDescription)), | ||
isPortedFxCopRule: false, | ||
isDataflowRule: false); | ||
|
||
internal static readonly DiagnosticDescriptor DoNotCompareSpanToDefaultRule = DiagnosticDescriptorHelper.Create( | ||
RuleId, | ||
CreateLocalizableResourceString(nameof(DoNotCompareSpanToNullOrDefaultTitle)), | ||
CreateLocalizableResourceString(nameof(DoNotCompareSpanToDefaultMessage)), | ||
DiagnosticCategory.Usage, | ||
RuleLevel.BuildWarning, | ||
description: CreateLocalizableResourceString(nameof(DoNotCompareSpanToNullOrDefaultDescription)), | ||
isPortedFxCopRule: false, | ||
isDataflowRule: false); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.EnableConcurrentExecution(); | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
context.RegisterCompilationStartAction(static context => | ||
{ | ||
var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); | ||
if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemSpan1, out var spanType) | ||
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1, out var readOnlySpanType)) | ||
{ | ||
return; | ||
} | ||
context.RegisterOperationAction(ctx => AnalyzeComparison(ctx, spanType, readOnlySpanType), OperationKind.Binary); | ||
}); | ||
|
||
} | ||
|
||
private static void AnalyzeComparison(OperationAnalysisContext context, INamedTypeSymbol spanType, INamedTypeSymbol readOnlySpanType) | ||
{ | ||
var binaryOperation = (IBinaryOperation)context.Operation; | ||
var leftOperand = binaryOperation.LeftOperand.WalkDownConversion(); | ||
var rightOperand = binaryOperation.RightOperand.WalkDownConversion(); | ||
if (rightOperand.HasNullConstantValue() && binaryOperation.LeftOperand.Type is not null && IsSpan(binaryOperation.LeftOperand.Type) | ||
|| leftOperand.HasNullConstantValue() && binaryOperation.RightOperand.Type is not null && IsSpan(binaryOperation.RightOperand.Type)) | ||
{ | ||
context.ReportDiagnostic(binaryOperation.CreateDiagnostic(DoNotCompareSpanToNullRule)); | ||
} | ||
|
||
if (rightOperand is IDefaultValueOperation && binaryOperation.LeftOperand.Type is not null && IsSpan(binaryOperation.LeftOperand.Type) | ||
|| leftOperand is IDefaultValueOperation && binaryOperation.RightOperand.Type is not null && IsSpan(binaryOperation.RightOperand.Type)) | ||
{ | ||
context.ReportDiagnostic(binaryOperation.CreateDiagnostic(DoNotCompareSpanToDefaultRule)); | ||
} | ||
|
||
bool IsSpan(ITypeSymbol typeSymbol) | ||
{ | ||
var originalType = typeSymbol.OriginalDefinition; | ||
|
||
return originalType.Equals(spanType, SymbolEqualityComparer.Default) | ||
|| originalType.Equals(readOnlySpanType, SymbolEqualityComparer.Default); | ||
} | ||
} | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotCompareSpanToNullRule, DoNotCompareSpanToDefaultRule); | ||
} | ||
} |
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
Oops, something went wrong.