Skip to content

Commit

Permalink
Add support for more modifiers, bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Dec 6, 2024
1 parent 9439f37 commit eb41e28
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -147,6 +148,37 @@ public static bool IsCandidateSymbolValid(IPropertySymbol propertySymbol, bool u
return true;
}

/// <summary>
/// Gathers all allowed property modifiers that should be forwarded to the generated property.
/// </summary>
/// <param name="node">The input <see cref="PropertyDeclarationSyntax"/> node.</param>
/// <returns>The returned set of property modifiers, if any.</returns>
public static ImmutableArray<SyntaxKind> GetPropertyModifiers(PropertyDeclarationSyntax node)
{
// We only allow a subset of all possible modifiers (aside from the accessibility modifiers)
ReadOnlySpan<SyntaxKind> candidateKinds =
[
SyntaxKind.NewKeyword,
SyntaxKind.VirtualKeyword,
SyntaxKind.SealedKeyword,
SyntaxKind.OverrideKeyword,
SyntaxKind.RequiredKeyword
];

using ImmutableArrayBuilder<SyntaxKind> 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();
}

/// <summary>
/// Tries to get the accessibility of the property and accessors, if possible.
/// </summary>
Expand Down Expand Up @@ -331,16 +363,6 @@ public static bool IsSharedPropertyChangedCallbackImplemented(IPropertySymbol pr
return false;
}

/// <summary>
/// Checks whether an input property is required.
/// </summary>
/// <param name="propertySymbol">The input <see cref="IPropertySymbol"/> instance to process.</param>
/// <returns>Whether <paramref name="propertySymbol"/> is required.</returns>
public static bool IsRequiredProperty(IPropertySymbol propertySymbol)
{
return propertySymbol.IsRequired;
}

/// <summary>
/// Writes all implementations of partial dependency property declarations.
/// </summary>
Expand All @@ -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 + " ",
_ => ""
Expand Down Expand Up @@ -428,11 +450,20 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
{
string oldValueTypeNameAsNullable = GetOldValueTypeNameAsNullable(propertyInfo);

// Declare the property
writer.WriteLine(skipIfPresent: true);
writer.WriteLine("/// <inheritdoc/>");
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())
Expand Down Expand Up @@ -653,7 +684,7 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
/// <remarks>This method is invoked by the <see cref="global::{WellKnownTypeNames.DependencyProperty(propertyInfo.UseWindowsUIXaml)}"/> infrastructure, after the value of <see cref="{propertyInfo.PropertyName}"/> is changed.</remarks>
""", 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
Expand All @@ -664,7 +695,7 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
/// <remarks>This method is invoked by the <see cref="global::{WellKnownTypeNames.DependencyProperty(propertyInfos[0].UseWindowsUIXaml)}"/> infrastructure, after the value of any dependency property has just changed.</remarks>
""", 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);");
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,6 +71,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

token.ThrowIfCancellationRequested();

// Get all additional modifiers for the property
ImmutableArray<SyntaxKind> propertyModifiers = Execute.GetPropertyModifiers((PropertyDeclarationSyntax)context.TargetNode);

token.ThrowIfCancellationRequested();

// Get the accessibility values, if the property is valid
if (!Execute.TryGetAccessibilityModifiers(
node: (PropertyDeclarationSyntax)context.TargetNode,
Expand All @@ -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();
Expand Down Expand Up @@ -124,14 +129,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return new DependencyPropertyInfo(
Hierarchy: hierarchyInfo,
PropertyName: propertySymbol.Name,
PropertyModifiers: propertyModifiers.AsUnderlyingType(),
DeclaredAccessibility: declaredAccessibility,
GetterAccessibility: getterAccessibility,
SetterAccessibility: setterAccessibility,
TypeName: typeName,
TypeNameWithNullabilityAnnotations: typeNameWithNullabilityAnnotations,
DefaultValue: defaultValue,
IsReferenceTypeOrUnconstraindTypeParameter: isReferenceTypeOrUnconstraindTypeParameter,
IsRequired: isRequired,
IsLocalCachingEnabled: isLocalCachingEnabled,
IsPropertyChangedCallbackImplemented: isPropertyChangedCallbackImplemented,
IsSharedPropertyChangedCallbackImplemented: isSharedPropertyChangedCallbackImplemented,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -11,14 +12,14 @@ namespace CommunityToolkit.GeneratedDependencyProperty.Models;
/// </summary>
/// <param name="Hierarchy">The hierarchy info for the containing type.</param>
/// <param name="PropertyName">The property name.</param>
/// <param name="PropertyModifiers">The list of additional modifiers for the property (they are <see cref="SyntaxKind"/> values).</param>
/// <param name="DeclaredAccessibility">The accessibility of the property, if available.</param>
/// <param name="GetterAccessibility">The accessibility of the <see langword="get"/> accessor, if available.</param>
/// <param name="SetterAccessibility">The accessibility of the <see langword="set"/> accessor, if available.</param>
/// <param name="TypeName">The type name for the generated property (without nullability annotations).</param>
/// <param name="TypeNameWithNullabilityAnnotations">The type name for the generated property, including nullability annotations.</param>
/// <param name="DefaultValue">The default value to set the generated property to.</param>
/// <param name="IsReferenceTypeOrUnconstraindTypeParameter">Indicates whether the property is of a reference type or an unconstrained type parameter.</param>
/// <param name="IsRequired">Whether or not the generated property should be marked as required.</param>
/// <param name="IsLocalCachingEnabled">Indicates whether local caching should be used for the property value.</param>
/// <param name="IsPropertyChangedCallbackImplemented">Indicates whether the WinRT-based property changed callback is implemented.</param>
/// <param name="IsSharedPropertyChangedCallbackImplemented">Indicates whether the WinRT-based shared property changed callback is implemented.</param>
Expand All @@ -27,14 +28,14 @@ namespace CommunityToolkit.GeneratedDependencyProperty.Models;
internal sealed record DependencyPropertyInfo(
HierarchyInfo Hierarchy,
string PropertyName,
EquatableArray<ushort> PropertyModifiers,
Accessibility DeclaredAccessibility,
Accessibility GetterAccessibility,
Accessibility SetterAccessibility,
string TypeName,
string TypeNameWithNullabilityAnnotations,
TypedConstantInfo DefaultValue,
bool IsReferenceTypeOrUnconstraindTypeParameter,
bool IsRequired,
bool IsLocalCachingEnabled,
bool IsPropertyChangedCallbackImplemented,
bool IsSharedPropertyChangedCallbackImplemented,
Expand Down

0 comments on commit eb41e28

Please sign in to comment.