Skip to content

Commit c684646

Browse files
committed
Handle nullability in new hooks with previous value
1 parent f07bb20 commit c684646

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/PropertyInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
1818
/// <param name="NotifyPropertyChangedRecipients">Whether or not the generated property also broadcasts changes.</param>
1919
/// <param name="NotifyDataErrorInfo">Whether or not the generated property also validates its value.</param>
2020
/// <param name="IsOldPropertyValueDirectlyReferenced">Whether the old property value is being directly referenced.</param>
21+
/// <param name="IsReferenceType">Indicates whether the property is of a reference type.</param>
2122
/// <param name="ForwardedAttributes">The sequence of forwarded attributes for the generated property.</param>
2223
internal sealed record PropertyInfo(
2324
string TypeNameWithNullabilityAnnotations,
@@ -29,4 +30,5 @@ internal sealed record PropertyInfo(
2930
bool NotifyPropertyChangedRecipients,
3031
bool NotifyDataErrorInfo,
3132
bool IsOldPropertyValueDirectlyReferenced,
33+
bool IsReferenceType,
3234
EquatableArray<AttributeInfo> ForwardedAttributes);

src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public static bool TryGetInfo(
112112
bool hasOrInheritsClassLevelNotifyDataErrorInfo = false;
113113
bool hasAnyValidationAttributes = false;
114114
bool isOldPropertyValueDirectlyReferenced = IsOldPropertyValueDirectlyReferenced(fieldSymbol, propertyName);
115+
bool isReferenceType = fieldSymbol.Type.IsReferenceType;
115116

116117
// Track the property changing event for the property, if the type supports it
117118
if (shouldInvokeOnPropertyChanging)
@@ -265,6 +266,7 @@ public static bool TryGetInfo(
265266
notifyRecipients,
266267
notifyDataErrorInfo,
267268
isOldPropertyValueDirectlyReferenced,
269+
isReferenceType,
268270
forwardedAttributes.ToImmutable());
269271

270272
diagnostics = builder.ToImmutable();
@@ -950,6 +952,18 @@ public static ImmutableArray<MemberDeclarationSyntax> GetOnPropertyChangeMethods
950952
Comment($"/// <remarks>This method is invoked right before the value of <see cref=\"{propertyInfo.PropertyName}\"/> is changed.</remarks>")), SyntaxKind.OpenBracketToken, TriviaList())))
951953
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
952954

955+
// Prepare the nullable type for the previous property value. This is needed because if the type is a reference
956+
// type, the previous value might be null even if the property type is not nullable, as the first invocation would
957+
// happen when the property is first set to some value that is not null (but the backing field would still be so).
958+
// As a cheap way to check whether we need to add nullable, we can simply check whether the type name with nullability
959+
// annotations ends with a '?'. If it doesn't and the type is a reference type, we add it. Otherwise, we keep it.
960+
TypeSyntax oldValueTypeSyntax = propertyInfo.IsReferenceType switch
961+
{
962+
true when !propertyInfo.TypeNameWithNullabilityAnnotations.EndsWith("?")
963+
=> IdentifierName($"{propertyInfo.TypeNameWithNullabilityAnnotations}?"),
964+
_ => parameterType
965+
};
966+
953967
// Construct the generated method as follows:
954968
//
955969
// /// <summary>Executes the logic for when <see cref="<PROPERTY_NAME>"/> is changing.</summary>
@@ -962,7 +976,7 @@ public static ImmutableArray<MemberDeclarationSyntax> GetOnPropertyChangeMethods
962976
MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier($"On{propertyInfo.PropertyName}Changing"))
963977
.AddModifiers(Token(SyntaxKind.PartialKeyword))
964978
.AddParameterListParameters(
965-
Parameter(Identifier("oldValue")).WithType(parameterType),
979+
Parameter(Identifier("oldValue")).WithType(oldValueTypeSyntax),
966980
Parameter(Identifier("newValue")).WithType(parameterType))
967981
.AddAttributeLists(
968982
AttributeList(SingletonSeparatedList(
@@ -1012,7 +1026,7 @@ public static ImmutableArray<MemberDeclarationSyntax> GetOnPropertyChangeMethods
10121026
MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier($"On{propertyInfo.PropertyName}Changed"))
10131027
.AddModifiers(Token(SyntaxKind.PartialKeyword))
10141028
.AddParameterListParameters(
1015-
Parameter(Identifier("oldValue")).WithType(parameterType),
1029+
Parameter(Identifier("oldValue")).WithType(oldValueTypeSyntax),
10161030
Parameter(Identifier("newValue")).WithType(parameterType))
10171031
.AddAttributeLists(
10181032
AttributeList(SingletonSeparatedList(

0 commit comments

Comments
 (0)