-
Notifications
You must be signed in to change notification settings - Fork 121
Add [GeneratedCustomPropertyProvider] generator and analyzers
#2160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging/3.0
Are you sure you want to change the base?
Add [GeneratedCustomPropertyProvider] generator and analyzers
#2160
Conversation
Introduces the GeneratedCustomPropertyProviderAttribute for marking bindable properties in XAML scenarios. Supports specifying property names and indexer types for binding code generation via ICustomPropertyProvider interfaces.
Renamed the file for consistency with naming conventions. No code changes were made.
Changed BindableIReadOnlyListAdapterComWrappersMarshallerAttribute from public to internal and removed obsolete/editor-browsable attributes. Applied the marshaller attribute to BindableIReadOnlyListAdapter to improve type registration and usage.
The TypeMapAssemblyTargetGenerator class no longer implements the IIncrementalGenerator interface. This may be in preparation for refactoring or changing the generator's integration with Roslyn.
Introduces a new SyntaxExtensions class providing extension methods for SyntaxNode and SyntaxTokenList. These methods allow checking if a node or token list matches any of the specified SyntaxKind values, improving code readability and reuse in Roslyn-based source generators.
Introduces IncrementalGeneratorInitializationContextExtensions with a method to combine attribute-based syntax analysis and analyzer config options. Also adds a struct to encapsulate both GeneratorAttributeSyntaxContext and AnalyzerConfigOptions for use in source generators.
Introduces extension methods to determine if a member declaration is partial and if it resides within a hierarchy of partial type declarations. This aids in analyzing and generating code for partial types in the source generator.
Introduces ObjectPool<T> to efficiently manage reusable objects with a fixed pool size. This class is ported from Roslyn and provides thread-safe allocation and freeing of objects to reduce allocations and improve performance.
Introduces PooledArrayBuilder<T>, a ref struct for efficiently building sequences of values using pooled buffers. This utility provides methods for adding, inserting, and enumerating items, and is ported from ComputeSharp to support high-performance source generation scenarios.
Introduces IndentedTextWriter, a helper for writing indented text with pooled buffers, ported from ComputeSharp. Changes PooledArrayBuilder<T> from a ref struct to a struct to support usage in IndentedTextWriter and improve compatibility.
The code responsible for generating bindable custom properties in CustomPropertyProviderGenerator has been commented out, likely to temporarily disable this feature or for refactoring purposes.
Introduces IsDefaultOrEmpty and Length properties to the EquatableArray<T> struct for easier array state inspection. Also replaces <sinheritdoc/> with <inheritdoc/> in documentation comments for improved clarity and consistency.
Introduces ITypeSymbolExtensions with methods to retrieve and append fully qualified metadata names for ITypeSymbol instances. These utilities facilitate working with Roslyn symbols in source generation scenarios.
Introduces IndentedTextWriterExtensions with helper methods for writing generated attributes, sorted using directives, line-separated members, and initialization expressions to streamline code generation tasks.
Introduces HierarchyInfo and TypeInfo classes to model type hierarchies for source generation. These are ported from ComputeSharp and provide utilities for describing and generating type syntax based on Roslyn symbols.
Updated the generator to prevent implementing 'ICustomPropertyProvider' on types marked as 'ref', in addition to static and abstract types.
Introduces an extension method to filter out null values from IncrementalValuesProvider instances, improving safety and convenience when working with nullable types in source generators.
Introduces EnumerateAllMembers method to enumerate all members of an ITypeSymbol, including inherited members. This enhances symbol analysis capabilities in the source generator.
The 'SkipNullValues' method in IncrementalValuesProviderExtensions is now correctly marked as an extension method by adding the 'this' modifier to the first parameter.
Introduces GetFullyQualifiedName and GetFullyQualifiedNameWithNullabilityAnnotations extension methods to ISymbolExtensions, allowing retrieval of a symbol's fully qualified name with or without nullability annotations.
Replaces manual array conversion and unsafe cast in PooledArrayBuilder<T>.ToImmutable with a direct call to WrittenSpan.ToImmutableArray for improved clarity and safety.
Simplifies and unifies property discovery logic in GetCustomPropertyInfo by consolidating handling of attribute constructor arguments and property filtering. The method now uses explicit property and indexer type filters when provided, and iterates all members in a single loop, improving maintainability and readability.
Moved CustomPropertyInfo and CustomPropertyProviderInfo to a new Models namespace and files. Refactored WriteCustomPropertyProviderImplementations to WriteCustomPropertyProviderImplementation, improving code organization and clarity. Added detailed implementations for ICustomPropertyProvider interface methods.
Moved the ICustomPropertyProvider implementation emission methods from Execute.cs to a new CustomPropertyProviderGenerator.Emit.cs file. Updated references to use the new Emit class for improved code organization and separation of concerns.
Introduces a CanBeBoxed property to determine if a type can be boxed, and updates CustomPropertyProviderGenerator to skip properties with unboxable types. This improves robustness when generating custom property info for types with indexers or ref-like types.
Introduces the IsIndexer property to determine if a property is an indexer based on FullyQualifiedIndexerTypeName. Utilizes MemberNotNullWhen attribute for improved nullability annotations.
Added a check to ensure that static indexer properties are ignored during code generation, as indexers must be instance properties. This prevents invalid code from being generated for unsupported property types.
Introduces methods to emit implementation types for ICustomProperty, generating appropriate classes for both normal and indexer custom properties. Refactors property checks to use the new IsIndexer property and updates code generation logic to support both property types. This enables more complete and correct code generation for custom property providers.
Introduces a test for ICustomPropertyProvider interface activation and updates the project to reference Windows.UI.Xaml. Also adds a GeneratedCustomPropertyProvider implementation for testing custom property provider scenarios.
Introduces DiagnosticDescriptors.cs containing DiagnosticDescriptor instances for errors related to the [GeneratedCustomPropertyProvider] attribute, including invalid target types, missing partial modifiers, and unavailable interface types.
Introduced AnalyzerReleases.Shipped.md and AnalyzerReleases.Unshipped.md for Roslyn analyzer release tracking. Updated WinRT.SourceGenerator2.csproj to include these files as additional resources. Added Test.cs to WinRT.Runtime2/Xaml.Attributes with ICustomPropertyProvider and ICustomProperty interfaces, a sample Test class, and generated property implementation for testing source generator functionality.
Introduces GeneratedCustomPropertyProviderTargetTypeAnalyzer to validate that types with [GeneratedCustomPropertyProvider] are valid targets. The analyzer checks for correct type kind, required modifiers, and reports diagnostics for invalid usage.
Introduces GeneratedCustomPropertyProviderNoAvailableInterfaceTypeAnalyzer to report diagnostics when [GeneratedCustomPropertyProvider] is used but no ICustomPropertyProvider interface is available in the compilation.
Introduced a new test project, SourceGenerator2Test, targeting .NET 10.0 with relevant package and project references. Updated cswinrt.slnx to include the new test project.
Added suppression for CS8620 compiler warning in CustomPropertyProviderGenerator.Execute.cs. This is a temporary measure until the underlying compiler warning is resolved.
Added VersionOverride="5.0.0" to the Microsoft.CodeAnalysis.CSharp.Workspaces package reference in the test project to ensure a specific version is used.
Included MSTest version 4.0.2 in Directory.Packages.props to ensure it is available as a dependency for the project.
Introduces AssemblyInfo.cs to the SourceGenerator2Test project and enables parallel test execution using the Parallelize attribute.
Introduces a generic static helper class to facilitate testing of source generators. Provides methods to verify generated sources, create compilations, and run generators with specified language versions, streamlining the process of writing and maintaining source generator tests.
Introduces a unit test for the CustomPropertyProviderGenerator using a simple class with properties and an indexer. This test verifies the source generator's output for a class annotated with [GeneratedCustomPropertyProvider].
Introduces a generic CSharpAnalyzerTest<TAnalyzer> class to facilitate analyzer testing with configurable C# language version and unsafe block support. This helper streamlines test setup and allows specifying reference assemblies and additional references.
Reordered parameters in RunGenerator to place languageVersion before out parameters and removed its default value. Updated VerifySources to match the new signature.
Introduces unit tests for the GeneratedCustomPropertyProviderTargetTypeAnalyzer to verify correct diagnostics for valid and invalid target types, partial type requirements, and type hierarchy scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 38 out of 38 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public class Test_CustomPropertyProviderGenerator | ||
| { | ||
| [TestMethod] | ||
| public async Task SimpleShader_ComputeShader() |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test method name 'SimpleShader_ComputeShader' appears to be copied from another test and doesn't match the test's purpose. It should be renamed to something like 'GeneratesCustomPropertyProvider_ForClassWithProperties'.
| public async Task SimpleShader_ComputeShader() | |
| public async Task GeneratesCustomPropertyProvider_ForClassWithProperties() |
| return false; | ||
| } | ||
|
|
||
| // We can only generated the 'ICustomPropertyProvider' implementation if the type is 'partial'. |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'generated' to 'generate' in the comment.
| // We can only generated the 'ICustomPropertyProvider' implementation if the type is 'partial'. | |
| // We can only generate the 'ICustomPropertyProvider' implementation if the type is 'partial'. |
| /// <inheritdoc/> | ||
| public object GetIndexedValue(object target, object index) | ||
| { | ||
| return (({{info.TypeHierarchy.GetFullyQualifiedTypeName()}})target)[({{propertyInfo.FullyQualifiedIndexerTypeName}})index]; |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method WriteCustomPropertyImplementationType is generating code for a non-indexed property but includes implementation for GetIndexedValue and SetIndexedValue that reference propertyInfo.FullyQualifiedIndexerTypeName, which will be null for non-indexed properties. This appears to be swapped with the logic in WriteIndexedCustomPropertyImplementationType. The two method implementations should be exchanged or the indexer logic should be corrected.
| { | ||
| // We should be able to get an 'ICustomPropertyProvider' interface pointer | ||
| Marshal.ThrowExceptionForHR(Marshal.QueryInterface( | ||
| pUnk: (nint)customPropertyProviderPtr, |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable customPropertyProviderPtr is being used as the source pointer in QueryInterface, but it should be testCustomPropertyProviderUnknownPtr. The customPropertyProviderPtr is the output parameter and should be passed to the ppv argument instead.
| pUnk: (nint)customPropertyProviderPtr, | |
| pUnk: (nint)testCustomPropertyProviderUnknownPtr, |
| private readonly LanguageVersion _languageVersion; | ||
|
|
||
| /// <summary> | ||
| /// Creates a new <see cref="CSharpAnalyzerTest{TAnalyzer}"/> instance with the specified paramaters. |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'paramaters' to 'parameters'.
| /// Creates a new <see cref="CSharpAnalyzerTest{TAnalyzer}"/> instance with the specified paramaters. | |
| /// Creates a new <see cref="CSharpAnalyzerTest{TAnalyzer}"/> instance with the specified parameters. |
|
|
||
| using (writer.WriteBlock()) | ||
| { | ||
| // Emit all 'ICustomProperty' members for a normal proprty, and the singleton field |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'proprty' to 'property'.
| // Emit all 'ICustomProperty' members for a normal proprty, and the singleton field | |
| // Emit all 'ICustomProperty' members for a normal property, and the singleton field |
| private static readonly ObjectPool<Writer> SharedObjectPool = new(static () => new Writer()); | ||
|
|
||
| /// <summary> | ||
| /// The rented <see cref="Writer"/> instance to use. |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 29 describes '_writer' as 'rented', but it would be clearer to describe it as 'pooled' to match the type name and usage pattern, since the pooling is managed internally via SharedObjectPool.
| /// The rented <see cref="Writer"/> instance to use. | |
| /// The pooled <see cref="Writer"/> instance to use. |
Introduces ReferenceAssembliesExtensions to provide .NET 10 reference assemblies and updates CSharpAnalyzerTest to use Net100. This enables testing against .NET 10 until official support is available in Roslyn SDK.
Title. This doesn't need any special casing at all in CsWinRT 3.0, thanks to 'cswinrtgen'.
Note
Still draft as fully setting up the unit tests needs a bit more work.