From c3dc3e263a85f0fd969d997403cd577231b806b3 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Wed, 23 Nov 2022 20:27:10 +0100 Subject: [PATCH] Use pattern matching (RCS1146) (#999) --- ChangeLog.md | 1 + .../UseConditionalAccessCodeFixProvider.cs | 6 +- .../Analysis/UseConditionalAccessAnalyzer.cs | 8 ++- .../RCS1146UseConditionalAccessTests.cs | 55 +++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 3aa4337353..495eca5531 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix refactoring ([RR0014](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RR0014.md)) ([#988](https://github.com/josefpihrt/roslynator/pull/988)). - Fix refactoring ([RR0180](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RR0180.md)) ([#988](https://github.com/josefpihrt/roslynator/pull/988)). - Recognize `ArgumentNullException.ThrowIfNull` ([RCS1227](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1227.md)) ([#992](https://github.com/josefpihrt/roslynator/pull/992)). +- Detect pattern matching in [RCS1146](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1146.md) ([#999](https://github.com/josefpihrt/roslynator/pull/999)). ## [4.1.2] - 2022-10-31 diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseConditionalAccessCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseConditionalAccessCodeFixProvider.cs index ebd5fa4ba3..35bf316784 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseConditionalAccessCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseConditionalAccessCodeFixProvider.cs @@ -84,8 +84,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) (ExpressionSyntax left, ExpressionSyntax right) = UseConditionalAccessAnalyzer.GetFixableExpressions(binaryExpression, kind, semanticModel, cancellationToken); NullCheckStyles allowedStyles = (kind == SyntaxKind.LogicalAndExpression) - ? NullCheckStyles.NotEqualsToNull - : NullCheckStyles.EqualsToNull; + ? (NullCheckStyles.NotEqualsToNull | NullCheckStyles.IsNotNull) + : (NullCheckStyles.EqualsToNull | NullCheckStyles.IsNull); NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(left, semanticModel, allowedStyles: allowedStyles, cancellationToken: cancellationToken); @@ -190,7 +190,7 @@ int GetParenTokenDiff() StatementSyntax newStatement = statement; - NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(ifStatement.Condition, NullCheckStyles.NotEqualsToNull); + NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(ifStatement.Condition, NullCheckStyles.NotEqualsToNull | NullCheckStyles.IsNotNull); SimpleMemberInvocationStatementInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationStatementInfo(statement); diff --git a/src/Analyzers/CSharp/Analysis/UseConditionalAccessAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UseConditionalAccessAnalyzer.cs index 93cdb2a9d3..2959fdd407 100644 --- a/src/Analyzers/CSharp/Analysis/UseConditionalAccessAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UseConditionalAccessAnalyzer.cs @@ -58,7 +58,9 @@ private static void AnalyzeIfStatement(SyntaxNodeAnalysisContext context) if (ifStatement.SpanContainsDirectives()) return; - NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(ifStatement.Condition, allowedStyles: NullCheckStyles.NotEqualsToNull); + NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo( + ifStatement.Condition, + allowedStyles: NullCheckStyles.NotEqualsToNull | NullCheckStyles.IsNotNull); ExpressionSyntax expression = nullCheck.Expression; @@ -165,8 +167,8 @@ static bool ExistsImplicitConversionToBoolean(INamedTypeSymbol typeSymbol) CancellationToken cancellationToken) { NullCheckStyles allowedStyles = (binaryExpressionKind == SyntaxKind.LogicalAndExpression) - ? NullCheckStyles.NotEqualsToNull - : NullCheckStyles.EqualsToNull; + ? (NullCheckStyles.NotEqualsToNull | NullCheckStyles.IsNotNull) + : (NullCheckStyles.EqualsToNull | NullCheckStyles.IsNull); NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(left, semanticModel, allowedStyles: allowedStyles, cancellationToken: cancellationToken); diff --git a/src/Tests/Analyzers.Tests/RCS1146UseConditionalAccessTests.cs b/src/Tests/Analyzers.Tests/RCS1146UseConditionalAccessTests.cs index 0289069218..e523fc427e 100644 --- a/src/Tests/Analyzers.Tests/RCS1146UseConditionalAccessTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1146UseConditionalAccessTests.cs @@ -80,6 +80,35 @@ void M() "); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccess)] + public async Task Test_IfStatement_PatternMatching() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + C x = null; + + [|if (x is not null) + { + x.M(); + }|] + } +} +", @" +class C +{ + void M() + { + C x = null; + + x?.M(); + } +} +"); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccess)] public async Task Test_LogicalAnd_ReferenceType() { @@ -183,6 +212,32 @@ void M() "); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccess)] + public async Task Test_LogicalAnd_ReferenceType_PatternMatching() + { + await VerifyDiagnosticAndFixAsync(@" +class Foo +{ + void M() + { + Foo x = null; + + if ([|x is not null && !x.Equals(x)|]) { } + } +} +", @" +class Foo +{ + void M() + { + Foo x = null; + + if (x?.Equals(x) == false) { } + } +} +"); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccess)] public async Task Test_LogicalOr_ReferenceType() {