From da4cb6a8ea8a37f2a17ceb893d336892ce09daa1 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 1 Mar 2024 01:37:37 +0700 Subject: [PATCH] [AK1000] Remove AK1001 rule (#74) --- ...eOverSenderWhenUsingPipeToAnalyzerSpecs.cs | 237 ------------ ...loseOverSenderWhenUsingPipeToFixerSpecs.cs | 366 ------------------ ...tCloseOverSenderWhenUsingPipeToAnalyzer.cs | 61 --- 3 files changed, 664 deletions(-) delete mode 100644 src/Akka.Analyzers.Tests/Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzerSpecs.cs delete mode 100644 src/Akka.Analyzers.Tests/Fixes/AK1000/MustCloseOverSenderWhenUsingPipeToFixerSpecs.cs delete mode 100644 src/Akka.Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzer.cs diff --git a/src/Akka.Analyzers.Tests/Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzerSpecs.cs b/src/Akka.Analyzers.Tests/Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzerSpecs.cs deleted file mode 100644 index 2989ff5..0000000 --- a/src/Akka.Analyzers.Tests/Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzerSpecs.cs +++ /dev/null @@ -1,237 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2013-2024 .NET Foundation -// -// ----------------------------------------------------------------------- - -using Microsoft.CodeAnalysis; -using Verify = Akka.Analyzers.Tests.Utility.AkkaVerifier; - -namespace Akka.Analyzers.Tests.Analyzers.AK1000; - -public class MustCloseOverSenderWhenUsingPipeToAnalyzerSpecs -{ - public static readonly TheoryData SuccessCases = new() - { - // ReceiveActor using PipeTo with a closure inside a Receive block - @"using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - async Task LocalFunction(){ - await Task.Delay(10); - return str.Length; - } - - // correct use of closure - var sender = Sender; - LocalFunction().PipeTo(sender); - }); - } - }", - - // ReceiveActor using PipeTo calling a method from within a Receive block - @"using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - Execute(str); - }); - } - - private void Execute(string str){ - async Task LocalFunction(){ - await Task.Delay(10); - return str.Length; - } - - // correct use of closure - var sender = Sender; - LocalFunction().PipeTo(sender); - } - };", - - // Actor doing message handling in a Receive block without PipeTo at all - @"using Akka.Actor; - - public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - Sender.Tell(str); // shouldn't flag this - }); - } - }", - - // Non-Actor class that has an IActorRef Sender property - """ - using Akka.Actor; - using System.Threading.Tasks; - - public class MyActor - { - public MyActor(IActorRef sender) - { - Sender = sender; - } - - public IActorRef Sender { get; } - - public void Method() - { - async Task LocalFunction(){ - await Task.Delay(10); - return 11; - } - - // Sender is immutable on this custom non-Actor class, so shouldn't flag this - LocalFunction().PipeTo(Sender); - } - } - """, - // Replying to Sender using Context.Sender - @"using Akka.Actor; - - public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - Context.Sender.Tell(str); // shouldn't flag this - }); - } - }", - }; - - public static readonly - TheoryData<(string testData, (int startLine, int startColumn, int endLine, int endColumn) spanData)> - FailureCases = new() - { - // Receive actor using PipeTo without a closure inside a Receive block - (@"using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - async Task LocalFunction(){ - await Task.Delay(10); - return str.Length; - } - - // incorrect use of closure - LocalFunction().PipeTo(Sender); - }); - } - }", (14, 37, 14, 43)), - - // UntypedActor using PipeTo without a closure inside a OnReceive block - (@"using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - // incorrect use of closure - LocalFunction().PipeTo(Sender); - } - }", (13, 33, 13, 39)), - - // Actor is using Sender as the "sender" property on PipeTo, rather than the recipient - (@"using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - // incorrect use of closure - LocalFunction().PipeTo(Self, Sender); - } - }", (13, 33, 13, 39)), - - // Actor is using Sender as the "sender" property on PipeTo, rather than the recipient - ( // Actor is using this.Sender rather than just "Sender" - """ - using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - // incorrect use of closure - LocalFunction().PipeTo(this.Sender); - } - } - """, (13, 25, 13, 31)) - }; - - [Theory] - [MemberData(nameof(SuccessCases))] - public async Task SuccessCase(string testCode) - { - await Verify.VerifyAnalyzer(testCode).ConfigureAwait(true); - } - - [Theory] - [MemberData(nameof(FailureCases))] - public Task FailureCase( - (string testCode, (int startLine, int startColumn, int endLine, int endColumn) spanData) d) - { - var expected = Verify.Diagnostic() - .WithSpan(d.spanData.startLine, d.spanData.startColumn, d.spanData.endLine, d.spanData.endColumn) - .WithArguments("Sender") - .WithSeverity(DiagnosticSeverity.Error); - - return Verify.VerifyAnalyzer(d.testCode, expected); - } - - [Fact(DisplayName = "Should detect missing closure when using Context.Sender instead of this.Sender")] - public Task FailureCaseWithContextSender() - { - var code = """ - using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - // incorrect use of closures - LocalFunction().PipeTo(Context.Sender); - } - } - """; - - var expected = Verify.Diagnostic() - .WithSpan(13, 25, 13, 31) - .WithArguments("Context.Sender") - .WithSeverity(DiagnosticSeverity.Error); - - return Verify.VerifyAnalyzer(code, expected); - } -} \ No newline at end of file diff --git a/src/Akka.Analyzers.Tests/Fixes/AK1000/MustCloseOverSenderWhenUsingPipeToFixerSpecs.cs b/src/Akka.Analyzers.Tests/Fixes/AK1000/MustCloseOverSenderWhenUsingPipeToFixerSpecs.cs deleted file mode 100644 index 4878f06..0000000 --- a/src/Akka.Analyzers.Tests/Fixes/AK1000/MustCloseOverSenderWhenUsingPipeToFixerSpecs.cs +++ /dev/null @@ -1,366 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2013-2024 .NET Foundation -// -// ----------------------------------------------------------------------- - -using Akka.Analyzers.Fixes; -using Microsoft.CodeAnalysis; -using Verify = Akka.Analyzers.Tests.Utility.AkkaVerifier; - -namespace Akka.Analyzers.Tests.Fixes.AK1000; - -public class MustCloseOverSenderWhenUsingPipeToFixerSpecs -{ - [Fact] - public Task AddClosureInsideReceiveActor() - { - var before = - @"using Akka.Actor; -using System.Threading.Tasks; - -public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - async Task LocalFunction(){ - await Task.Delay(10); - return str.Length; - } - - // incorrect use of closure - LocalFunction().PipeTo(Sender); - }); - } -}"; - - var after = - @"using Akka.Actor; -using System.Threading.Tasks; - -public sealed class MyActor : ReceiveActor{ - - public MyActor(){ - Receive(str => { - async Task LocalFunction(){ - await Task.Delay(10); - return str.Length; - } - var sender = this.Sender; - - // incorrect use of closure - LocalFunction().PipeTo(sender); - }); - } -}"; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(14, 29, 14, 35) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact] - public Task AddClosureInsideOneLinerReceiveMethod() - { - var before = -""" -using Akka.Actor; -using System.Threading.Tasks; - -public sealed class MyActor : ReceiveActor -{ - public MyActor() - { - Receive(str => MessageHandler(str).PipeTo(Sender)); - } - - private Task MessageHandler(string str) - { - return Task.FromResult(str.Length); - } -} -"""; - - var after = -""" -using Akka.Actor; -using System.Threading.Tasks; - -public sealed class MyActor : ReceiveActor -{ - public MyActor() - { - Receive(str => - { - var sender = this.Sender; - MessageHandler(str).PipeTo(sender); - }); - } - - private Task MessageHandler(string str) - { - return Task.FromResult(str.Length); - } -} -"""; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(8, 52, 8, 58) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact] - public Task AddClosureInsideUntypedActor() - { - var before = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - // incorrect use of closure - LocalFunction().PipeTo(Sender); - } -}"; - - var after = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - var sender = this.Sender; - // incorrect use of closure - LocalFunction().PipeTo(sender); - } -}"; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(15, 25, 15, 31) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact] - public Task AddClosureWithoutErasingOtherPipeToArguments() - { - var before = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - // incorrect use of closure - LocalFunction().PipeTo(Sender, success: r => 1); - } -}"; - - var after = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - var sender = this.Sender; - // incorrect use of closure - LocalFunction().PipeTo(sender, success: r => 1); - } -}"; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(15, 25, 15, 31) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact] - public Task MustOnlyReplaceSenderArgumentWhenUsed() - { - var before = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - // incorrect use of closure - LocalFunction().PipeTo(Self, Sender); - } -}"; - - var after = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - var sender = this.Sender; - // incorrect use of closure - LocalFunction().PipeTo(Self, sender); - } -}"; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(15, 25, 15, 31) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact] - public Task ReplaceSenderWhenUsedForBothRecipientAndSender() - { - var before = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - // incorrect use of closure - LocalFunction().PipeTo(this.Sender, Sender); - } -}"; - - var after = - @"using Akka.Actor; -using System.Threading.Tasks; -using System; - -public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - Console.WriteLine(Sender); - var sender = this.Sender; - // incorrect use of closure - LocalFunction().PipeTo(sender, sender); - } -}"; - - var expectedDiagnostic = Verify.Diagnostic() - .WithSpan(15, 25, 15, 31) - .WithArguments("Sender"); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expectedDiagnostic); - } - - [Fact(DisplayName = "Should fix missing closure when using Context.Sender instead of this.Sender")] - public Task FailureCaseWithContextSender() - { - var before = """ - using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - - // incorrect use of closures - LocalFunction().PipeTo(Context.Sender); - } - } - """; - - var after = """ - using Akka.Actor; - using System.Threading.Tasks; - - public sealed class MyActor : UntypedActor{ - - protected override void OnReceive(object message){ - async Task LocalFunction(){ - await Task.Delay(10); - return message.ToString().Length; - } - var sender = this.Sender; - - // incorrect use of closures - LocalFunction().PipeTo(sender); - } - } - """; - - var expected = Verify.Diagnostic() - .WithSpan(13, 25, 13, 31) - .WithArguments("Context.Sender") - .WithSeverity(DiagnosticSeverity.Error); - - return Verify.VerifyCodeFix(before, after, MustCloseOverSenderWhenUsingPipeToFixer.Key_FixPipeToSender, - expected); - } -} \ No newline at end of file diff --git a/src/Akka.Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzer.cs b/src/Akka.Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzer.cs deleted file mode 100644 index c907b2f..0000000 --- a/src/Akka.Analyzers/AK1000/MustCloseOverSenderWhenUsingPipeToAnalyzer.cs +++ /dev/null @@ -1,61 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2013-2024 .NET Foundation -// -// ----------------------------------------------------------------------- - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Akka.Analyzers; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public class MustCloseOverSenderWhenUsingPipeToAnalyzer() - : AkkaDiagnosticAnalyzer(RuleDescriptors.Ak1001CloseOverSenderUsingPipeTo) -{ - public override void AnalyzeCompilation(CompilationStartAnalysisContext context, AkkaContext akkaContext) - { - Guard.AssertIsNotNull(context); - Guard.AssertIsNotNull(akkaContext); - - context.RegisterSyntaxNodeAction(ctx => - { - var invocationExpr = (InvocationExpressionSyntax)ctx.Node; - - // Check if it's a PipeTo method call - if (invocationExpr.Expression is MemberAccessExpressionSyntax - { - Name.Identifier.ValueText: "PipeTo" - } memberAccessExpr) - { - // Check if the containing type is an Akka.NET actor - var containingType = ctx.SemanticModel.GetEnclosingSymbol(invocationExpr.SpanStart)?.ContainingType; - if (containingType != null && containingType.IsActorBaseSubclass(akkaContext)) - // Check if 'this.Sender' is used in the arguments - foreach (var arg in invocationExpr.ArgumentList.Arguments) - { - var symbol = ctx.SemanticModel.GetSymbolInfo(arg.Expression).Symbol; - if (IsThisSenderSymbol(symbol, akkaContext)) - { - var diagnostic = Diagnostic.Create(RuleDescriptors.Ak1001CloseOverSenderUsingPipeTo, - memberAccessExpr.Name.GetLocation()); - ctx.ReportDiagnostic(diagnostic); - break; // Report only once per invocation - } - } - } - }, SyntaxKind.InvocationExpression); - } - - private static bool IsThisSenderSymbol(ISymbol? symbol, AkkaContext akkaContext) - { - // Check if the symbol is 'this.Sender' or 'Context.Sender' - return (symbol is { Name: "Sender", ContainingType.BaseType: not null } && - symbol.ContainingType.IsActorBaseSubclass(akkaContext)) || - (symbol is IPropertySymbol propertySymbol && - propertySymbol.Name == "Sender" && - SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingType, akkaContext.AkkaCore.ActorContextType)); - } -} \ No newline at end of file