From bae4d297199ec1384b6e01d200b1b2a5b2e2912a Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 19 Nov 2022 15:17:23 +0100 Subject: [PATCH] Analyzer: Simplify argument null check (RCS1255) (#994) --- .editorconfig | 1 + ChangeLog.md | 4 + .../CodeFixes/IfStatementCodeFixProvider.cs | 43 ++++++- src/Analyzers/Analyzers.xml | 17 +++ .../SimplifyArgumentNullCheckAnalyzer.cs | 99 +++++++++++++++ .../CSharp/DiagnosticIdentifiers.Generated.cs | 1 + .../CSharp/DiagnosticRules.Generated.cs | 14 ++- .../Analyzers.Tests/Analyzers.Tests.csproj | 2 +- .../RCS1255SimplifyArgumentNullCheckTests.cs | 118 ++++++++++++++++++ src/Tests/CSharp.Tests/CSharp.Tests.csproj | 2 +- .../CSharp.Workspaces.Tests.csproj | 2 +- .../CodeAnalysis.Analyzers.Tests.csproj | 2 +- .../CodeFixes.Tests/CodeFixes.Tests.csproj | 2 +- src/Tests/Core.Tests/Core.Tests.csproj | 2 +- .../Formatting.Analyzers.Tests.csproj | 2 +- .../Refactorings.Tests.csproj | 2 +- src/Tests/TestConsole/TestConsole.csproj | 2 +- src/Tests/TestLibrary/TestLibrary.csproj | 2 +- 18 files changed, 303 insertions(+), 14 deletions(-) create mode 100644 src/Analyzers/CSharp/Analysis/SimplifyArgumentNullCheckAnalyzer.cs create mode 100644 src/Tests/Analyzers.Tests/RCS1255SimplifyArgumentNullCheckTests.cs diff --git a/.editorconfig b/.editorconfig index 034defa6af..0c0dc310ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -138,6 +138,7 @@ dotnet_diagnostic.RCS1250.severity = suggestion dotnet_diagnostic.RCS1252.severity = suggestion dotnet_diagnostic.RCS1253.severity = suggestion dotnet_diagnostic.RCS1254.severity = suggestion +dotnet_diagnostic.RCS1255.severity = suggestion dotnet_diagnostic.IDE0007.severity = none dotnet_diagnostic.IDE0007WithoutSuggestion.severity = none diff --git a/ChangeLog.md b/ChangeLog.md index 3fd2e4d0fb..ecb7a1ab5f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Arm64 VS 2022 extension support ([#990](https://github.com/JosefPihrt/Roslynator/pull/990) by @snickler). - Add analyzer "Add/remove blank line after file scoped namespace declaration" [RCS0060](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS0060.md) ([#993](https://github.com/josefpihrt/roslynator/pull/993). - Required option: `roslynator_blank_line_after_file_scoped_namespace_declaration = true|false` + - Not enabled by default +- Add analyzer "Simplify argument null check" ([RCS1255](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1255.md)) ([#994](https://github.com/JosefPihrt/Roslynator/pull/994)). + - Use `ArgumentNullException.ThrowIfNull` instead of `if` null check. + - Not enabled by default ### Changed diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/IfStatementCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/IfStatementCodeFixProvider.cs index 18edb1cef0..76ce45cca2 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/IfStatementCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/IfStatementCodeFixProvider.cs @@ -16,6 +16,9 @@ using Roslynator.CSharp.Analysis.If; using Roslynator.CSharp.Refactorings; using Roslynator.CSharp.Refactorings.ReduceIfNesting; +using Roslynator.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Roslynator.CSharp.CSharpFactory; namespace Roslynator.CSharp.CodeFixes; @@ -33,7 +36,8 @@ public override ImmutableArray FixableDiagnosticIds DiagnosticIdentifiers.ConvertIfToReturnStatement, DiagnosticIdentifiers.ConvertIfToAssignment, DiagnosticIdentifiers.ReduceIfNesting, - DiagnosticIdentifiers.UseExceptionFilter); + DiagnosticIdentifiers.UseExceptionFilter, + DiagnosticIdentifiers.SimplifyArgumentNullCheck); } } @@ -110,6 +114,22 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) }, GetEquivalenceKey(diagnostic)); + context.RegisterCodeFix(codeAction, diagnostic); + break; + } + case DiagnosticIdentifiers.SimplifyArgumentNullCheck: + { + CodeAction codeAction = CodeAction.Create( + "Call ArgumentNullException.ThrowIfNull", + ct => + { + return CallArgumentNullExceptionThrowIfNullAsync( + context.Document, + ifStatement, + cancellationToken: ct); + }, + GetEquivalenceKey(diagnostic)); + context.RegisterCodeFix(codeAction, diagnostic); break; } @@ -156,7 +176,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) CatchClauseSyntax newCatchClause = catchClause.Update( catchKeyword: catchClause.CatchKeyword, declaration: catchClause.Declaration, - filter: SyntaxFactory.CatchFilterClause(filterExpression.WalkDownParentheses()), + filter: CatchFilterClause(filterExpression.WalkDownParentheses()), block: catchClause.Block.WithStatements(newStatements)); newCatchClause = newCatchClause.WithFormatterAnnotation(); @@ -189,7 +209,7 @@ SyntaxList ReplaceStatement(StatementSyntax statement) if (!right.IsKind(SyntaxKind.LogicalAndExpression)) right = right.Parenthesize(); - BinaryExpressionSyntax newCondition = CSharpFactory.LogicalAndExpression(left, right); + BinaryExpressionSyntax newCondition = LogicalAndExpression(left, right); IfStatementSyntax newNode = GetNewIfStatement(ifStatement, nestedIf) .WithCondition(newCondition) @@ -216,4 +236,21 @@ private static IfStatementSyntax GetNewIfStatement(IfStatementSyntax ifStatement return ifStatement.ReplaceNode(ifStatement.Statement, ifStatement2.Statement); } } + + private Task CallArgumentNullExceptionThrowIfNullAsync( + Document document, + IfStatementSyntax ifStatement, + CancellationToken cancellationToken) + { + NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(ifStatement.Condition); + + ExpressionStatementSyntax newStatement = ExpressionStatement( + SimpleMemberInvocationExpression( + ParseExpression("global::System.ArgumentNullException").WithSimplifierAnnotation(), + IdentifierName("ThrowIfNull"), + Argument(nullCheck.Expression.WithoutTrivia()))) + .WithTriviaFrom(ifStatement); + + return document.ReplaceNodeAsync(ifStatement, newStatement, cancellationToken); + } } diff --git a/src/Analyzers/Analyzers.xml b/src/Analyzers/Analyzers.xml index 37928a8cff..7124e69790 100644 --- a/src/Analyzers/Analyzers.xml +++ b/src/Analyzers/Analyzers.xml @@ -5741,4 +5741,21 @@ void M()