Skip to content

Commit 019332e

Browse files
jnm2JohanLarsson
authored andcommitted
Fix false recursion detection
1 parent 173996a commit 019332e

3 files changed

Lines changed: 133 additions & 1 deletion

File tree

PropertyChangedAnalyzers.Test/INPC015PropertyIsRecursive/Valid.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,5 +225,72 @@ protected bool TrySet<T>(ref T field, T value, [CallerMemberName] string propert
225225

226226
RoslynAssert.Valid(Analyzer, withProperties, code);
227227
}
228+
229+
[Test]
230+
public static void SameExpressionWithDifferentMeanings()
231+
{
232+
var code = @"
233+
namespace ValidCode.Recursion
234+
{
235+
using System;
236+
using System.ComponentModel;
237+
using System.Runtime.CompilerServices;
238+
239+
public class NotRecursion : INotifyPropertyChanged
240+
{
241+
public event PropertyChangedEventHandler? PropertyChanged;
242+
243+
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
244+
{
245+
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
246+
}
247+
248+
protected bool Set<T>(ref T location, T value, [CallerMemberName] string? propertyName = null)
249+
{
250+
if (RuntimeHelpers.Equals(location, value)) return false;
251+
location = value;
252+
OnPropertyChanged(propertyName);
253+
return true;
254+
}
255+
256+
record DifferentClass
257+
{
258+
public int P;
259+
}
260+
261+
private int p;
262+
public int P
263+
{
264+
get => p;
265+
set
266+
{
267+
if (!Set(ref p, value)) return;
268+
269+
_ = new DifferentClass { P = value };
270+
_ = new DifferentClass() with { P = value };
271+
272+
{
273+
int P;
274+
P = value;
275+
}
276+
277+
_ = new Action<int>(P =>
278+
{
279+
P = value;
280+
});
281+
282+
LocalFunction(42);
283+
void LocalFunction(int P)
284+
{
285+
P = value;
286+
}
287+
}
288+
}
289+
}
290+
}
291+
";
292+
293+
RoslynAssert.Valid(Analyzer, code);
294+
}
228295
}
229296
}

PropertyChangedAnalyzers/Analyzers/PropertyDeclarationAnalyzer.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,14 @@ context.Node is PropertyDeclarationSyntax propertyDeclaration &&
8282
}
8383

8484
using var assignmentWalker = AssignmentWalker.Borrow(setter);
85-
if (assignmentWalker.Assignments.TryFirst(x => IsProperty(x.Left) && !x.Parent.IsKind(SyntaxKind.ObjectInitializerExpression), out var recursiveAssignment))
85+
if (assignmentWalker.Assignments.TryFirst(
86+
x =>
87+
IsProperty(x.Left) &&
88+
!x.Parent.IsKind(SyntaxKind.ObjectInitializerExpression)
89+
&& SymbolEqualityComparer.Default.Equals(
90+
context.SemanticModel.GetDeclaredSymbolSafe(propertyDeclaration, context.CancellationToken),
91+
context.SemanticModel.GetSymbolSafe(x.Left, context.CancellationToken)),
92+
out var recursiveAssignment))
8693
{
8794
context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC015PropertyIsRecursive, recursiveAssignment.Left.GetLocation(), "Setter assigns property, infinite recursion"));
8895
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
namespace ValidCode.Recursion
2+
{
3+
using System;
4+
using System.ComponentModel;
5+
using System.Runtime.CompilerServices;
6+
7+
public class NotRecursion : INotifyPropertyChanged
8+
{
9+
public event PropertyChangedEventHandler? PropertyChanged;
10+
11+
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
12+
{
13+
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
14+
}
15+
16+
protected bool Set<T>(ref T location, T value, [CallerMemberName] string? propertyName = null)
17+
{
18+
if (RuntimeHelpers.Equals(location, value)) return false;
19+
location = value;
20+
OnPropertyChanged(propertyName);
21+
return true;
22+
}
23+
24+
record DifferentClass
25+
{
26+
public int P;
27+
}
28+
29+
private int p;
30+
public int P
31+
{
32+
get => p;
33+
set
34+
{
35+
if (!Set(ref p, value)) return;
36+
37+
_ = new DifferentClass { P = value };
38+
_ = new DifferentClass() with { P = value };
39+
40+
{
41+
int P;
42+
P = value;
43+
}
44+
45+
_ = new Action<int>(P =>
46+
{
47+
P = value;
48+
});
49+
50+
LocalFunction(42);
51+
void LocalFunction(int P)
52+
{
53+
P = value;
54+
}
55+
}
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)