From 92c2636c3a3d81e5eabfb25696599781909848db Mon Sep 17 00:00:00 2001 From: Jakub Reznak Date: Sat, 17 Feb 2024 22:43:09 +0100 Subject: [PATCH 1/4] RCS1268 - Simplify Numeric Comparison --- ChangeLog.md | 1 + .../BinaryExpressionCodeFixProvider.cs | 69 +++++- src/Analyzers.xml | 30 +++ .../SimplifyNumericComparisonAnalyzer.cs | 59 +++++ .../CSharp/DiagnosticIdentifiers.Generated.cs | 1 + .../CSharp/DiagnosticRules.Generated.cs | 12 + .../RCS1268SimplifyNumericComparisonTests.cs | 223 ++++++++++++++++++ .../src/configurationFiles.generated.ts | 8 +- 8 files changed, 397 insertions(+), 6 deletions(-) create mode 100644 src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs create mode 100644 src/Tests/Analyzers.Tests/RCS1268SimplifyNumericComparisonTests.cs diff --git a/ChangeLog.md b/ChangeLog.md index 0b03d8dace..02c0f4f38a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Simplify LINQ query [RCS1077](https://josefpihrt.github.io/docs/roslynator/analyzers/1077) ([PR](https://github.com/dotnet/roslynator/pull/1384)) - `items.Select(selector).Average()` => `items.Average(selector)` - `items.Select(selector).Sum()` => `items.Sum(selector)` +- Add analyzer "Simplify numeric comparison" [RCS1268](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1268) ([PR](https://github.com/dotnet/roslynator/pull/1405) by @jakubreznak) ### Fixed diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs index 1817aca8ea..626775eae8 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs @@ -37,7 +37,8 @@ public override ImmutableArray FixableDiagnosticIds DiagnosticIdentifiers.UseExclusiveOrOperator, DiagnosticIdentifiers.UnnecessaryNullCheck, DiagnosticIdentifiers.UseShortCircuitingOperator, - DiagnosticIdentifiers.UnnecessaryOperator); + DiagnosticIdentifiers.UnnecessaryOperator, + DiagnosticIdentifiers.SimplifyNumericComparison); } } @@ -240,6 +241,16 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) }, GetEquivalenceKey(diagnostic)); + context.RegisterCodeFix(codeAction, diagnostic); + break; + } + case DiagnosticIdentifiers.SimplifyNumericComparison: + { + CodeAction codeAction = CodeAction.Create( + "Simplify numeric comparison", + ct => SimplifyNumericComparisonAsync(document, binaryExpression, ct), + GetEquivalenceKey(diagnostic)); + context.RegisterCodeFix(codeAction, diagnostic); break; } @@ -360,4 +371,60 @@ private static async Task RemoveUnnecessaryNullCheckAsync( return await document.ReplaceNodeAsync(logicalAnd, newBinaryExpression, cancellationToken).ConfigureAwait(false); } + + private static Task SimplifyNumericComparisonAsync( + Document document, + BinaryExpressionSyntax binaryExpression, + CancellationToken cancellationToken) + { + BinaryExpressionSyntax subtractionExpression; + SyntaxKind kind = binaryExpression.Kind(); + + BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(binaryExpression); + + if (info.Left.IsNumericLiteralExpression("0")) + { + subtractionExpression = (BinaryExpressionSyntax)info.Right; + + switch (kind) + { + case SyntaxKind.GreaterThanExpression: + { + kind = SyntaxKind.LessThanExpression; + break; + } + case SyntaxKind.GreaterThanOrEqualExpression: + { + kind = SyntaxKind.LessThanOrEqualExpression; + break; + } + case SyntaxKind.LessThanExpression: + { + kind = SyntaxKind.GreaterThanExpression; + break; + } + case SyntaxKind.LessThanOrEqualExpression: + { + kind = SyntaxKind.GreaterThanOrEqualExpression; + break; + } + } + } + else + { + subtractionExpression = (BinaryExpressionSyntax)info.Left; + } + + BinaryExpressionSyntax newBinaryExpression = BinaryExpression( + kind, + subtractionExpression.Left, + subtractionExpression.Right); + + newBinaryExpression = newBinaryExpression + .WithTriviaFrom(binaryExpression) + .WithFormatterAnnotation(); + + return document.ReplaceNodeAsync(binaryExpression, newBinaryExpression, cancellationToken); + } + } diff --git a/src/Analyzers.xml b/src/Analyzers.xml index ff5f2fc9af..c964f38a32 100644 --- a/src/Analyzers.xml +++ b/src/Analyzers.xml @@ -7727,6 +7727,36 @@ string s = """ + + RCS1268 + SimplifyNumericComparison + Simplify numeric comparison expression + Info + true + Simplifies comparisons that involve subtracting two values and comparing the result to zero. + + + + + + + 0]]> + b]]> + + + = 0]]> + = b]]> + + + + + + + + + + + RCS9001 UsePatternMatching diff --git a/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs new file mode 100644 index 0000000000..ed7e2838c0 --- /dev/null +++ b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs @@ -0,0 +1,59 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslynator.CSharp.Analysis; +using Roslynator.CSharp.Syntax; + +namespace Roslynator.CSharp.CSharp.Analysis; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class SimplifyNumericComparisonAnalyzer : BaseDiagnosticAnalyzer +{ + private static ImmutableArray _supportedDiagnostics; + + public override ImmutableArray SupportedDiagnostics + { + get + { + if (_supportedDiagnostics.IsDefault) + { + Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.SimplifyNumericComparison); + } + + return _supportedDiagnostics; + } + } + + public override void Initialize(AnalysisContext context) + { + base.Initialize(context); + + context.RegisterSyntaxNodeAction(f => AnalyzeComparison(f) + ,SyntaxKind.EqualsExpression, SyntaxKind.GreaterThanExpression, SyntaxKind.GreaterThanOrEqualExpression + ,SyntaxKind.LessThanExpression, SyntaxKind.LessThanOrEqualExpression); + } + + private static void AnalyzeComparison(SyntaxNodeAnalysisContext context) + { + var binaryExpression = (BinaryExpressionSyntax)context.Node; + + BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(binaryExpression); + + if (!info.Success) + return; + + ExpressionSyntax leftExpression = info.Left; + ExpressionSyntax rightExpression = info.Right; + + if ((leftExpression.IsNumericLiteralExpression("0") && rightExpression.Kind() == SyntaxKind.SubtractExpression) + || (leftExpression.Kind() == SyntaxKind.SubtractExpression && rightExpression.IsNumericLiteralExpression("0"))) + { + DiagnosticHelpers.ReportDiagnostic( + context, + DiagnosticRules.SimplifyNumericComparison, + binaryExpression); + } + } +} diff --git a/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs b/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs index e091f4bfcb..dd1cb8ae80 100644 --- a/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs @@ -224,5 +224,6 @@ public static partial class DiagnosticIdentifiers public const string RemoveRedundantCatchBlock = "RCS1265"; public const string UseRawStringLiteral = "RCS1266"; public const string UseStringInterpolationInsteadOfStringConcat = "RCS1267"; + public const string SimplifyNumericComparison = "RCS1268"; } } \ No newline at end of file diff --git a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs index 48cd08e20d..55d1fca299 100644 --- a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs @@ -2651,5 +2651,17 @@ public static partial class DiagnosticRules helpLinkUri: DiagnosticIdentifiers.UseStringInterpolationInsteadOfStringConcat, customTags: Array.Empty()); + /// RCS1268 + public static readonly DiagnosticDescriptor SimplifyNumericComparison = DiagnosticDescriptorFactory.Create( + id: DiagnosticIdentifiers.SimplifyNumericComparison, + title: "Simplify numeric comparison expression", + messageFormat: "Simplify numeric comparison expression", + category: DiagnosticCategories.Roslynator, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true, + description: null, + helpLinkUri: DiagnosticIdentifiers.SimplifyNumericComparison, + customTags: Array.Empty()); + } } \ No newline at end of file diff --git a/src/Tests/Analyzers.Tests/RCS1268SimplifyNumericComparisonTests.cs b/src/Tests/Analyzers.Tests/RCS1268SimplifyNumericComparisonTests.cs new file mode 100644 index 0000000000..a3e316a244 --- /dev/null +++ b/src/Tests/Analyzers.Tests/RCS1268SimplifyNumericComparisonTests.cs @@ -0,0 +1,223 @@ +// Copyright (c) .NET Foundation 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.CSharp.CSharp.Analysis; +using Roslynator.Testing.CSharp; +using Xunit; + +namespace Roslynator.CSharp.Analysis.Tests; + +public class RCS1268SimplifyNumericComparisonTests : AbstractCSharpDiagnosticVerifier +{ + public override DiagnosticDescriptor Descriptor { get; } = DiagnosticRules.SimplifyNumericComparison; + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task Test_LeftSideSubtraction_EqualsZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|(a - b) == 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a == b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task Test_RightSideSubtraction_EqualsZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|0 == (a - b)|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a == b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task Test_LeftSideSubtraction_WithoutParenthesis_EqualsZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|a - b == 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a == b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task Test_LeftSideSubtraction_SubtractionGreaterThanZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|(a - b) > 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a > b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task Test_RightSideSubtraction_SubtractionSmallerThanZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|0 > (a - b)|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a < b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task TestSubtractionLessThanZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|(a - b) < 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a < b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task TestSubtractionGreaterThanOrEqualToZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|(a - b) >= 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a >= b) + { + } + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.SimplifyNumericComparison)] + public async Task TestSubtractionLessThanOrEqualToZero() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(int a, int b) + { + if ([|(a - b) <= 0|]) + { + } + } +} +", @" +class C +{ + void M(int a, int b) + { + if (a <= b) + { + } + } +} +"); + } +} diff --git a/src/VisualStudioCode/package/src/configurationFiles.generated.ts b/src/VisualStudioCode/package/src/configurationFiles.generated.ts index 14d8d056ca..01f2790e57 100644 --- a/src/VisualStudioCode/package/src/configurationFiles.generated.ts +++ b/src/VisualStudioCode/package/src/configurationFiles.generated.ts @@ -916,6 +916,9 @@ roslynator_analyzers.enabled_by_default = true|false # Use string interpolation instead of 'string.Concat' #dotnet_diagnostic.rcs1267.severity = suggestion +# Simplify numeric comparison expression +#dotnet_diagnostic.rcs1268.severity = suggestion + # Use pattern matching #dotnet_diagnostic.rcs9001.severity = silent @@ -1024,11 +1027,6 @@ roslynator_analyzers.enabled_by_default = true|false #roslynator_refactoring.generate_enum_values.enabled = true #roslynator_refactoring.generate_event_invoking_method.enabled = true #roslynator_refactoring.generate_property_for_debuggerdisplay_attribute.enabled = true -#roslynator_refactoring.change_accessibility.enabled = true -#roslynator_refactoring.change_method_return_type_to_void.enabled = true -#roslynator_refactoring.change_type_according_to_expression.enabled = true -#roslynator_refactoring.check_expression_for_null.enabled = true -#roslynator_refactoring.check_parameter_for_null.enabled = true #roslynator_refactoring.implement_custom_enumerator.enabled = true #roslynator_refactoring.implement_iequatable.enabled = true #roslynator_refactoring.initialize_field_from_constructor.enabled = true From 9322500ff45248808ce7f4687074b1ab959b771e Mon Sep 17 00:00:00 2001 From: Jakub Reznak Date: Sat, 17 Feb 2024 22:58:34 +0100 Subject: [PATCH 2/4] Formatting edits --- .../CodeFixes/BinaryExpressionCodeFixProvider.cs | 1 - .../Analysis/SimplifyNumericComparisonAnalyzer.cs | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs index 626775eae8..ede0dea2e8 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs @@ -426,5 +426,4 @@ private static Task SimplifyNumericComparisonAsync( return document.ReplaceNodeAsync(binaryExpression, newBinaryExpression, cancellationToken); } - } diff --git a/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs index ed7e2838c0..162802ac60 100644 --- a/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs @@ -30,9 +30,13 @@ public override void Initialize(AnalysisContext context) { base.Initialize(context); - context.RegisterSyntaxNodeAction(f => AnalyzeComparison(f) - ,SyntaxKind.EqualsExpression, SyntaxKind.GreaterThanExpression, SyntaxKind.GreaterThanOrEqualExpression - ,SyntaxKind.LessThanExpression, SyntaxKind.LessThanOrEqualExpression); + context.RegisterSyntaxNodeAction( + f => AnalyzeComparison(f), + SyntaxKind.EqualsExpression, + SyntaxKind.GreaterThanExpression, + SyntaxKind.GreaterThanOrEqualExpression, + SyntaxKind.LessThanExpression, + SyntaxKind.LessThanOrEqualExpression); } private static void AnalyzeComparison(SyntaxNodeAnalysisContext context) From 3a878d2db690d8d6ceb5b66586d51060cad6f6f9 Mon Sep 17 00:00:00 2001 From: Jakub Reznak Date: Sun, 25 Feb 2024 18:26:54 +0100 Subject: [PATCH 3/4] Code review edits. --- .../BinaryExpressionCodeFixProvider.cs | 30 +++++-------------- src/Analyzers.xml | 2 +- .../SimplifyNumericComparisonAnalyzer.cs | 4 +-- .../CSharp/DiagnosticRules.Generated.cs | 4 +-- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs index ede0dea2e8..848f6f5e63 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs @@ -386,29 +386,15 @@ private static Task SimplifyNumericComparisonAsync( { subtractionExpression = (BinaryExpressionSyntax)info.Right; - switch (kind) + kind = kind switch { - case SyntaxKind.GreaterThanExpression: - { - kind = SyntaxKind.LessThanExpression; - break; - } - case SyntaxKind.GreaterThanOrEqualExpression: - { - kind = SyntaxKind.LessThanOrEqualExpression; - break; - } - case SyntaxKind.LessThanExpression: - { - kind = SyntaxKind.GreaterThanExpression; - break; - } - case SyntaxKind.LessThanOrEqualExpression: - { - kind = SyntaxKind.GreaterThanOrEqualExpression; - break; - } - } + SyntaxKind.GreaterThanExpression => SyntaxKind.LessThanExpression, + SyntaxKind.GreaterThanOrEqualExpression => SyntaxKind.LessThanOrEqualExpression, + SyntaxKind.LessThanExpression => SyntaxKind.GreaterThanExpression, + SyntaxKind.LessThanOrEqualExpression => SyntaxKind.GreaterThanOrEqualExpression, + _ => kind + }; + } else { diff --git a/src/Analyzers.xml b/src/Analyzers.xml index c964f38a32..2d69e5cf5a 100644 --- a/src/Analyzers.xml +++ b/src/Analyzers.xml @@ -7730,7 +7730,7 @@ string s = """ RCS1268 SimplifyNumericComparison - Simplify numeric comparison expression + Simplify numeric comparison Info true Simplifies comparisons that involve subtracting two values and comparing the result to zero. diff --git a/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs index 162802ac60..0f391d54bb 100644 --- a/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs @@ -51,8 +51,8 @@ private static void AnalyzeComparison(SyntaxNodeAnalysisContext context) ExpressionSyntax leftExpression = info.Left; ExpressionSyntax rightExpression = info.Right; - if ((leftExpression.IsNumericLiteralExpression("0") && rightExpression.Kind() == SyntaxKind.SubtractExpression) - || (leftExpression.Kind() == SyntaxKind.SubtractExpression && rightExpression.IsNumericLiteralExpression("0"))) + if ((leftExpression.IsNumericLiteralExpression("0") && rightExpression.IsKind(SyntaxKind.SubtractExpression)) + || (leftExpression.IsKind(SyntaxKind.SubtractExpression) && rightExpression.IsNumericLiteralExpression("0"))) { DiagnosticHelpers.ReportDiagnostic( context, diff --git a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs index 55d1fca299..9e0d1ca25f 100644 --- a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs @@ -2654,8 +2654,8 @@ public static partial class DiagnosticRules /// RCS1268 public static readonly DiagnosticDescriptor SimplifyNumericComparison = DiagnosticDescriptorFactory.Create( id: DiagnosticIdentifiers.SimplifyNumericComparison, - title: "Simplify numeric comparison expression", - messageFormat: "Simplify numeric comparison expression", + title: "Simplify numeric comparison", + messageFormat: "Simplify numeric comparison", category: DiagnosticCategories.Roslynator, defaultSeverity: DiagnosticSeverity.Info, isEnabledByDefault: true, From 0927a63ca49b5ea69ce4b0f8c54a3f560171f7f5 Mon Sep 17 00:00:00 2001 From: Jakub Reznak Date: Sun, 25 Feb 2024 18:31:19 +0100 Subject: [PATCH 4/4] Blank line removed. --- .../CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs index 848f6f5e63..8f9946e7e3 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/BinaryExpressionCodeFixProvider.cs @@ -394,7 +394,6 @@ private static Task SimplifyNumericComparisonAsync( SyntaxKind.LessThanOrEqualExpression => SyntaxKind.GreaterThanOrEqualExpression, _ => kind }; - } else {