diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.Execute.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.Execute.cs
index 8568b84a3..169f8e974 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.Execute.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.Execute.cs
@@ -46,6 +46,12 @@ public static bool IsCandidatePropertyDeclaration(SyntaxNode node, CancellationT
return false;
}
+ // Static properties are not supported
+ if (property.Modifiers.Any(SyntaxKind.StaticKeyword))
+ {
+ return false;
+ }
+
// The accessors must be a get and a set (with any accessibility)
if (accessors[0].Kind() is not (SyntaxKind.GetAccessorDeclaration or SyntaxKind.SetAccessorDeclaration) ||
accessors[1].Kind() is not (SyntaxKind.GetAccessorDeclaration or SyntaxKind.SetAccessorDeclaration))
@@ -128,6 +134,11 @@ public static CanvasEffectInvalidationType GetCanvasEffectInvalidationType(Attri
return CanvasEffectInvalidationType.Update;
}
+ ///
+ /// Writes all implementations of partial effect property declarations.
+ ///
+ /// The input set of declared effect properties.
+ /// The instance to write into.
public static void WritePropertyDeclarations(EquatableArray properties, IndentedTextWriter writer)
{
// Helper to get the nullable type name for the initial property value
@@ -163,8 +174,10 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
writer.WriteLine("/// ");
writer.WriteGeneratedAttributes(GeneratorName);
- writer.WriteLine($$"""
- {{GetExpressionWithTrailingSpace(propertyInfo.DeclaredAccessibility)}}partial {{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}}
+ writer.Write(GetExpressionWithTrailingSpace(propertyInfo.DeclaredAccessibility));
+ writer.WriteIf(propertyInfo.IsRequired, "required ");
+ writer.WriteLine($"partial {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.PropertyName}");
+ writer.WriteLine($$"""
{
{{GetExpressionWithTrailingSpace(propertyInfo.GetterAccessibility)}}get => field;
{{GetExpressionWithTrailingSpace(propertyInfo.SetterAccessibility)}}set
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.cs
index 269650a13..3bf7d0698 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/CanvasEffectPropertyGenerator.cs
@@ -63,7 +63,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
}
// Also ignore all properties that have an invalid declaration
- if (propertySymbol.IsStatic || propertySymbol.ReturnsByRef || propertySymbol.ReturnsByRefReadonly || propertySymbol.Type.IsRefLikeType)
+ if (propertySymbol.ReturnsByRef || propertySymbol.ReturnsByRefReadonly || propertySymbol.Type.IsRefLikeType)
{
return default;
}
@@ -101,6 +101,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// This will cover both reference types as well T when the constraints are not struct or unmanaged.
// If this is true, it means the field storage can potentially be in a null state (even if not annotated).
bool isReferenceTypeOrUnconstraindTypeParameter = !propertySymbol.Type.IsValueType;
+ bool isRequired = propertySymbol.IsRequired;
// Finally, get the hierarchy too
HierarchyInfo hierarchyInfo = HierarchyInfo.From(typeSymbol);
@@ -115,6 +116,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
SetterAccessibility: setterAccessibility,
TypeNameWithNullabilityAnnotations: typeNameWithNullabilityAnnotations,
IsReferenceTypeOrUnconstraindTypeParameter: isReferenceTypeOrUnconstraindTypeParameter,
+ IsRequired: isRequired,
InvalidationType: invalidationType);
})
.WithTrackingName(WellKnownTrackingNames.Execute)
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
index 64e1c2f0c..4a745df32 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
@@ -80,7 +80,7 @@ internal static class DiagnosticDescriptors
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
///
- /// Gets a for a CanvasEffect property with invalid accessors.
+ /// Gets a for a CanvasEffect property that is not an incomplete partial property definition.
///
/// Format: "The property "{0}" is not an incomplete partial definition ([GeneratedCanvasEffectProperty] must be used on partial property definitions with no implementation part)".
///
@@ -92,11 +92,11 @@ internal static class DiagnosticDescriptors
category: DiagnosticCategory,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description: "A property using [GeneratedCanvasEffectProperty] is either not partial, or a partial implementation part ([GeneratedCanvasEffectProperty] must be used on partial property definitions with no implementation par).",
+ description: "A property using [GeneratedCanvasEffectProperty] is either not partial, or a partial implementation part ([GeneratedCanvasEffectProperty] must be used on partial property definitions with no implementation part).",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
///
- /// Gets a for a CanvasEffect property with invalid accessors.
+ /// Gets a for a CanvasEffect property that returns a ref value.
///
/// Format: "The property "{0}" returns a value by reference ([GeneratedCanvasEffectProperty] must be used on properties returning a type by value)".
///
@@ -112,7 +112,7 @@ internal static class DiagnosticDescriptors
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
///
- /// Gets a for a CanvasEffect property with invalid accessors.
+ /// Gets a for a CanvasEffect property that returns a byref-like value.
///
/// Format: "The property "{0}" returns a ref struct value ([GeneratedCanvasEffectProperty] must be used on properties with a type that is not a ref struct)".
///
@@ -128,7 +128,7 @@ internal static class DiagnosticDescriptors
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
///
- /// Gets a for a CanvasEffect property with invalid accessors.
+ /// Gets a for when C# is not set to 'preview'.
///
/// Format: "Using [GeneratedCanvasEffectProperty] requires the C# language version to be set to 'preview', as support for the 'field' keyword is needed by the source generators to emit valid code (add <LangVersion>preview</LangVersion> to your .csproj/.props file)".
///
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/Models/CanvasEffectPropertyInfo.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/Models/CanvasEffectPropertyInfo.cs
index 3116543b8..f8eef81a3 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/Models/CanvasEffectPropertyInfo.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/Models/CanvasEffectPropertyInfo.cs
@@ -17,6 +17,7 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators.Models;
/// The accessibility of the accessor, if available.
/// The type name for the generated property, including nullability annotations.
/// 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.
/// The invalidation type to request.
internal sealed record CanvasEffectPropertyInfo(
HierarchyInfo Hierarchy,
@@ -26,4 +27,5 @@ internal sealed record CanvasEffectPropertyInfo(
Accessibility SetterAccessibility,
string TypeNameWithNullabilityAnnotations,
bool IsReferenceTypeOrUnconstraindTypeParameter,
+ bool IsRequired,
CanvasEffectInvalidationType InvalidationType);
diff --git a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator.cs b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator.cs
index 6597d5ac5..7a98b1712 100644
--- a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator.cs
+++ b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator.cs
@@ -344,6 +344,90 @@ private set
VerifyGeneratedDiagnostics(source, ("MyNamespace.MyEffect.g.cs", result));
}
+ [TestMethod]
+ public void SingleProperty_Required()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyNamespace;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public required partial int Number { get; set; }
+ }
+ """;
+
+ const string result = """"
+ //
+ #pragma warning disable
+
+ namespace MyNamespace
+ {
+ ///
+ partial class MyEffect
+ {
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("ComputeSharp.D2D1.WinUI.CanvasEffectPropertyGenerator", )]
+ [global::System.Diagnostics.DebuggerNonUserCode]
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ public required partial int Number
+ {
+ get => field;
+ set
+ {
+ if (global::System.Collections.Generic.EqualityComparer.Default.Equals(field, value))
+ {
+ return;
+ }
+
+ int oldValue = field;
+
+ OnNumberChanging(value);
+ OnNumberChanging(oldValue, value);
+
+ field = value;
+
+ OnNumberChanged(value);
+ OnNumberChanged(oldValue, value);
+
+ InvalidateEffectGraph(global::ComputeSharp.D2D1.WinUI.CanvasEffectInvalidationType.Update);
+ }
+ }
+
+ /// Executes the logic for when is changing.
+ /// The new property value being set.
+ /// This method is invoked right before the value of is changed.
+ [global::System.CodeDom.Compiler.GeneratedCode("ComputeSharp.D2D1.WinUI.CanvasEffectPropertyGenerator", )]
+ partial void OnNumberChanging(int newValue);
+
+ /// Executes the logic for when is changing.
+ /// The previous property value that is being replaced.
+ /// The new property value being set.
+ /// This method is invoked right before the value of is changed.
+ [global::System.CodeDom.Compiler.GeneratedCode("ComputeSharp.D2D1.WinUI.CanvasEffectPropertyGenerator", )]
+ partial void OnNumberChanging(int oldValue, int newValue);
+
+ /// Executes the logic for when has just changed.
+ /// The new property value that has been set.
+ /// This method is invoked right after the value of is changed.
+ [global::System.CodeDom.Compiler.GeneratedCode("ComputeSharp.D2D1.WinUI.CanvasEffectPropertyGenerator", )]
+ partial void OnNumberChanged(int newValue);
+
+ /// Executes the logic for when has just changed.
+ /// The previous property value that has been replaced.
+ /// The new property value that has been set.
+ /// This method is invoked right after the value of is changed.
+ [global::System.CodeDom.Compiler.GeneratedCode("ComputeSharp.D2D1.WinUI.CanvasEffectPropertyGenerator", )]
+ partial void OnNumberChanged(int oldValue, int newValue);
+ }
+ }
+ """";
+
+ VerifyGeneratedDiagnostics(source, ("MyNamespace.MyEffect.g.cs", result));
+ }
+
[TestMethod]
public void MultipleProperties()
{