Skip to content

Commit ccd997c

Browse files
committed
Handle more codegen cases with explicit types
1 parent e73eb24 commit ccd997c

File tree

4 files changed

+64
-23
lines changed

4 files changed

+64
-23
lines changed

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,13 +243,15 @@ public static bool TryGetAccessibilityModifiers(
243243
/// <param name="attributeData">The input <see cref="AttributeData"/> that triggered the annotation.</param>
244244
/// <param name="typeName">The type name for the generated property (without nullability annotations).</param>
245245
/// <param name="typeNameWithNullabilityAnnotations">The type name for the generated property, including nullability annotations.</param>
246-
/// <param name=",etadataTypeName">The type name for the metadata declaration of the property, if explicitly set.</param>
246+
/// <param name="metadataTypeName">The type name for the metadata declaration of the property, if explicitly set.</param>
247+
/// <param name="metadataTypeSymbol">The type symbol for the metadata declaration of the property, if explicitly set.</param>
247248
public static void GetPropertyTypes(
248249
IPropertySymbol propertySymbol,
249250
AttributeData attributeData,
250251
out string typeName,
251252
out string typeNameWithNullabilityAnnotations,
252-
out string? metadataTypeName)
253+
out string? metadataTypeName,
254+
out ITypeSymbol? metadataTypeSymbol)
253255
{
254256
// These type names are always present and directly derived from the property type
255257
typeName = propertySymbol.Type.GetFullyQualifiedName();
@@ -264,29 +266,33 @@ public static void GetPropertyTypes(
264266
if (propertyType is { Kind: TypedConstantKind.Type, IsNull: false, Value: ITypeSymbol typeSymbol })
265267
{
266268
metadataTypeName = typeSymbol.GetFullyQualifiedName();
269+
metadataTypeSymbol = typeSymbol;
267270

268271
return;
269272
}
270273
}
271274

272275
// By default, we'll just match the declared property type
273276
metadataTypeName = null;
277+
metadataTypeSymbol = null;
274278
}
275279

276280
/// <summary>
277281
/// Gets the default value to use to initialize the generated property, if explicitly specified.
278282
/// </summary>
279283
/// <param name="attributeData">The input <see cref="AttributeData"/> that triggered the annotation.</param>
280284
/// <param name="propertySymbol">The input <see cref="IPropertySymbol"/> instance.</param>
285+
/// <param name="metadataTypeSymbol">The type symbol for the metadata declaration of the property, if explicitly set.</param>
281286
/// <param name="semanticModel">The <see cref="SemanticModel"/> for the current compilation.</param>
282287
/// <param name="useWindowsUIXaml">Whether to use the UWP XAML or WinUI 3 XAML namespaces.</param>
283288
/// <param name="token">The <see cref="CancellationToken"/> used to cancel the operation, if needed.</param>
284289
/// <returns>The default value to use to initialize the generated property.</returns>
285290
public static DependencyPropertyDefaultValue GetDefaultValue(
286291
AttributeData attributeData,
287292
IPropertySymbol propertySymbol,
293+
ITypeSymbol? metadataTypeSymbol,
288294
SemanticModel semanticModel,
289-
bool useWindowsUIXaml,
295+
bool useWindowsUIXaml,
290296
CancellationToken token)
291297
{
292298
// First, check if we have a callback
@@ -355,10 +361,21 @@ public static DependencyPropertyDefaultValue GetDefaultValue(
355361
// First we need to special case non nullable values, as for those we need 'default'.
356362
if (!propertySymbol.Type.IsDefaultValueNull())
357363
{
364+
// We need special logic to handle cases where the metadata type is different. For instance,
365+
// the XAML initialization won't work if the metadata type on a property is just 'object'.
366+
ITypeSymbol effectiveMetadataTypeSymbol = metadataTypeSymbol ?? propertySymbol.Type;
367+
358368
// For non nullable types, we return 'default(T)', unless we can optimize for projected types
359369
return new DependencyPropertyDefaultValue.Default(
360370
TypeName: propertySymbol.Type.GetFullyQualifiedName(),
361-
IsProjectedType: propertySymbol.Type.IsWellKnownWinRTProjectedValueType(useWindowsUIXaml));
371+
IsProjectedType: effectiveMetadataTypeSymbol.IsWellKnownWinRTProjectedValueType(useWindowsUIXaml));
372+
}
373+
374+
// If the property type is nullable, but the metadata type is not, and it's a projected WinRT value
375+
// type (meaning that XAML would initialize it to a value), we need to explicitly set it to 'null'.
376+
if (metadataTypeSymbol?.IsWellKnownWinRTProjectedValueType(useWindowsUIXaml) is true)
377+
{
378+
return DependencyPropertyDefaultValue.ExplicitNull.Instance;
362379
}
363380

364381
// For all other ones, we can just use the 'null' placeholder again

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
9494
context.Attributes[0],
9595
out string typeName,
9696
out string typeNameWithNullabilityAnnotations,
97-
out string? metadataTypeName);
97+
out string? metadataTypeName,
98+
out ITypeSymbol? metadataTypeSymbol);
9899

99100
token.ThrowIfCancellationRequested();
100101

@@ -115,8 +116,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
115116
DependencyPropertyDefaultValue defaultValue = Execute.GetDefaultValue(
116117
context.Attributes[0],
117118
propertySymbol,
119+
metadataTypeSymbol,
118120
context.SemanticModel,
119-
useWindowsUIXaml,
121+
useWindowsUIXaml,
120122
token);
121123

122124
// The 'UnsetValue' can only be used when local caching is disabled

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Models/DependencyPropertyDefaultValue.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ public override string ToString()
2828
}
2929
}
3030

31+
/// <summary>
32+
/// A <see cref="DependencyPropertyDefaultValue"/> type representing an explicit <see langword="null"/> value.
33+
/// </summary>
34+
/// <remarks>This is used in some scenarios with mismatched metadata types.</remarks>
35+
public sealed record ExplicitNull : DependencyPropertyDefaultValue
36+
{
37+
/// <summary>
38+
/// The shared <see cref="ExplicitNull"/> instance (the type is stateless).
39+
/// </summary>
40+
public static ExplicitNull Instance { get; } = new();
41+
42+
/// <inheritdoc/>
43+
public override string ToString()
44+
{
45+
return "null";
46+
}
47+
}
48+
3149
/// <summary>
3250
/// A <see cref="DependencyPropertyDefaultValue"/> type representing default value for a specific type.
3351
/// </summary>

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_DependencyPropertyGenerator.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4400,21 +4400,25 @@ public partial bool IsSelected
44004400
}
44014401

44024402
[TestMethod]
4403-
[DataRow("bool", "null", "bool", "object")]
4404-
[DataRow("bool", "bool", "bool", "object")]
4405-
[DataRow("bool", "object", "object", "object")]
4406-
[DataRow("bool?", "null", "bool?", "object?")]
4407-
[DataRow("bool?", "bool?", "bool?", "object?")]
4408-
[DataRow("bool?", "object", "object", "object?")]
4409-
[DataRow("bool?", "bool", "bool", "object?")]
4410-
[DataRow("string?", "null", "string", "object?")]
4411-
[DataRow("string?", "string", "string", "object?")]
4412-
[DataRow("string?", "object", "object", "object?")]
4403+
[DataRow("bool", "bool", "null", "bool", "object", "null")]
4404+
[DataRow("bool", "bool", "bool", "bool", "object", "null")]
4405+
[DataRow("bool", "bool", "object", "object", "object", "new global::Windows.UI.Xaml.PropertyMetadata(default(bool))")]
4406+
[DataRow("bool?", "bool?", "null", "bool?", "object?", "null")]
4407+
[DataRow("bool?", "bool?", "bool?", "bool?", "object?", "null")]
4408+
[DataRow("bool?", "bool?", "object", "object", "object?", "null")]
4409+
[DataRow("bool?", "bool?", "bool", "bool", "object?", "new global::Windows.UI.Xaml.PropertyMetadata(null)")]
4410+
[DataRow("string?", "string?", "null", "string", "object?", "null")]
4411+
[DataRow("string?", "string?", "string", "string", "object?", "null")]
4412+
[DataRow("string?", "string?", "object", "object", "object?", "null")]
4413+
[DataRow("Visibility", "global::Windows.UI.Xaml.Visibility", "object", "object", "object", "new global::Windows.UI.Xaml.PropertyMetadata(default(global::Windows.UI.Xaml.Visibility))")]
4414+
[DataRow("Visibility?", "global::Windows.UI.Xaml.Visibility?", "object", "object", "object?", "null")]
44134415
public void SingleProperty_WithCustomMetadataType_WithNoCaching(
44144416
string declaredType,
4417+
string generatedDeclaredType,
44154418
string propertyType,
44164419
string generatedPropertyType,
4417-
string boxedType)
4420+
string boxedType,
4421+
string propertyMetadata)
44184422
{
44194423
string source = $$"""
44204424
using CommunityToolkit.WinUI;
@@ -4449,21 +4453,21 @@ partial class MyControl
44494453
name: "IsSelected",
44504454
propertyType: typeof({{generatedPropertyType}}),
44514455
ownerType: typeof(MyControl),
4452-
typeMetadata: null);
4456+
typeMetadata: {{propertyMetadata}});
44534457
44544458
/// <inheritdoc/>
44554459
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.WinUI.DependencyPropertyGenerator", <ASSEMBLY_VERSION>)]
44564460
[global::System.Diagnostics.DebuggerNonUserCode]
44574461
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
4458-
public partial {{declaredType}} IsSelected
4462+
public partial {{generatedDeclaredType}} IsSelected
44594463
{
44604464
get
44614465
{
44624466
object? __boxedValue = GetValue(IsSelectedProperty);
44634467
44644468
OnIsSelectedGet(ref __boxedValue);
44654469
4466-
{{declaredType}} __unboxedValue = ({{declaredType}})__boxedValue;
4470+
{{generatedDeclaredType}} __unboxedValue = ({{generatedDeclaredType}})__boxedValue;
44674471
44684472
OnIsSelectedGet(ref __unboxedValue);
44694473
@@ -4493,7 +4497,7 @@ public partial {{declaredType}} IsSelected
44934497
/// <param name="propertyValue">The unboxed property value that has been retrieved from <see cref="IsSelectedProperty"/>.</param>
44944498
/// <remarks>This method is invoked on the unboxed value retrieved via <see cref="GetValue"/> on <see cref="IsSelectedProperty"/>.</remarks>
44954499
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.WinUI.DependencyPropertyGenerator", <ASSEMBLY_VERSION>)]
4496-
partial void OnIsSelectedGet(ref {{declaredType}} propertyValue);
4500+
partial void OnIsSelectedGet(ref {{generatedDeclaredType}} propertyValue);
44974501
44984502
/// <summary>Executes the logic for when the <see langword="set"/> accessor <see cref="IsSelected"/> is invoked</summary>
44994503
/// <param name="propertyValue">The boxed property value that has been produced before assigning to <see cref="IsSelectedProperty"/>.</param>
@@ -4505,13 +4509,13 @@ public partial {{declaredType}} IsSelected
45054509
/// <param name="propertyValue">The property value that is being assigned to <see cref="IsSelected"/>.</param>
45064510
/// <remarks>This method is invoked on the raw value being assigned to <see cref="IsSelected"/>, before <see cref="SetValue"/> is used.</remarks>
45074511
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.WinUI.DependencyPropertyGenerator", <ASSEMBLY_VERSION>)]
4508-
partial void OnIsSelectedSet(ref {{declaredType}} propertyValue);
4512+
partial void OnIsSelectedSet(ref {{generatedDeclaredType}} propertyValue);
45094513
45104514
/// <summary>Executes the logic for when <see cref="IsSelected"/> has just changed.</summary>
45114515
/// <param name="value">The new property value that has been set.</param>
45124516
/// <remarks>This method is invoked right after the value of <see cref="IsSelected"/> is changed.</remarks>
45134517
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.WinUI.DependencyPropertyGenerator", <ASSEMBLY_VERSION>)]
4514-
partial void OnIsSelectedChanged({{declaredType}} newValue);
4518+
partial void OnIsSelectedChanged({{generatedDeclaredType}} newValue);
45154519
45164520
/// <summary>Executes the logic for when <see cref="IsSelected"/> has just changed.</summary>
45174521
/// <param name="e">Event data that is issued by any event that tracks changes to the effective value of this property.</param>

0 commit comments

Comments
 (0)