diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs index e32929567..3b85c56d0 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs @@ -33,63 +33,63 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) switch (data) { case DoNotUseBlockingCallInAsyncContextData.Thread_Sleep: - { - var codeAction = CodeAction.Create( - "Use Task.Delay", - ct => UseTaskDelay(context.Document, nodeToFix, ct), - equivalenceKey: "Thread_Sleep"); + { + var codeAction = CodeAction.Create( + "Use Task.Delay", + ct => UseTaskDelay(context.Document, nodeToFix, ct), + equivalenceKey: "Thread_Sleep"); - context.RegisterCodeFix(codeAction, context.Diagnostics); - } - break; + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + break; case DoNotUseBlockingCallInAsyncContextData.Task_Wait: - { - var codeAction = CodeAction.Create( - "Use await", - ct => ReplaceTaskWaitWithAwait(context.Document, nodeToFix, ct), - equivalenceKey: "Task_Wait"); + { + var codeAction = CodeAction.Create( + "Use await", + ct => ReplaceTaskWaitWithAwait(context.Document, nodeToFix, ct), + equivalenceKey: "Task_Wait"); - context.RegisterCodeFix(codeAction, context.Diagnostics); - } - break; + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + break; case DoNotUseBlockingCallInAsyncContextData.Task_Result: - { - var codeAction = CodeAction.Create( - "Use await", - ct => ReplaceTaskResultWithAwait(context.Document, nodeToFix, ct), - equivalenceKey: "Task_Result"); + { + var codeAction = CodeAction.Create( + "Use await", + ct => ReplaceTaskResultWithAwait(context.Document, nodeToFix, ct), + equivalenceKey: "Task_Result"); - context.RegisterCodeFix(codeAction, context.Diagnostics); - } - break; + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + break; case DoNotUseBlockingCallInAsyncContextData.Overload: - { - if (!properties.TryGetValue("MethodName", out var methodName) || methodName is null) - return; + { + if (!properties.TryGetValue("MethodName", out var methodName) || methodName is null) + return; - var codeAction = CodeAction.Create( - $"Use '{methodName}'", - ct => ReplaceWithMethodName(context.Document, nodeToFix, methodName, ct), - equivalenceKey: "Overload"); + var codeAction = CodeAction.Create( + $"Use '{methodName}'", + ct => ReplaceWithMethodName(context.Document, nodeToFix, methodName, ct), + equivalenceKey: "Overload"); - context.RegisterCodeFix(codeAction, context.Diagnostics); - } - break; + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + break; case DoNotUseBlockingCallInAsyncContextData.Using: case DoNotUseBlockingCallInAsyncContextData.UsingDeclarator: - { - var codeAction = CodeAction.Create( - $"Use 'await using'", - ct => ReplaceWithAwaitUsing(context.Document, nodeToFix, ct), - equivalenceKey: "Overload"); - - context.RegisterCodeFix(codeAction, context.Diagnostics); - } - break; + { + var codeAction = CodeAction.Create( + $"Use 'await using'", + ct => ReplaceWithAwaitUsing(context.Document, nodeToFix, ct), + equivalenceKey: "Overload"); + + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + break; } } diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs index 7e50231b0..3ca5523cd 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs @@ -380,8 +380,6 @@ private static async Task UseOrderInsteadOfOrderBy(Document document, var newName = member.Name.Identifier.ValueText is "OrderBy" ? "Order" : "OrderDescending"; var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var generator = editor.Generator; - editor.RemoveNode(invocation.ArgumentList.Arguments.First()); editor.ReplaceNode(member, member.WithName(IdentifierName(newName))); return editor.GetChangedDocument(); @@ -513,12 +511,21 @@ private static async Task UseIndexerLast(Document document, SyntaxNode if (semanticModel.GetOperation(nodeToFix, cancellationToken) is not IInvocationOperation operation) return document; - var newExpression = generator.ElementAccessExpression(operation.Arguments[0].Syntax, - generator.SubtractExpression( - generator.MemberAccessExpression(operation.Arguments[0].Syntax, GetMemberName()), - generator.LiteralExpression(1))); + // if C# 8.0, use ^1 + if (expression.SyntaxTree.GetCSharpLanguageVersion() >= LanguageVersion.CSharp8 && editor.SemanticModel.Compilation.GetBestTypeByMetadataName("System.Index") is not null) + { + var newExpression = generator.ElementAccessExpression(operation.Arguments[0].Syntax, PrefixUnaryExpression(SyntaxKind.IndexExpression, LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1)))); + editor.ReplaceNode(nodeToFix, newExpression); + } + else + { + var newExpression = generator.ElementAccessExpression(operation.Arguments[0].Syntax, + generator.SubtractExpression( + generator.MemberAccessExpression(operation.Arguments[0].Syntax, GetMemberName()), + generator.LiteralExpression(1))); + editor.ReplaceNode(nodeToFix, newExpression); + } - editor.ReplaceNode(nodeToFix, newExpression); return editor.GetChangedDocument(); string GetMemberName() diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs index dcacd784c..5505b6cbb 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs @@ -34,7 +34,7 @@ public Test() """) .ValidateAsync(); } - + [Fact] public async Task IEnumerable_Order_LambdaNotValid() { @@ -54,7 +54,7 @@ public Test() """) .ValidateAsync(); } - + [Fact] public async Task IEnumerable_Order_LambdaReferenceAnotherParameter() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs index a79711e02..b0b5d0488 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs @@ -139,6 +139,77 @@ public Test() "; await CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7_3) + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use '[]' instead of 'Last()'") + .ShouldFixCodeWith(CodeFix) + .ValidateAsync(); + } + + [Fact] + public async Task Last_Array_CSharp8_IndexNotAvailable() + { + const string SourceCode = @"using System.Linq; +class Test +{ + public Test() + { + var list = new int[5]; + _ = list.[|Last|](); + list.First(x=> x == 0); + } +} +"; + const string CodeFix = @"using System.Linq; +class Test +{ + public Test() + { + var list = new int[5]; + _ = list[list.Length - 1]; + list.First(x=> x == 0); + } +} +"; + + await CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8) + .WithTargetFramework(TargetFramework.Net4_8) + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use '[]' instead of 'Last()'") + .ShouldFixCodeWith(CodeFix) + .ValidateAsync(); + } + + [Fact] + public async Task Last_Array_CSharp8_IndexAvailable() + { + const string SourceCode = @"using System.Linq; +class Test +{ + public Test() + { + var list = new int[5]; + _ = list.[|Last|](); + list.First(x=> x == 0); + } +} +"; + const string CodeFix = @"using System.Linq; +class Test +{ + public Test() + { + var list = new int[5]; + _ = list[^1]; + list.First(x=> x == 0); + } +} +"; + + await CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8) + .WithTargetFramework(TargetFramework.Net8_0) .WithSourceCode(SourceCode) .ShouldReportDiagnosticWithMessage("Use '[]' instead of 'Last()'") .ShouldFixCodeWith(CodeFix) @@ -172,6 +243,7 @@ public Test() "; await CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7_3) .WithSourceCode(SourceCode) .ShouldReportDiagnosticWithMessage("Use '[]' instead of 'Last()'") .ShouldFixCodeWith(CodeFix)