Skip to content

Commit 9d6768f

Browse files
committed
WPF0012 Allow accessor property to be nullable
Fix #293
1 parent 4e7a8a6 commit 9d6768f

3 files changed

Lines changed: 72 additions & 6 deletions

File tree

ValidCode/Repro/Issue293.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace ValidCode.Repro;
2+
3+
using System.Drawing;
4+
using System.Windows;
5+
6+
public class Issue293 : FrameworkElement
7+
{
8+
/// <summary>Identifies the <see cref="Background"/> dependency property.</summary>
9+
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
10+
nameof(Background),
11+
typeof(Brush),
12+
typeof(Issue293),
13+
new FrameworkPropertyMetadata(
14+
Brushes.Transparent,
15+
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
16+
17+
public Brush? Background
18+
{
19+
get => (Brush?)this.GetValue(BackgroundProperty);
20+
set => this.SetValue(BackgroundProperty, value);
21+
}
22+
}

WpfAnalyzers.Test/WPF0012ClrPropertyShouldMatchRegisteredTypeTests/Valid.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,34 @@ public enum FooEnum
291291
}";
292292
RoslynAssert.Valid(Analyzer, fooCode, code, enumCode);
293293
}
294+
295+
[Test]
296+
public static void AllowNullableAccessorIssue293()
297+
{
298+
var code = @"
299+
namespace N;
300+
301+
using System.Drawing;
302+
using System.Windows;
303+
304+
public class C : FrameworkElement
305+
{
306+
/// <summary>Identifies the <see cref=""Background""/> dependency property.</summary>
307+
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
308+
nameof(Background),
309+
typeof(Brush),
310+
typeof(C),
311+
new FrameworkPropertyMetadata(
312+
Brushes.Transparent,
313+
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
314+
315+
public Brush? Background
316+
{
317+
get => (Brush?)this.GetValue(BackgroundProperty);
318+
set => this.SetValue(BackgroundProperty, value);
319+
}
320+
}";
321+
322+
RoslynAssert.Valid(Analyzer, code);
323+
}
294324
}

WpfAnalyzers/Analyzers/ClrPropertyDeclarationAnalyzer.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,13 @@ context.Node is PropertyDeclarationSyntax propertyDeclaration &&
3636
{
3737
if (ClrProperty.Match(property, context.SemanticModel, context.CancellationToken) is { SetValue: { } setValue, BackingSet: { } backingSet, GetValue: { } getValue, BackingGet: { } backingGet })
3838
{
39-
if (getter.Body is { Statements: { } getStatements } &&
40-
getStatements.Count > 1 &&
39+
if (getter.Body is { Statements: { Count: > 1 } getStatements } &&
4140
getStatements.TryFirst(x => !x.Contains(getValue), out var statement))
4241
{
4342
context.ReportDiagnostic(Diagnostic.Create(Descriptors.WPF0036AvoidSideEffectsInClrAccessors, statement.GetLocation()));
4443
}
4544

46-
if (setter.Body is { Statements: { } setStatements } &&
47-
setStatements.Count > 1 &&
45+
if (setter.Body is { Statements: { Count: > 1 } setStatements } &&
4846
setStatements.TryFirst(x => !x.Contains(setValue), out var sideEffect))
4947
{
5048
context.ReportDiagnostic(Diagnostic.Create(Descriptors.WPF0036AvoidSideEffectsInClrAccessors, sideEffect.GetLocation()));
@@ -73,7 +71,7 @@ context.Node is PropertyDeclarationSyntax propertyDeclaration &&
7371

7472
if (backingGet.RegisteredType(context.SemanticModel, context.CancellationToken) is { Value: { } registeredType })
7573
{
76-
if (!TypeSymbolComparer.Equal(property.Type, registeredType))
74+
if (!MatchesRegisteredType(property.Type, registeredType))
7775
{
7876
context.ReportDiagnostic(
7977
Diagnostic.Create(
@@ -85,7 +83,7 @@ context.Node is PropertyDeclarationSyntax propertyDeclaration &&
8583
}
8684
else if (getValue is { Parent: CastExpressionSyntax { Type: { } type } } &&
8785
context.SemanticModel.GetType(type, context.CancellationToken) is { } castType &&
88-
!TypeSymbolComparer.Equal(registeredType, castType))
86+
!MatchesRegisteredType(castType, registeredType))
8987
{
9088
context.ReportDiagnostic(
9189
Diagnostic.Create(
@@ -95,6 +93,22 @@ context.Node is PropertyDeclarationSyntax propertyDeclaration &&
9593
property,
9694
registeredType));
9795
}
96+
97+
static bool MatchesRegisteredType(ITypeSymbol candidate, ITypeSymbol registeredType)
98+
{
99+
if (TypeSymbolComparer.Equal(candidate, registeredType))
100+
{
101+
return true;
102+
}
103+
104+
if (candidate.NullableAnnotation == NullableAnnotation.Annotated &&
105+
registeredType.NullableAnnotation == NullableAnnotation.NotAnnotated)
106+
{
107+
return TypeSymbolComparer.Equal(candidate.OriginalDefinition, registeredType);
108+
}
109+
110+
return false;
111+
}
98112
}
99113

100114
bool IsGettingAndSettingDifferent()

0 commit comments

Comments
 (0)