File tree Expand file tree Collapse file tree
ErrorProne.NET.CoreAnalyzers.Tests
ErrorProne.NET.CoreAnalyzers Expand file tree Collapse file tree Original file line number Diff line number Diff line change 11using NUnit . Framework ;
22using System . Threading . Tasks ;
3- using ErrorProne . NET . CoreAnalyzers ;
43using Verify = ErrorProne . NET . TestHelpers . CSharpCodeFixVerifier <
54 ErrorProne . NET . CoreAnalyzers . RecursiveCallAnalyzer ,
65 Microsoft . CodeAnalysis . Testing . EmptyCodeFixProvider > ;
@@ -32,6 +31,48 @@ void Foo(bool b) {
3231 if (b) [|Foo(b)|];
3332 }
3433}
34+ " ;
35+ await Verify . VerifyAsync ( test ) ;
36+ }
37+
38+ [ Test ]
39+ public async Task WarnsOnConditionalRecursiveCall_With_Named_Parameters ( )
40+ {
41+ var test = @"
42+ class C {
43+ void Foo(bool b) {
44+ if (b) [|Foo(b: b)|];
45+ }
46+ }
47+ " ;
48+ await Verify . VerifyAsync ( test ) ;
49+ }
50+
51+ [ Test ]
52+ public async Task NoWarn_When_Different_Argument_Is_Passed ( )
53+ {
54+ var test = @"
55+ class C {
56+ void Foo(bool b) {
57+ if (b) Foo(false);
58+ }
59+ }
60+ " ;
61+ await Verify . VerifyAsync ( test ) ;
62+ }
63+
64+ [ Test ]
65+ public async Task NoWarn_For_Factorial ( )
66+ {
67+ var test = @"
68+ class C {
69+ int Factorial(int n)
70+ {
71+ if (n <= 1)
72+ return 1; // Base case
73+ return n * Factorial(n - 1); // Recursive call with changing argument
74+ }
75+ }
3576" ;
3677 await Verify . VerifyAsync ( test ) ;
3778 }
Original file line number Diff line number Diff line change @@ -26,7 +26,19 @@ private static void AnalyzeMethodBody(OperationAnalysisContext context)
2626 var methodBody = ( IMethodBodyOperation ) context . Operation ;
2727 foreach ( var invocation in methodBody . Descendants ( ) . OfType < IInvocationOperation > ( ) )
2828 {
29- if ( SymbolEqualityComparer . Default . Equals ( invocation . TargetMethod . OriginalDefinition , method . OriginalDefinition ) )
29+ // Check if all parameters are passed as-is
30+ // So Factorial(n - 1) should be totally fine!
31+ if ( invocation . Arguments . Length == method . Parameters . Length &&
32+ // Checking that the method is the same.
33+ SymbolEqualityComparer . Default . Equals ( invocation . TargetMethod . OriginalDefinition , method . OriginalDefinition ) &&
34+
35+ // Checking if the parameters are passed as is.
36+ // It is possible to have a false positive here if the parameters are mutable.
37+ // But it is a very rare case, so we will ignore it for now.
38+ invocation . Arguments . Zip ( method . Parameters , ( arg , param ) =>
39+ arg . Value is IParameterReferenceOperation paramRef &&
40+ SymbolEqualityComparer . Default . Equals ( paramRef . Parameter , param )
41+ ) . All ( b => b ) )
3042 {
3143 context . ReportDiagnostic ( Diagnostic . Create (
3244 DiagnosticDescriptors . EPC30 ,
You can’t perform that action at this time.
0 commit comments