From eb41e2869d1e858cd6fd0b73400fd08720e101c3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 5 Dec 2024 19:27:46 -0800 Subject: [PATCH] Add support for more modifiers, bug fixes --- .../DependencyPropertyGenerator.Execute.cs | 59 ++++++++++++++----- .../DependencyPropertyGenerator.cs | 9 ++- .../Extensions/AccessibilityExtensions.cs | 32 ---------- .../Models/DependencyPropertyInfo.cs | 5 +- 4 files changed, 55 insertions(+), 50 deletions(-) delete mode 100644 components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Extensions/AccessibilityExtensions.cs diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs index b94b1e752..3501de65c 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -147,6 +148,37 @@ public static bool IsCandidateSymbolValid(IPropertySymbol propertySymbol, bool u return true; } + /// + /// Gathers all allowed property modifiers that should be forwarded to the generated property. + /// + /// The input node. + /// The returned set of property modifiers, if any. + public static ImmutableArray GetPropertyModifiers(PropertyDeclarationSyntax node) + { + // We only allow a subset of all possible modifiers (aside from the accessibility modifiers) + ReadOnlySpan candidateKinds = + [ + SyntaxKind.NewKeyword, + SyntaxKind.VirtualKeyword, + SyntaxKind.SealedKeyword, + SyntaxKind.OverrideKeyword, + SyntaxKind.RequiredKeyword + ]; + + using ImmutableArrayBuilder builder = new(); + + // Track all modifiers from the allowed set on the input property declaration + foreach (SyntaxKind kind in candidateKinds) + { + if (node.Modifiers.Any(kind)) + { + builder.Add(kind); + } + } + + return builder.ToImmutable(); + } + /// /// Tries to get the accessibility of the property and accessors, if possible. /// @@ -331,16 +363,6 @@ public static bool IsSharedPropertyChangedCallbackImplemented(IPropertySymbol pr return false; } - /// - /// Checks whether an input property is required. - /// - /// The input instance to process. - /// Whether is required. - public static bool IsRequiredProperty(IPropertySymbol propertySymbol) - { - return propertySymbol.IsRequired; - } - /// /// Writes all implementations of partial dependency property declarations. /// @@ -367,7 +389,7 @@ static string GetOldValueTypeNameAsNullable(DependencyPropertyInfo propertyInfo) // Helper to get the accessibility with a trailing space static string GetExpressionWithTrailingSpace(Accessibility accessibility) { - return accessibility.GetExpression() switch + return SyntaxFacts.GetText(accessibility) switch { { Length: > 0 } expression => expression + " ", _ => "" @@ -428,11 +450,20 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility) { string oldValueTypeNameAsNullable = GetOldValueTypeNameAsNullable(propertyInfo); + // Declare the property writer.WriteLine(skipIfPresent: true); writer.WriteLine("/// "); writer.WriteGeneratedAttributes(GeneratorName); writer.Write(GetExpressionWithTrailingSpace(propertyInfo.DeclaredAccessibility)); - writer.WriteIf(propertyInfo.IsRequired, "required "); + + // Add all gathered modifiers + foreach (SyntaxKind modifier in propertyInfo.PropertyModifiers.AsImmutableArray().AsSyntaxKindArray()) + { + writer.Write($"{SyntaxFacts.GetText(modifier)} "); + } + + // The 'partial' modifier always goes last, right before the property type and the property name. + // We will never have the 'partial' modifier in the set of property modifiers processed above. writer.WriteLine($"partial {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.PropertyName}"); using (writer.WriteBlock()) @@ -653,7 +684,7 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility) /// This method is invoked by the infrastructure, after the value of is changed. """, isMultiline: true); writer.WriteGeneratedAttributes(GeneratorName, includeNonUserCodeAttributes: false); - writer.WriteLine($"partial void On{propertyInfo.PropertyName}PropertyChanged(global::{WellKnownTypeNames.DependencyPropertyChangedEventArgs} e);"); + writer.WriteLine($"partial void On{propertyInfo.PropertyName}PropertyChanged(global::{WellKnownTypeNames.DependencyPropertyChangedEventArgs(propertyInfo.UseWindowsUIXaml)} e);"); } // OnPropertyChanged, for the shared property metadata callback @@ -664,7 +695,7 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility) /// This method is invoked by the infrastructure, after the value of any dependency property has just changed. """, isMultiline: true); writer.WriteGeneratedAttributes(GeneratorName, includeNonUserCodeAttributes: false); - writer.WriteLine($"partial void OnPropertyChanged(global::{WellKnownTypeNames.DependencyPropertyChangedEventArgs} e);"); + writer.WriteLine($"partial void OnPropertyChanged(global::{WellKnownTypeNames.DependencyPropertyChangedEventArgs(propertyInfos[0].UseWindowsUIXaml)} e);"); } /// diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs index 2e21759c1..ff70652e7 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using CommunityToolkit.GeneratedDependencyProperty.Constants; using CommunityToolkit.GeneratedDependencyProperty.Extensions; using CommunityToolkit.GeneratedDependencyProperty.Helpers; @@ -70,6 +71,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) token.ThrowIfCancellationRequested(); + // Get all additional modifiers for the property + ImmutableArray propertyModifiers = Execute.GetPropertyModifiers((PropertyDeclarationSyntax)context.TargetNode); + + token.ThrowIfCancellationRequested(); + // Get the accessibility values, if the property is valid if (!Execute.TryGetAccessibilityModifiers( node: (PropertyDeclarationSyntax)context.TargetNode, @@ -88,7 +94,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) token.ThrowIfCancellationRequested(); - bool isRequired = Execute.IsRequiredProperty(propertySymbol); bool isPropertyChangedCallbackImplemented = Execute.IsPropertyChangedCallbackImplemented(propertySymbol, useWindowsUIXaml); bool isSharedPropertyChangedCallbackImplemented = Execute.IsSharedPropertyChangedCallbackImplemented(propertySymbol, useWindowsUIXaml); bool isNet8OrGreater = !context.SemanticModel.Compilation.IsWindowsRuntimeApplication(); @@ -124,6 +129,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new DependencyPropertyInfo( Hierarchy: hierarchyInfo, PropertyName: propertySymbol.Name, + PropertyModifiers: propertyModifiers.AsUnderlyingType(), DeclaredAccessibility: declaredAccessibility, GetterAccessibility: getterAccessibility, SetterAccessibility: setterAccessibility, @@ -131,7 +137,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) TypeNameWithNullabilityAnnotations: typeNameWithNullabilityAnnotations, DefaultValue: defaultValue, IsReferenceTypeOrUnconstraindTypeParameter: isReferenceTypeOrUnconstraindTypeParameter, - IsRequired: isRequired, IsLocalCachingEnabled: isLocalCachingEnabled, IsPropertyChangedCallbackImplemented: isPropertyChangedCallbackImplemented, IsSharedPropertyChangedCallbackImplemented: isSharedPropertyChangedCallbackImplemented, diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Extensions/AccessibilityExtensions.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Extensions/AccessibilityExtensions.cs deleted file mode 100644 index f7154e446..000000000 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Extensions/AccessibilityExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis; - -namespace CommunityToolkit.GeneratedDependencyProperty.Extensions; - -/// -/// Extension methods for the type. -/// -internal static class AccessibilityExtensions -{ - /// - /// Gets the expression for a given value. - /// - /// The input value. - /// The expression for . - public static string GetExpression(this Accessibility accessibility) - { - return accessibility switch - { - Accessibility.Private => "private", - Accessibility.ProtectedAndInternal => "private protected", - Accessibility.Protected => "protected", - Accessibility.Internal => "internal", - Accessibility.ProtectedOrInternal => "protected internal", - Accessibility.Public => "public", - _ => "" - }; - } -} diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Models/DependencyPropertyInfo.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Models/DependencyPropertyInfo.cs index 38a5af29d..363ea6516 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Models/DependencyPropertyInfo.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Models/DependencyPropertyInfo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using CommunityToolkit.GeneratedDependencyProperty.Helpers; using Microsoft.CodeAnalysis; namespace CommunityToolkit.GeneratedDependencyProperty.Models; @@ -11,6 +12,7 @@ namespace CommunityToolkit.GeneratedDependencyProperty.Models; /// /// The hierarchy info for the containing type. /// The property name. +/// The list of additional modifiers for the property (they are values). /// The accessibility of the property, if available. /// The accessibility of the accessor, if available. /// The accessibility of the accessor, if available. @@ -18,7 +20,6 @@ namespace CommunityToolkit.GeneratedDependencyProperty.Models; /// The type name for the generated property, including nullability annotations. /// The default value to set the generated property to. /// Indicates whether the property is of a reference type or an unconstrained type parameter. -/// Whether or not the generated property should be marked as required. /// Indicates whether local caching should be used for the property value. /// Indicates whether the WinRT-based property changed callback is implemented. /// Indicates whether the WinRT-based shared property changed callback is implemented. @@ -27,6 +28,7 @@ namespace CommunityToolkit.GeneratedDependencyProperty.Models; internal sealed record DependencyPropertyInfo( HierarchyInfo Hierarchy, string PropertyName, + EquatableArray PropertyModifiers, Accessibility DeclaredAccessibility, Accessibility GetterAccessibility, Accessibility SetterAccessibility, @@ -34,7 +36,6 @@ internal sealed record DependencyPropertyInfo( string TypeNameWithNullabilityAnnotations, TypedConstantInfo DefaultValue, bool IsReferenceTypeOrUnconstraindTypeParameter, - bool IsRequired, bool IsLocalCachingEnabled, bool IsPropertyChangedCallbackImplemented, bool IsSharedPropertyChangedCallbackImplemented,