From 23acb10fcbd0c663a4d87348caa942a749a251b0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 4 Jan 2025 02:46:19 +0100 Subject: [PATCH] Fix more edge cases in code fixer --- ...endencyPropertyOnManualPropertyAnalyzer.cs | 31 ++++++++++++++++--- ...ndencyPropertyOnManualPropertyCodeFixer.cs | 4 +-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/UseGeneratedDependencyPropertyOnManualPropertyAnalyzer.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/UseGeneratedDependencyPropertyOnManualPropertyAnalyzer.cs index fa4c203f..00e1db00 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/UseGeneratedDependencyPropertyOnManualPropertyAnalyzer.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/UseGeneratedDependencyPropertyOnManualPropertyAnalyzer.cs @@ -502,8 +502,18 @@ void HandleSetAccessor(IPropertySymbol propertySymbol, PropertyFlags propertyFla continue; } - // Execute the deferred default value validation, if necessary - if (fieldFlags.DefaultValueOperation is not null) + // Execute the deferred default value validation, if necessary. If we have an operation + // here, it means we had some constructed property metadata. Otherwise, it was 'null'. + if (fieldFlags.DefaultValueOperation is null) + { + // Special case: the whole property metadata is 'null', and the metadata type is nullable, but + // the property type isn't. In this case, we need to ensure the explicit 'null' is preserved. + if (fieldFlags.IsExplicitConversionFromNonNullableToNullableMetdataType(pair.Key.Type)) + { + fieldFlags.DefaultValue = TypedConstantInfo.Null.Instance; + } + } + else { bool isNullableValueType = pair.Key.Type.IsNullableValueType(); @@ -523,9 +533,7 @@ void HandleSetAccessor(IPropertySymbol propertySymbol, PropertyFlags propertyFla { fieldFlags.DefaultValue = TypedConstantInfo.Null.Instance; } - else if (!SymbolEqualityComparer.Default.Equals(pair.Key.Type, fieldFlags.PropertyType) && - !pair.Key.Type.IsNullableValueType() && - fieldFlags.PropertyType!.IsDefaultValueNull()) + else if (fieldFlags.IsExplicitConversionFromNonNullableToNullableMetdataType(pair.Key.Type)) { // Special case: the property type is not nullable, but the property metadata type is explicitly declared as // a nullable type, and the default value is set to 'null'. In this case, we need to preserve this value. @@ -773,6 +781,19 @@ private sealed class FieldFlags /// The location of the target field being initialized. /// public Location? FieldLocation; + + /// + /// Checks whether the field has an explicit conversion to a nullable metadata type (where the property isn't). + /// + /// The property type. + /// Whether the field has an explicit conversion to a nullable metadata type + public bool IsExplicitConversionFromNonNullableToNullableMetdataType(ITypeSymbol propertyType) + { + return + !SymbolEqualityComparer.Default.Equals(propertyType, PropertyType) && + !propertyType.IsDefaultValueNull() && + PropertyType!.IsDefaultValueNull(); + } } } diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer.cs index 3eb889f9..29e18c0a 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer.cs @@ -2448,7 +2448,7 @@ public abstract partial class KeyFrame : DependencyObject [DataRow("Visibility", "Visibility", "new PropertyMetadata(default(Visibility))", "")] [DataRow("Visibility", "Visibility", "new PropertyMetadata(Visibility.Visible)", "")] [DataRow("Visibility", "Visibility", "new PropertyMetadata(Visibility.Collapsed)", "(DefaultValue = Visibility.Collapsed)")] - //[DataRow("Visibility", "object", "null", "(PropertyType = typeof(object), DefaultValue = null)")] + [DataRow("Visibility", "object", "null", "(PropertyType = typeof(object), DefaultValue = null)")] [DataRow("Visibility", "object", "new PropertyMetadata(null)", "(PropertyType = typeof(object), DefaultValue = null)")] [DataRow("Visibility", "object", "new PropertyMetadata(default(Visibility))", "(PropertyType = typeof(object))")] [DataRow("Visibility", "object", "new PropertyMetadata(Visibility.Visible)", "(PropertyType = typeof(object))")] @@ -2469,7 +2469,7 @@ public abstract partial class KeyFrame : DependencyObject [DataRow("MyEnum", "MyEnum", "new PropertyMetadata(default(MyEnum))", "")] [DataRow("MyEnum", "MyEnum", "new PropertyMetadata(MyEnum.A)", "")] [DataRow("MyEnum", "MyEnum", "new PropertyMetadata(MyEnum.B)", "(DefaultValue = MyEnum.B)")] - //[DataRow("MyEnum", "object", "null", "(PropertyType = typeof(object), DefaultValue = null)")] + [DataRow("MyEnum", "object", "null", "(PropertyType = typeof(object), DefaultValue = null)")] [DataRow("MyEnum", "object", "new PropertyMetadata(null)", "(PropertyType = typeof(object), DefaultValue = null)")] [DataRow("MyEnum", "object", "new PropertyMetadata(default(MyEnum))", "(PropertyType = typeof(object))")] [DataRow("MyEnum", "object", "new PropertyMetadata(MyEnum.A)", "(PropertyType = typeof(object))")]