Skip to content

Commit efde5ee

Browse files
committed
Add new nullable diagnostics to analyzer
1 parent c37fb2f commit efde5ee

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/AnalyzerReleases.Shipped.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ WCTDP0020 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenera
3030
WCTDP0021 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
3131
WCTDP0022 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
3232
WCTDP0023 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Error |
33+
WCTDP0024 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
34+
WCTDP0025 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/InvalidPropertyNullableAnnotationAnalyzer.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ namespace CommunityToolkit.GeneratedDependencyProperty;
1919
public sealed class InvalidPropertyNullableAnnotationAnalyzer : DiagnosticAnalyzer
2020
{
2121
/// <inheritdoc/>
22-
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [NonNullablePropertyDeclarationIsNotEnforced];
22+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
23+
[
24+
NonNullablePropertyDeclarationIsNotEnforced,
25+
NotNullResilientAccessorsForNullablePropertyDeclaration,
26+
NotNullResilientAccessorsForNullablePropertyDeclaration
27+
];
2328

2429
/// <inheritdoc/>
2530
public override void Initialize(AnalysisContext context)
@@ -65,13 +70,14 @@ public override void Initialize(AnalysisContext context)
6570
// If we have '[NotNull]', it means the property getter must always ensure that a non-null value is returned.
6671
// This can be achieved in two different ways:
6772
// 1) By implementing one of the 'On___Get' methods, and adding '[NotNull]' on the parameter.
68-
// 2) By having '[DisallowNull]' on the property, and either marking the property as required, or providing a non-null default value.
73+
// 2) By having '[DisallowNull]' on the property or implementing one of the 'On___Set' methods with '[NotNull]'
74+
// on the parameter, and either marking the property as required, or providing a non-null default value.
6975
if (!IsAccessorMethodMarkedAsNotNull(propertySymbol, SyntaxKind.GetAccessorDeclaration, notNullAttributeSymbols) &&
70-
!(propertySymbol.HasAttributeWithAnyType(disallowNullAttributeSymbols) &&
71-
(propertySymbol.IsRequired || IsDefaultValueNotNull(propertySymbol, attributeData, maybeNullAttributeSymbols, notNullAttributeSymbols))))
76+
!((propertySymbol.HasAttributeWithAnyType(disallowNullAttributeSymbols) || IsAccessorMethodMarkedAsNotNull(propertySymbol, SyntaxKind.SetAccessorDeclaration, notNullAttributeSymbols)) &&
77+
(propertySymbol.IsRequired || IsDefaultValueNotNull(propertySymbol, attributeData, maybeNullAttributeSymbols, notNullAttributeSymbols))))
7278
{
7379
context.ReportDiagnostic(Diagnostic.Create(
74-
NonNullablePropertyDeclarationIsNotEnforced,
80+
NotNullResilientAccessorsForNullablePropertyDeclaration,
7581
attributeData.GetLocation(),
7682
propertySymbol));
7783
}
@@ -93,7 +99,7 @@ public override void Initialize(AnalysisContext context)
9399
!IsAccessorMethodMarkedAsNotNull(propertySymbol, SyntaxKind.SetAccessorDeclaration, notNullAttributeSymbols))
94100
{
95101
context.ReportDiagnostic(Diagnostic.Create(
96-
NonNullablePropertyDeclarationIsNotEnforced,
102+
NotNullResilientAccessorsForNullablePropertyDeclaration,
97103
attributeData.GetLocation(),
98104
propertySymbol));
99105
}

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,30 @@ internal static class DiagnosticDescriptors
324324
isEnabledByDefault: true,
325325
description: "Properties annotated with [GeneratedDependencyProperty] and setting 'PropertyType' must do so with a type that is compatible with the declared property type.",
326326
helpLinkUri: "https://aka.ms/toolkit/labs/windows");
327+
328+
/// <summary>
329+
/// <c>The property '{0}' is not annotated as nullable and is using [AllowNull], but neither of its accessors are null-resilient (at least one generated 'On___Get' or 'On___Set' method must be implemented with [NotNull] on the 'propertyValue' parameter, to ensure assigning null values does not break the nullable annotations on the property)</c>.
330+
/// </summary>
331+
public static readonly DiagnosticDescriptor NotNullResilientAccessorsForNotNullablePropertyDeclaration = new(
332+
id: "WCTDP0024",
333+
title: "Non-nullable dependency property using [AllowNull] incorrectly",
334+
messageFormat: "The property '{0}' is not annotated as nullable and is using [AllowNull], but neither of its accessors are null-resilient (at least one generated 'On___Get' or 'On___Set' method must be implemented with [NotNull] on the 'propertyValue' parameter, to ensure assigning null values does not break the nullable annotations on the property)",
335+
category: typeof(DependencyPropertyGenerator).FullName,
336+
defaultSeverity: DiagnosticSeverity.Warning,
337+
isEnabledByDefault: true,
338+
description: "Non-nullable properties annotated with [GeneratedDependencyProperty] using [AllowNull] should have at least one generated 'On___Get' or 'On___Set' method implemented as null-resilient (by adding [NotNull] on the 'propertyValue' parameter) to ensure assigning null values does not break the nullable annotations on the property.",
339+
helpLinkUri: "https://aka.ms/toolkit/labs/windows");
340+
341+
/// <summary>
342+
/// <c>The property '{0}' is annotated as nullable and is using [NotNull], but it's not guaranteeing that returned values will not be null (it must either make its 'get' accessor null-resilient, by implementing at least one generated 'On___Get' method with [NotNull] on the 'propertyValue' parameter, or it must either add [DisallowNull] or implement at least one generated 'On___Set' method with [NotNull], and also either mark the property as required, or ensure that its default value is not null)</c>.
343+
/// </summary>
344+
public static readonly DiagnosticDescriptor NotNullResilientAccessorsForNullablePropertyDeclaration = new(
345+
id: "WCTDP0025",
346+
title: "Nullable dependency property using [NotNull] incorrectly",
347+
messageFormat: "The property '{0}' is annotated as nullable and is using [NotNull], but it's not guaranteeing that returned values will not be null (it must either make its 'get' accessor null-resilient, by implementing at least one generated 'On___Get' method with [NotNull] on the 'propertyValue' parameter, or it must either add [DisallowNull] or implement at least one generated 'On___Set' method with [NotNull], and also either mark the property as required, or ensure that its default value is not null)",
348+
category: typeof(DependencyPropertyGenerator).FullName,
349+
defaultSeverity: DiagnosticSeverity.Warning,
350+
isEnabledByDefault: true,
351+
description: "Nullable properties annotated with [GeneratedDependencyProperty] using [NotNull] should make their 'get' accessors null-resilient, by implementing at least one generated 'On___Get' method with [NotNull] on the 'propertyValue' parameter, or they must either add [DisallowNull] or implement at least one generated 'On___Set' method with [NotNull], and also either be marked as required properties, or ensure that the default value is not null.",
352+
helpLinkUri: "https://aka.ms/toolkit/labs/windows");
327353
}

0 commit comments

Comments
 (0)