diff --git a/ComputeSharp.sln b/ComputeSharp.sln
index cb4e0e52d..8408de20e 100644
--- a/ComputeSharp.sln
+++ b/ComputeSharp.sln
@@ -187,6 +187,12 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ComputeSharp.D2D1.UI.Source
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputeSharp.D2D1.Uwp.SourceGenerators", "src\ComputeSharp.D2D1.Uwp.SourceGenerators\ComputeSharp.D2D1.Uwp.SourceGenerators.csproj", "{690C4014-A06B-44DB-B717-AEA4CAFA9D68}"
EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ComputeSharp.D2D1.UI.CodeFixers", "src\ComputeSharp.D2D1.UI.CodeFixers\ComputeSharp.D2D1.UI.CodeFixers.shproj", "{CE85DC5A-E455-42FE-9B64-0D5F0D7EE366}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputeSharp.D2D1.WinUI.CodeFixers", "src\ComputeSharp.D2D1.WinUI.CodeFixers\ComputeSharp.D2D1.WinUI.CodeFixers.csproj", "{9D3C872E-D64E-47E7-BDC8-077A151DBB7D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputeSharp.D2D1.Uwp.CodeFixers", "src\ComputeSharp.D2D1.Uwp.CodeFixers\ComputeSharp.D2D1.Uwp.CodeFixers.csproj", "{8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -535,6 +541,22 @@ Global
{690C4014-A06B-44DB-B717-AEA4CAFA9D68}.Release|ARM64.Build.0 = Release|Any CPU
{690C4014-A06B-44DB-B717-AEA4CAFA9D68}.Release|x64.ActiveCfg = Release|Any CPU
{690C4014-A06B-44DB-B717-AEA4CAFA9D68}.Release|x64.Build.0 = Release|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Debug|x64.Build.0 = Debug|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Release|ARM64.Build.0 = Release|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Release|x64.ActiveCfg = Release|Any CPU
+ {9D3C872E-D64E-47E7-BDC8-077A151DBB7D}.Release|x64.Build.0 = Release|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Debug|x64.Build.0 = Debug|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Release|ARM64.Build.0 = Release|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Release|x64.ActiveCfg = Release|Any CPU
+ {8BE73DB3-16E0-41F4-A87F-0C2AB43856A2}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -597,14 +619,17 @@ Global
src\ComputeSharp.SourceGeneration\ComputeSharp.SourceGeneration.projitems*{690c4014-a06b-44db-b717-aea4cafa9d68}*SharedItemsImports = 5
src\ComputeSharp.D2D1.UI\ComputeSharp.D2D1.UI.projitems*{716df19e-69ba-4a9c-9ca4-bfd196152f46}*SharedItemsImports = 5
samples\ComputeSharp.SwapChain.Shaders.D2D1\ComputeSharp.SwapChain.Shaders.D2D1.projitems*{73c32d0f-64db-4674-84e9-8fcc41228474}*SharedItemsImports = 5
+ src\ComputeSharp.D2D1.UI.CodeFixers\ComputeSharp.D2D1.UI.CodeFixers.projitems*{8be73db3-16e0-41f4-a87f-0c2ab43856a2}*SharedItemsImports = 5
src\ComputeSharp.SourceGeneration.Hlsl\ComputeSharp.SourceGeneration.Hlsl.projitems*{9ac496a3-bbf0-4c8f-a50d-a20bf01c5e05}*SharedItemsImports = 13
src\ComputeSharp.CodeFixing\ComputeSharp.CodeFixing.projitems*{9b4448b1-200f-4966-8a13-a508691b3003}*SharedItemsImports = 5
+ src\ComputeSharp.D2D1.UI.CodeFixers\ComputeSharp.D2D1.UI.CodeFixers.projitems*{9d3c872e-d64e-47e7-bdc8-077a151dbb7d}*SharedItemsImports = 5
src\ComputeSharp.Win32.D2D1\ComputeSharp.Win32.D2D1.projitems*{9da1da9f-f8b2-4b25-be80-c21f773029e3}*SharedItemsImports = 13
samples\ComputeSharp.SwapChain.Shaders.D2D1\ComputeSharp.SwapChain.Shaders.D2D1.projitems*{9ea5ae9d-c39a-4f43-b03e-0a848ea2558a}*SharedItemsImports = 5
samples\ComputeSharp.SwapChain.Shaders.D2D1\ComputeSharp.SwapChain.Shaders.D2D1.projitems*{9fbe070e-a210-4cef-9f04-61c2b269c600}*SharedItemsImports = 5
src\ComputeSharp.D2D1.UI\ComputeSharp.D2D1.UI.projitems*{a2a2171b-0baf-4a2a-bfb3-3357ef714bf0}*SharedItemsImports = 13
src\ComputeSharp.D2D1.UI\ComputeSharp.D2D1.UI.projitems*{bd9e6556-357e-4c20-bfcd-fb131f9372fa}*SharedItemsImports = 5
samples\ComputeSharp.SwapChain.Shaders\ComputeSharp.SwapChain.Shaders.projitems*{c12d7ace-98ed-4813-8118-6667c34f484f}*SharedItemsImports = 5
+ src\ComputeSharp.D2D1.UI.CodeFixers\ComputeSharp.D2D1.UI.CodeFixers.projitems*{ce85dc5a-e455-42fe-9b64-0d5f0d7ee366}*SharedItemsImports = 13
src\ComputeSharp.D2D1.UI.SourceGenerators\ComputeSharp.D2D1.UI.SourceGenerators.projitems*{d20e610f-eb37-46e7-b028-04784d7400d5}*SharedItemsImports = 13
src\ComputeSharp.SourceGeneration.Hlsl\ComputeSharp.SourceGeneration.Hlsl.projitems*{e44053bd-a761-47fb-aa78-087a599672ea}*SharedItemsImports = 5
src\ComputeSharp.SourceGeneration\ComputeSharp.SourceGeneration.projitems*{e44053bd-a761-47fb-aa78-087a599672ea}*SharedItemsImports = 5
diff --git a/build/Directory.Build.props b/build/Directory.Build.props
index 5d060e9de..6545de68c 100644
--- a/build/Directory.Build.props
+++ b/build/Directory.Build.props
@@ -186,7 +186,9 @@
false
diff --git a/src/ComputeSharp.CodeFixers/ComputeSharp.CodeFixers.csproj b/src/ComputeSharp.CodeFixers/ComputeSharp.CodeFixers.csproj
index 40b481406..685c48f96 100644
--- a/src/ComputeSharp.CodeFixers/ComputeSharp.CodeFixers.csproj
+++ b/src/ComputeSharp.CodeFixers/ComputeSharp.CodeFixers.csproj
@@ -4,7 +4,7 @@
-
+
diff --git a/src/ComputeSharp.D2D1.CodeFixers/ComputeSharp.D2D1.CodeFixers.csproj b/src/ComputeSharp.D2D1.CodeFixers/ComputeSharp.D2D1.CodeFixers.csproj
index cf24b82b9..dbfc9741f 100644
--- a/src/ComputeSharp.D2D1.CodeFixers/ComputeSharp.D2D1.CodeFixers.csproj
+++ b/src/ComputeSharp.D2D1.CodeFixers/ComputeSharp.D2D1.CodeFixers.csproj
@@ -4,7 +4,7 @@
-
+
diff --git a/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.projitems b/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.projitems
new file mode 100644
index 000000000..7f9be6958
--- /dev/null
+++ b/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.projitems
@@ -0,0 +1,14 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ ce85dc5a-e455-42fe-9b64-0d5f0d7ee366
+
+
+ ComputeSharp.D2D1.UI.CodeFixers
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.shproj b/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.shproj
new file mode 100644
index 000000000..51be84cca
--- /dev/null
+++ b/src/ComputeSharp.D2D1.UI.CodeFixers/ComputeSharp.D2D1.UI.CodeFixers.shproj
@@ -0,0 +1,13 @@
+
+
+
+ ce85dc5a-e455-42fe-9b64-0d5f0d7ee366
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/src/ComputeSharp.D2D1.UI.CodeFixers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs b/src/ComputeSharp.D2D1.UI.CodeFixers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs
new file mode 100644
index 000000000..da15eade9
--- /dev/null
+++ b/src/ComputeSharp.D2D1.UI.CodeFixers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs
@@ -0,0 +1,350 @@
+using System.Collections.Immutable;
+using System.Composition;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+#if WINDOWS_UWP
+using ComputeSharp.D2D1.Uwp.SourceGenerators.Constants;
+#else
+using ComputeSharp.D2D1.WinUI.SourceGenerators.Constants;
+#endif
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Simplification;
+using Microsoft.CodeAnalysis.Text;
+#if WINDOWS_UWP
+using static ComputeSharp.D2D1.Uwp.SourceGenerators.DiagnosticDescriptors;
+#else
+using static ComputeSharp.D2D1.WinUI.SourceGenerators.DiagnosticDescriptors;
+#endif
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+#if WINDOWS_UWP
+namespace ComputeSharp.D2D1.Uwp.SourceGenerators;
+#else
+namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
+#endif
+
+///
+/// A code fixer that converts manual properties into partial properties using [GeneratedCanvasEffectProperty].
+///
+[ExportCodeFixProvider(LanguageNames.CSharp)]
+[Shared]
+public sealed class UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer : CodeFixProvider
+{
+ ///
+ public override ImmutableArray FixableDiagnosticIds { get; } = [UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyId];
+
+ ///
+ public override Microsoft.CodeAnalysis.CodeFixes.FixAllProvider? GetFixAllProvider()
+ {
+ return new FixAllProvider();
+ }
+
+ ///
+ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ Diagnostic diagnostic = context.Diagnostics[0];
+ TextSpan diagnosticSpan = context.Span;
+
+ // This code fixer needs the semantic model, so check that first
+ if (!context.Document.SupportsSemanticModel)
+ {
+ return;
+ }
+
+ // Retrieve the properties passed by the analyzer
+ if (!int.TryParse(diagnostic.Properties[UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer.InvalidationTypePropertyName], out int invalidationType))
+ {
+ return;
+ }
+
+ SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+
+ // Get the property declaration from the target diagnostic
+ if (root!.FindNode(diagnosticSpan) is PropertyDeclarationSyntax propertyDeclaration)
+ {
+ // Get the semantic model, as we need to resolve symbols
+ SemanticModel semanticModel = (await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false))!;
+
+ // Register the code fix to update the semi-auto property to a partial property with [GeneratedCanvasEffectProperty]
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ title: "Use [GeneratedCanvasEffectProperty]",
+ createChangedDocument: token => ConvertToPartialProperty(
+ context.Document,
+ semanticModel,
+ root,
+ propertyDeclaration,
+ invalidationType),
+ equivalenceKey: "Use [GeneratedCanvasEffectProperty]"),
+ diagnostic);
+ }
+ }
+
+ ///
+ /// Tries to get an for the [GeneratedCanvasEffectProperty] attribute.
+ ///
+ /// The original document being fixed.
+ /// The instance for the current compilation.
+ /// The resulting attribute list, if successfully retrieved.
+ /// Whether could be retrieved successfully.
+ private static bool TryGetGeneratedCanvasEffectPropertyAttributeList(
+ Document document,
+ SemanticModel semanticModel,
+ [NotNullWhen(true)] out AttributeListSyntax? generatedCanvasEffectPropertyAttributeList)
+ {
+ // Make sure we can resolve the '[GeneratedCanvasEffectProperty]' attribute
+ if (semanticModel.Compilation.GetTypeByMetadataName(WellKnownTypeNames.GeneratedCanvasEffectPropertyAttribute) is not INamedTypeSymbol attributeSymbol)
+ {
+ generatedCanvasEffectPropertyAttributeList = null;
+
+ return false;
+ }
+
+ SyntaxGenerator syntaxGenerator = SyntaxGenerator.GetGenerator(document);
+
+ // Create the attribute syntax for the new '[GeneratedCanvasEffectProperty]' attribute here too
+ SyntaxNode attributeTypeSyntax = syntaxGenerator.TypeExpression(attributeSymbol).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
+
+ generatedCanvasEffectPropertyAttributeList = (AttributeListSyntax)syntaxGenerator.Attribute(attributeTypeSyntax);
+
+ return true;
+ }
+
+ ///
+ /// Updates an for the [GeneratedCanvasEffectProperty] attribute with the right default value.
+ ///
+ /// The original document being fixed.
+ /// The instance for the current compilation.
+ /// The with the attribute to add.
+ /// The invalidation type to use.
+ /// The updated attribute syntax.
+ private static AttributeListSyntax UpdateGeneratedCanvasEffectPropertyAttributeList(
+ Document document,
+ SemanticModel semanticModel,
+ AttributeListSyntax generatedCanvasEffectPropertyAttributeList,
+ int invalidationType)
+ {
+ // If the invalidation type is not the default, set it in the attribute.
+ // We extract the generated attribute so we can add the new argument.
+ // It's important to reuse it, as it has the "add usings" annotation.
+ if (invalidationType == 1)
+ {
+ // Try to resolve the attribute type, if present (this should always be the case)
+ if (semanticModel.Compilation.GetTypeByMetadataName(WellKnownTypeNames.CanvasEffectInvalidationType) is INamedTypeSymbol enumTypeSymbol)
+ {
+ SyntaxGenerator syntaxGenerator = SyntaxGenerator.GetGenerator(document);
+
+ // Create the identifier syntax for the enum type, with the right annotations
+ SyntaxNode enumTypeSyntax = syntaxGenerator.TypeExpression(enumTypeSymbol).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
+
+ // Create the member access expression for the target enum type.
+ // We only ever take this path for the 'Creation' invalidation type.
+ SyntaxNode enumMemberAccessExpressionSyntax = syntaxGenerator.MemberAccessExpression(enumTypeSyntax, "Creation");
+
+ // Create the attribute argument to insert
+ SyntaxNode attributeArgumentSyntax = syntaxGenerator.AttributeArgument(enumMemberAccessExpressionSyntax);
+
+ // Actually add the argument to the existing attribute syntax
+ return (AttributeListSyntax)syntaxGenerator.AddAttributeArguments(generatedCanvasEffectPropertyAttributeList, [attributeArgumentSyntax]);
+ }
+
+ // This failed... For some reason. Use the fully qualified type name as a last resort.
+ return
+ AttributeList(SingletonSeparatedList(
+ generatedCanvasEffectPropertyAttributeList.Attributes[0]
+ .AddArgumentListArguments(
+ AttributeArgument(ParseExpression(
+ $"global::{WellKnownTypeNames.CanvasEffectInvalidationType}.Creation")))));
+ }
+
+ // If we have no custom invalidation type, we can just reuse the attribute with no changes
+ return generatedCanvasEffectPropertyAttributeList;
+ }
+
+ ///
+ /// Applies the code fix to a target identifier and returns an updated document.
+ ///
+ /// The original document being fixed.
+ /// The instance for the current compilation.
+ /// The original tree root belonging to the current document.
+ /// The for the property being updated.
+ /// The invalidation type to use.
+ /// An updated document with the applied code fix, and being replaced with a partial property.
+ private static async Task ConvertToPartialProperty(
+ Document document,
+ SemanticModel semanticModel,
+ SyntaxNode root,
+ PropertyDeclarationSyntax propertyDeclaration,
+ int invalidationType)
+ {
+ await Task.CompletedTask;
+
+ // If we can't generate the new attribute list, bail (this should never happen)
+ if (!TryGetGeneratedCanvasEffectPropertyAttributeList(document, semanticModel, out AttributeListSyntax? generatedCanvasEffectPropertyAttributeList))
+ {
+ return document;
+ }
+
+ // Create an editor to perform all mutations
+ SyntaxEditor syntaxEditor = new(root, document.Project.Solution.Workspace.Services);
+
+ ConvertToPartialProperty(
+ document,
+ semanticModel,
+ propertyDeclaration,
+ generatedCanvasEffectPropertyAttributeList,
+ syntaxEditor,
+ invalidationType);
+
+ // Create the new document with the single change
+ return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
+ }
+
+ ///
+ /// Applies the code fix to a target identifier and returns an updated document.
+ ///
+ /// The original document being fixed.
+ /// The instance for the current compilation.
+ /// The for the property being updated.
+ /// The with the attribute to add.
+ /// The instance to use.
+ /// The invalidation type to use.
+ /// An updated document with the applied code fix, and being replaced with a partial property.
+ private static void ConvertToPartialProperty(
+ Document document,
+ SemanticModel semanticModel,
+ PropertyDeclarationSyntax propertyDeclaration,
+ AttributeListSyntax generatedCanvasEffectPropertyAttributeList,
+ SyntaxEditor syntaxEditor,
+ int invalidationType)
+ {
+ // Update the attribute to insert with the invalidation type, if needed
+ generatedCanvasEffectPropertyAttributeList = UpdateGeneratedCanvasEffectPropertyAttributeList(
+ document,
+ semanticModel,
+ generatedCanvasEffectPropertyAttributeList,
+ invalidationType);
+
+ // Start setting up the updated attribute lists
+ SyntaxList attributeLists = propertyDeclaration.AttributeLists;
+
+ if (attributeLists is [AttributeListSyntax firstAttributeListSyntax, ..])
+ {
+ // Remove the trivia from the original first attribute
+ attributeLists = attributeLists.Replace(
+ nodeInList: firstAttributeListSyntax,
+ newNode: firstAttributeListSyntax.WithoutTrivia());
+
+ // If the property has at least an attribute list, move the trivia from it to the new attribute
+ generatedCanvasEffectPropertyAttributeList = generatedCanvasEffectPropertyAttributeList.WithTriviaFrom(firstAttributeListSyntax);
+
+ // Insert the new attribute
+ attributeLists = attributeLists.Insert(0, generatedCanvasEffectPropertyAttributeList);
+ }
+ else
+ {
+ // Otherwise (there are no attribute lists), transfer the trivia to the new (only) attribute list
+ generatedCanvasEffectPropertyAttributeList = generatedCanvasEffectPropertyAttributeList.WithTriviaFrom(propertyDeclaration);
+
+ // Save the new attribute list
+ attributeLists = attributeLists.Add(generatedCanvasEffectPropertyAttributeList);
+ }
+
+ // Get a new property that is partial and with semicolon token accessors
+ PropertyDeclarationSyntax updatedPropertyDeclaration =
+ propertyDeclaration
+ .AddModifiers(Token(SyntaxKind.PartialKeyword))
+ .WithoutLeadingTrivia()
+ .WithAttributeLists(attributeLists)
+ .WithAdditionalAnnotations(Formatter.Annotation)
+ .WithAccessorList(AccessorList(List(
+ [
+ // Keep the accessors (so we can easily keep all trivia, modifiers, attributes, etc.) but make them semicolon only
+ propertyDeclaration.AccessorList!.Accessors[0]
+ .WithBody(null)
+ .WithExpressionBody(null)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ .WithAdditionalAnnotations(Formatter.Annotation),
+ propertyDeclaration.AccessorList!.Accessors[1]
+ .WithBody(null)
+ .WithExpressionBody(null)
+ .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+ .WithTrailingTrivia(propertyDeclaration.AccessorList.Accessors[1].GetTrailingTrivia())
+ .WithAdditionalAnnotations(Formatter.Annotation)
+ ])).WithTrailingTrivia(propertyDeclaration.AccessorList.GetTrailingTrivia()));
+
+ syntaxEditor.ReplaceNode(propertyDeclaration, updatedPropertyDeclaration);
+
+ // Find the parent type for the property
+ TypeDeclarationSyntax typeDeclaration = propertyDeclaration.FirstAncestorOrSelf()!;
+
+ // Make sure it's partial (we create the updated node in the function to preserve the updated property declaration).
+ // If we created it separately and replaced it, the whole tree would also be replaced, and we'd lose the new property.
+ if (!typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword))
+ {
+ syntaxEditor.ReplaceNode(typeDeclaration, static (node, generator) => generator.WithModifiers(node, generator.GetModifiers(node).WithPartial(true)));
+ }
+ }
+
+ ///
+ /// A custom with the logic from .
+ ///
+ private sealed class FixAllProvider : DocumentBasedFixAllProvider
+ {
+ ///
+ protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics)
+ {
+ // Get the semantic model, as we need to resolve symbols
+ if (await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false) is not SemanticModel semanticModel)
+ {
+ return document;
+ }
+
+ // Get the document root (this should always succeed)
+ if (await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false) is not SyntaxNode root)
+ {
+ return document;
+ }
+
+ // If we can't generate the new attribute list, bail (this should never happen)
+ if (!TryGetGeneratedCanvasEffectPropertyAttributeList(document, semanticModel, out AttributeListSyntax? generatedCanvasEffectPropertyAttributeList))
+ {
+ return document;
+ }
+
+ // Create an editor to perform all mutations (across all edits in the file)
+ SyntaxEditor syntaxEditor = new(root, fixAllContext.Solution.Services);
+
+ foreach (Diagnostic diagnostic in diagnostics)
+ {
+ // Get the current property declaration for the diagnostic
+ if (root.FindNode(diagnostic.Location.SourceSpan) is not PropertyDeclarationSyntax propertyDeclaration)
+ {
+ continue;
+ }
+
+ // Retrieve the properties passed by the analyzer
+ if (!int.TryParse(diagnostic.Properties[UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer.InvalidationTypePropertyName], out int invalidationType))
+ {
+ continue;
+ }
+
+ ConvertToPartialProperty(
+ document,
+ semanticModel,
+ propertyDeclaration,
+ generatedCanvasEffectPropertyAttributeList,
+ syntaxEditor,
+ invalidationType);
+ }
+
+ return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/ComputeSharp.D2D1.UI.SourceGenerators.projitems b/src/ComputeSharp.D2D1.UI.SourceGenerators/ComputeSharp.D2D1.UI.SourceGenerators.projitems
index b21e1a362..02d5da44e 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/ComputeSharp.D2D1.UI.SourceGenerators.projitems
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/ComputeSharp.D2D1.UI.SourceGenerators.projitems
@@ -16,6 +16,7 @@
+
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/Constants/WellKnownTypeNames.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/Constants/WellKnownTypeNames.cs
index 4abbee9d6..d79fbb566 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/Constants/WellKnownTypeNames.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/Constants/WellKnownTypeNames.cs
@@ -10,7 +10,7 @@ namespace ComputeSharp.D2D1.WinUI.SourceGenerators.Constants;
internal static class WellKnownTypeNames
{
///
- /// The fully qualified type name for the [GeneratedCanvasEffectPropertyAttribute] type.
+ /// The fully qualified type name for the [GeneratedCanvasEffectProperty] type.
///
public const string GeneratedCanvasEffectPropertyAttribute =
#if WINDOWS_UWP
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/Analyzers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/Analyzers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer.cs
new file mode 100644
index 000000000..b34fdb6fd
--- /dev/null
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/Analyzers/UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer.cs
@@ -0,0 +1,356 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+#if WINDOWS_UWP
+using ComputeSharp.D2D1.Uwp.SourceGenerators.Constants;
+#else
+using ComputeSharp.D2D1.WinUI.SourceGenerators.Constants;
+#endif
+using ComputeSharp.SourceGeneration.Extensions;
+using ComputeSharp.SourceGeneration.Helpers;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+#if WINDOWS_UWP
+using static ComputeSharp.D2D1.Uwp.SourceGenerators.DiagnosticDescriptors;
+#else
+using static ComputeSharp.D2D1.WinUI.SourceGenerators.DiagnosticDescriptors;
+#endif
+
+#if WINDOWS_UWP
+namespace ComputeSharp.D2D1.Uwp.SourceGenerators;
+#else
+namespace ComputeSharp.D2D1.WinUI.SourceGenerators;
+#endif
+
+///
+/// A diagnostic analyzer that generates a suggestion whenever [GeneratedCanvasEffectProperty] is used on a semi-auto property when a partial property could be used instead.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ /// The number of pooled flags per stack (ie. how many properties we expect on average per type).
+ ///
+ private const int NumberOfPooledFlagsPerStack = 20;
+
+ ///
+ /// Shared pool for instances.
+ ///
+ [SuppressMessage("MicrosoftCodeAnalysisPerformance", "RS1008", Justification = "This is a pool of (empty) dictionaries, it is not actually storing compilation data.")]
+ private static readonly ObjectPool> PropertyMapPool = new(static () => new Dictionary(SymbolEqualityComparer.Default));
+
+ ///
+ /// Shared pool for -s of flags, one per type being processed.
+ ///
+ private static readonly ObjectPool> PropertyFlagsStackPool = new(CreatePropertyFlagsStack);
+
+ ///
+ /// The property name for the serialized invalidation type.
+ ///
+ public const string InvalidationTypePropertyName = "InvalidationType";
+
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [UseGeneratedCanvasEffectPropertyOnSemiAutoProperty];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // Using [GeneratedCanvasEffectProperty] on partial properties is only supported when using C# preview.
+ // As such, if that is not the case, return immediately, as no diagnostic should be produced.
+ if (!context.Compilation.IsLanguageVersionPreview())
+ {
+ return;
+ }
+
+ // Get the [GeneratedCanvasEffectProperty] and CanvasEffect symbols
+ if (context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.GeneratedCanvasEffectPropertyAttribute) is not { } generatedCanvasEffectPropertyAttributeSymbol ||
+ context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.CanvasEffect) is not { } canvasEffectSymbol)
+ {
+ return;
+ }
+
+ // Get the symbol for the SetPropertyAndInvalidateEffectGraph method as well
+ if (!TryGetSetPropertyMethodSymbol(canvasEffectSymbol, out IMethodSymbol? setPropertySymbol))
+ {
+ return;
+ }
+
+ context.RegisterSymbolStartAction(context =>
+ {
+ // We only care about types that could derive from ObservableObject
+ if (context.Symbol is not INamedTypeSymbol { IsStatic: false, IsReferenceType: true, BaseType.SpecialType: not SpecialType.System_Object } typeSymbol)
+ {
+ return;
+ }
+
+ // If the type does not derive from ObservableObject, ignore it
+ if (!typeSymbol.InheritsFromType(canvasEffectSymbol))
+ {
+ return;
+ }
+
+ Dictionary propertyMap = PropertyMapPool.Allocate();
+ Stack propertyFlagsStack = PropertyFlagsStackPool.Allocate();
+
+ // Crawl all members to discover properties that might be of interest
+ foreach (ISymbol memberSymbol in typeSymbol.GetMembers())
+ {
+ // We're only looking for properties that might be valid candidates for conversion
+ if (memberSymbol is not IPropertySymbol
+ {
+ IsStatic: false,
+ IsPartialDefinition: false,
+ PartialDefinitionPart: null,
+ PartialImplementationPart: null,
+ ReturnsByRef: false,
+ ReturnsByRefReadonly: false,
+ Type.IsRefLikeType: false,
+ GetMethod: not null,
+ SetMethod.IsInitOnly: false
+ } propertySymbol)
+ {
+ continue;
+ }
+
+ // We can safely ignore properties that already have [GeneratedCanvasEffectProperty].
+ // This is because in that case, the other analyzer will already emit an error.
+ if (propertySymbol.HasAttributeWithType(generatedCanvasEffectPropertyAttributeSymbol))
+ {
+ continue;
+ }
+
+ // Take an array from the stack or create a new one otherwise
+ byte[] flags = propertyFlagsStack.Count > 0
+ ? propertyFlagsStack.Pop()
+ : new byte[2];
+
+ // Track the property for later
+ propertyMap.Add(propertySymbol, flags);
+ }
+
+ // We want to process both accessors, where we specifically need both the syntax
+ // and their semantic model to verify what they're doing. We can use a code callback.
+ context.RegisterOperationBlockAction(context =>
+ {
+ // Make sure the current symbol is a property accessor
+ if (context.OwningSymbol is not IMethodSymbol { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet, AssociatedSymbol: IPropertySymbol propertySymbol })
+ {
+ return;
+ }
+
+ // If so, check that we are actually processing one of the properties we care about
+ if (!propertyMap.TryGetValue(propertySymbol, out byte[]? validFlags))
+ {
+ return;
+ }
+
+ // Handle the 'get' logic
+ if (SymbolEqualityComparer.Default.Equals(propertySymbol.GetMethod, context.OwningSymbol))
+ {
+ // We expect a top-level block operation, that immediately returns an expression
+ if (context.OperationBlocks is not [IBlockOperation { Operations: [IReturnOperation returnOperation] }])
+ {
+ return;
+ }
+
+ // Next, we expect the return to produce a field reference
+ if (returnOperation is not { ReturnedValue: IFieldReferenceOperation fieldReferenceOperation })
+ {
+ return;
+ }
+
+ // The field has to be implicitly declared and not constant (and not static)
+ if (fieldReferenceOperation.Field is not { IsImplicitlyDeclared: true, IsStatic: false } fieldSymbol)
+ {
+ return;
+ }
+
+ // Validate tha the field is indeed 'field' (it will be associated with the property)
+ if (!SymbolEqualityComparer.Default.Equals(fieldSymbol.AssociatedSymbol, propertySymbol))
+ {
+ return;
+ }
+
+ // The 'get' accessor is valid
+ validFlags[0] = 1;
+ }
+ else if (SymbolEqualityComparer.Default.Equals(propertySymbol.SetMethod, context.OwningSymbol))
+ {
+ // We expect a top-level block operation, that immediately performs an invocation
+ if (context.OperationBlocks is not [IBlockOperation { Operations: [IExpressionStatementOperation { Operation: IInvocationOperation invocationOperation }] }])
+ {
+ return;
+ }
+
+ // Brief filtering of the target method, also get the original definition
+ if (invocationOperation.TargetMethod is not { Name: "SetPropertyAndInvalidateEffectGraph", IsGenericMethod: true, IsStatic: false } methodSymbol)
+ {
+ return;
+ }
+
+ // First, check that we're calling 'CanvasEffect.SetPropertyAndInvalidateEffectGraph'
+ if (!SymbolEqualityComparer.Default.Equals(methodSymbol.ConstructedFrom, setPropertySymbol))
+ {
+ return;
+ }
+
+ // We matched the method, now let's validate the arguments
+ if (invocationOperation.Arguments is not [{ } locationArgument, { } valueArgument, { } invalidationTypeArgument])
+ {
+ return;
+ }
+
+ // The field has to be implicitly declared and not constant (and not static)
+ if (locationArgument.Value is not IFieldReferenceOperation { Field: { IsImplicitlyDeclared: true, IsStatic: false } fieldSymbol })
+ {
+ return;
+ }
+
+ // Validate tha the field is indeed 'field' (it will be associated with the property)
+ if (!SymbolEqualityComparer.Default.Equals(fieldSymbol.AssociatedSymbol, propertySymbol))
+ {
+ return;
+ }
+
+ // The value is just the 'value' keyword
+ if (valueArgument.Value is not IParameterReferenceOperation { Syntax: IdentifierNameSyntax { Identifier.Text: "value" } })
+ {
+ return;
+ }
+
+ // The invalidation mode can either be the default value...
+ if (invalidationTypeArgument is { IsImplicit: true, ArgumentKind: ArgumentKind.DefaultValue })
+ {
+ validFlags[1] = 1;
+ }
+ else if (invalidationTypeArgument is { Value.ConstantValue: { HasValue: true, Value: byte mode } } && mode is 0 or 1)
+ {
+ // ...Or is has to be set explicitly to one of the two supported values
+ validFlags[1] = (byte)(mode + 1);
+ }
+ }
+ });
+
+ // We also need to track getters which have no body, and we need syntax for that
+ context.RegisterSyntaxNodeAction(context =>
+ {
+ // Let's just make sure we do have a property symbol
+ if (context.ContainingSymbol is not IPropertySymbol { GetMethod: not null } propertySymbol)
+ {
+ return;
+ }
+
+ // Lookup the property to get its flags
+ if (!propertyMap.TryGetValue(propertySymbol, out byte[]? validFlags))
+ {
+ return;
+ }
+
+ // We expect two accessors, skip if otherwise (the setter will be validated by the other callback)
+ if (context.Node is not PropertyDeclarationSyntax { AccessorList.Accessors: [{ } firstAccessor, { } secondAccessor] })
+ {
+ return;
+ }
+
+ // Check that either of them is a semicolon token 'get;' accessor (it can be in either position)
+ if ((firstAccessor.IsKind(SyntaxKind.GetAccessorDeclaration) &&
+ firstAccessor.SemicolonToken.IsKind(SyntaxKind.SemicolonToken) &&
+ firstAccessor.ExpressionBody is null) ||
+ (secondAccessor.IsKind(SyntaxKind.GetAccessorDeclaration) &&
+ secondAccessor.SemicolonToken.IsKind(SyntaxKind.SemicolonToken) &&
+ secondAccessor.ExpressionBody is null))
+ {
+ validFlags[0] = 1;
+ }
+ }, SyntaxKind.PropertyDeclaration);
+
+ // Finally, we can consume this information when we finish processing the symbol
+ context.RegisterSymbolEndAction(context =>
+ {
+ // Emit a diagnostic for each property that was a valid match
+ foreach (KeyValuePair pair in propertyMap)
+ {
+ if (pair.Value is [1, 1 or 2])
+ {
+ // Shift back the index to match the actual enum values, to simplify the code fixer. Here we're adding 1 to
+ // signal "the setter is valid", but we want to hide this implementation detail to downstream consumers.
+ int invalidationType = pair.Value[1] - 1;
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ UseGeneratedCanvasEffectPropertyOnSemiAutoProperty,
+ pair.Key.Locations.FirstOrDefault(),
+ ImmutableDictionary.Create().Add(InvalidationTypePropertyName, invalidationType.ToString()),
+ pair.Key));
+ }
+ }
+
+ // Before clearing the dictionary, move back all values to the stack
+ foreach (byte[] propertyFlags in propertyMap.Values)
+ {
+ // Make sure the array is cleared before returning it
+ propertyFlags.AsSpan().Clear();
+
+ propertyFlagsStack.Push(propertyFlags);
+ }
+
+ // We are now done processing the symbol, we can return the dictionary.
+ // Note that we must clear it before doing so to avoid leaks and issues.
+ propertyMap.Clear();
+
+ PropertyMapPool.Free(propertyMap);
+
+ // Also do the same for the stack, except we don't need to clean it (since it roots no compilation objects)
+ PropertyFlagsStackPool.Free(propertyFlagsStack);
+ });
+ }, SymbolKind.NamedType);
+ });
+ }
+
+ ///
+ /// Tries to get the symbol for the target SetPropertyAndInvalidateEffectGraph method this analyzer looks for.
+ ///
+ /// The symbol for CanvasEffect.
+ /// The resulting method symbol, if found (this should always be the case).
+ /// Whether could be resolved correctly.
+ private static bool TryGetSetPropertyMethodSymbol(INamedTypeSymbol canvasEffectSymbol, [NotNullWhen(true)] out IMethodSymbol? setPropertySymbol)
+ {
+ foreach (ISymbol symbol in canvasEffectSymbol.GetMembers("SetPropertyAndInvalidateEffectGraph"))
+ {
+ // There's only one method with this name, so we can just return it directly
+ setPropertySymbol = (IMethodSymbol)symbol;
+
+ return true;
+ }
+
+ setPropertySymbol = null;
+
+ return false;
+ }
+
+ ///
+ /// Produces a new instance to pool.
+ ///
+ /// The resulting instance to use.
+ private static Stack CreatePropertyFlagsStack()
+ {
+ static IEnumerable EnumerateFlags()
+ {
+ for (int i = 0; i < NumberOfPooledFlagsPerStack; i++)
+ {
+ yield return new byte[2];
+ }
+ }
+
+ return new(EnumerateFlags());
+ }
+}
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
index 4a745df32..8c0623325 100644
--- a/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/ComputeSharp.D2D1.UI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
@@ -31,6 +31,11 @@ internal static class DiagnosticDescriptors
"ComputeSharp.D2D1.WinUI.Effects";
#endif
+ ///
+ /// The diagnostic id for .
+ ///
+ public const string UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyId = $"{DiagnosticIdPrefix}0008";
+
///
/// Gets a for an invalid CanvasEffect property.
///
@@ -142,4 +147,20 @@ internal static class DiagnosticDescriptors
isEnabledByDefault: true,
description: "The C# language version must be set to 'preview' when using [GeneratedComputeShaderDescriptor] for the source generators to emit valid code (the preview option must be set in the .csproj/.props file).",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
+
+ ///
+ /// Gets a for when a semi-auto property can be converted to use [GeneratedCanvasEffectProperty] instead.
+ ///
+ /// Format: "The semi-auto property "{0}" can be converted to a partial property using [GeneratedCanvasEffectProperty], which is recommended (doing so makes the code less verbose and results in more optimized code)".
+ ///
+ ///
+ public static readonly DiagnosticDescriptor UseGeneratedCanvasEffectPropertyOnSemiAutoProperty = new(
+ id: UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyId,
+ title: "Prefer using [GeneratedCanvasEffectProperty] over semi-auto properties",
+ messageFormat: """The semi-auto property "{0}" can be converted to a partial property using [GeneratedCanvasEffectProperty], which is recommended (doing so makes the code less verbose and results in more optimized code)""",
+ category: DiagnosticCategory,
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true,
+ description: "Semi-auto properties should be converted to partial properties using [GeneratedCanvasEffectProperty] when possible, which is recommended (doing so makes the code less verbose and results in more optimized code).",
+ helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");
}
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.Uwp.CodeFixers/ComputeSharp.D2D1.Uwp.CodeFixers.csproj b/src/ComputeSharp.D2D1.Uwp.CodeFixers/ComputeSharp.D2D1.Uwp.CodeFixers.csproj
new file mode 100644
index 000000000..725f187a7
--- /dev/null
+++ b/src/ComputeSharp.D2D1.Uwp.CodeFixers/ComputeSharp.D2D1.Uwp.CodeFixers.csproj
@@ -0,0 +1,16 @@
+
+
+ netstandard2.0
+ $(DefineConstants);WINDOWS_UWP;D2D1_UI_SOURCE_GENERATOR
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ComputeSharp.D2D1.Uwp.SourceGenerators/AnalyzerReleases.Shipped.md b/src/ComputeSharp.D2D1.Uwp.SourceGenerators/AnalyzerReleases.Shipped.md
index 8c38544e6..d1feb69ca 100644
--- a/src/ComputeSharp.D2D1.Uwp.SourceGenerators/AnalyzerReleases.Shipped.md
+++ b/src/ComputeSharp.D2D1.Uwp.SourceGenerators/AnalyzerReleases.Shipped.md
@@ -14,3 +14,4 @@ CMPSD2DUWP0004 | ComputeSharp.D2D1.Uwp.Effects | Error | [Documentation](https:/
CMPSD2DUWP0005 | ComputeSharp.D2D1.Uwp.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2DUWP0006 | ComputeSharp.D2D1.Uwp.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2DUWP0007 | ComputeSharp.D2D1.Uwp.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
+CMPSD2DUWP0008 | ComputeSharp.D2D1.Uwp.Effects | Info | [Documentation](https://github.com/Sergio0694/ComputeSharp)
diff --git a/src/ComputeSharp.D2D1.Uwp.SourceGenerators/ComputeSharp.D2D1.Uwp.SourceGenerators.csproj b/src/ComputeSharp.D2D1.Uwp.SourceGenerators/ComputeSharp.D2D1.Uwp.SourceGenerators.csproj
index 2500e6831..29b983fce 100644
--- a/src/ComputeSharp.D2D1.Uwp.SourceGenerators/ComputeSharp.D2D1.Uwp.SourceGenerators.csproj
+++ b/src/ComputeSharp.D2D1.Uwp.SourceGenerators/ComputeSharp.D2D1.Uwp.SourceGenerators.csproj
@@ -8,7 +8,7 @@
-
+
@@ -18,4 +18,9 @@
+
+
+
+
+
diff --git a/src/ComputeSharp.D2D1.Uwp/ComputeSharp.D2D1.Uwp.targets b/src/ComputeSharp.D2D1.Uwp/ComputeSharp.D2D1.Uwp.targets
index 976c3a7ca..6ce36516b 100644
--- a/src/ComputeSharp.D2D1.Uwp/ComputeSharp.D2D1.Uwp.targets
+++ b/src/ComputeSharp.D2D1.Uwp/ComputeSharp.D2D1.Uwp.targets
@@ -7,7 +7,7 @@
-
+
@(ComputeSharpD2D1UwpCurrentCompilerAssemblyIdentity->'%(Version)')
- true
+ true
-
+
-
+
+
diff --git a/src/ComputeSharp.D2D1.WinUI.CodeFixers/ComputeSharp.D2D1.WinUI.CodeFixers.csproj b/src/ComputeSharp.D2D1.WinUI.CodeFixers/ComputeSharp.D2D1.WinUI.CodeFixers.csproj
new file mode 100644
index 000000000..f305ea879
--- /dev/null
+++ b/src/ComputeSharp.D2D1.WinUI.CodeFixers/ComputeSharp.D2D1.WinUI.CodeFixers.csproj
@@ -0,0 +1,16 @@
+
+
+ netstandard2.0
+ $(DefineConstants);D2D1_UI_SOURCE_GENERATOR
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ComputeSharp.D2D1.WinUI.SourceGenerators/AnalyzerReleases.Shipped.md b/src/ComputeSharp.D2D1.WinUI.SourceGenerators/AnalyzerReleases.Shipped.md
index a67085fd6..6573ce3d2 100644
--- a/src/ComputeSharp.D2D1.WinUI.SourceGenerators/AnalyzerReleases.Shipped.md
+++ b/src/ComputeSharp.D2D1.WinUI.SourceGenerators/AnalyzerReleases.Shipped.md
@@ -14,3 +14,4 @@ CMPSD2DWINUI0004 | ComputeSharp.D2D1.WinUI.Effects | Error | [Documentation](htt
CMPSD2DWINUI0005 | ComputeSharp.D2D1.WinUI.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2DWINUI0006 | ComputeSharp.D2D1.WinUI.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2DWINUI0007 | ComputeSharp.D2D1.WinUI.Effects | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
+CMPSD2DWINUI0008 | ComputeSharp.D2D1.WinUI.Effects | Info | [Documentation](https://github.com/Sergio0694/ComputeSharp)
diff --git a/src/ComputeSharp.D2D1.WinUI.SourceGenerators/ComputeSharp.D2D1.WinUI.SourceGenerators.csproj b/src/ComputeSharp.D2D1.WinUI.SourceGenerators/ComputeSharp.D2D1.WinUI.SourceGenerators.csproj
index 1781f0d7c..338ece165 100644
--- a/src/ComputeSharp.D2D1.WinUI.SourceGenerators/ComputeSharp.D2D1.WinUI.SourceGenerators.csproj
+++ b/src/ComputeSharp.D2D1.WinUI.SourceGenerators/ComputeSharp.D2D1.WinUI.SourceGenerators.csproj
@@ -8,7 +8,7 @@
-
+
@@ -18,4 +18,9 @@
+
+
+
+
+
diff --git a/src/ComputeSharp.D2D1.WinUI/ComputeSharp.D2D1.WinUI.targets b/src/ComputeSharp.D2D1.WinUI/ComputeSharp.D2D1.WinUI.targets
index e178811f9..792dd290b 100644
--- a/src/ComputeSharp.D2D1.WinUI/ComputeSharp.D2D1.WinUI.targets
+++ b/src/ComputeSharp.D2D1.WinUI/ComputeSharp.D2D1.WinUI.targets
@@ -7,7 +7,7 @@
-
+
@(ComputeSharpD2D1WinUICurrentCompilerAssemblyIdentity->'%(Version)')
- true
+ true
-
+
-
+
+
diff --git a/src/ComputeSharp.D2D1/ComputeSharp.D2D1.targets b/src/ComputeSharp.D2D1/ComputeSharp.D2D1.targets
index 6e684e620..7b6a59ac9 100644
--- a/src/ComputeSharp.D2D1/ComputeSharp.D2D1.targets
+++ b/src/ComputeSharp.D2D1/ComputeSharp.D2D1.targets
@@ -42,7 +42,7 @@
emitting this manually lets us customize the message to inform developers as to why exactly the generators have been
disabled, and that ComputeSharp will not work at all unless a more up to date IDE or compiler version are used.
-->
-
+
diff --git a/src/ComputeSharp/ComputeSharp.targets b/src/ComputeSharp/ComputeSharp.targets
index c4a233093..566b5c97a 100644
--- a/src/ComputeSharp/ComputeSharp.targets
+++ b/src/ComputeSharp/ComputeSharp.targets
@@ -42,7 +42,7 @@
emitting this manually lets us customize the message to inform developers as to why exactly the generators have been
disabled, and that ComputeSharp will not work at all unless a more up to date IDE or compiler version are used.
-->
-
+
diff --git a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/ComputeSharp.D2D1.Tests.SourceGenerators.csproj b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/ComputeSharp.D2D1.Tests.SourceGenerators.csproj
index 479f672ef..ebee105a0 100644
--- a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/ComputeSharp.D2D1.Tests.SourceGenerators.csproj
+++ b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/ComputeSharp.D2D1.Tests.SourceGenerators.csproj
@@ -19,7 +19,7 @@
-
+
@@ -30,7 +30,7 @@
-
+
diff --git a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator.cs b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator.cs
index c49e2d724..ec2379b31 100644
--- a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator.cs
+++ b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator.cs
@@ -1780,25 +1780,25 @@ file static class Data
/// The task for the operation.
private static async Task VerifyGeneratedDiagnosticsAsync(string source, (string Filename, string Source) result)
{
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
CSharpGeneratorTest.VerifySources(source, result);
}
diff --git a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator_Analyzers.cs b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator_Analyzers.cs
index fa1d3ccc8..71a93b3d6 100644
--- a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator_Analyzers.cs
+++ b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderDescriptorGenerator_Analyzers.cs
@@ -25,7 +25,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, allowUnsafeBlocks: false);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, allowUnsafeBlocks: false);
}
[TestMethod]
@@ -44,7 +44,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -112,7 +112,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -135,7 +135,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -156,7 +156,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -171,7 +171,7 @@ internal partial struct MyType
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -191,7 +191,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -214,7 +214,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -235,7 +235,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -257,7 +257,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -280,7 +280,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -304,7 +304,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -328,7 +328,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -351,7 +351,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -364,7 +364,7 @@ public async Task UnnecessaryD2DEnableRuntimeCompilation_OnAssembly_Warns()
[assembly: {|CMPSD2D0079:D2DEnableRuntimeCompilation|}]
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -388,7 +388,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -411,7 +411,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -433,7 +433,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -466,7 +466,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -489,7 +489,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -515,7 +515,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -538,7 +538,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -564,7 +564,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -590,7 +590,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -628,7 +628,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -640,7 +640,7 @@ public async Task InvalidPackMatrixColumnMajorOption_AssemblyLevel_WithColumnMaj
[assembly: {|CMPSD2D0044:D2DCompileOptions(D2D1CompileOptions.PackMatrixColumnMajor)|}]
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -661,7 +661,7 @@ public Float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -752,7 +752,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -773,7 +773,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -793,7 +793,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -814,7 +814,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -837,7 +837,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -859,7 +859,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -882,7 +882,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -910,7 +910,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -929,7 +929,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -952,7 +952,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -975,7 +975,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -999,7 +999,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1029,7 +1029,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1053,7 +1053,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1077,7 +1077,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1103,7 +1103,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1135,7 +1135,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1156,7 +1156,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1178,7 +1178,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1200,7 +1200,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1220,7 +1220,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1239,7 +1239,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1260,7 +1260,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1288,7 +1288,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1311,7 +1311,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1331,7 +1331,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1354,7 +1354,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1374,7 +1374,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1403,7 +1403,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1430,7 +1430,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1460,7 +1460,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1481,7 +1481,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1508,7 +1508,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1536,7 +1536,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -1562,6 +1562,6 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
}
\ No newline at end of file
diff --git a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderSourceGenerator_Analyzers.cs b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderSourceGenerator_Analyzers.cs
index 840b97131..1206ba94e 100644
--- a/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderSourceGenerator_Analyzers.cs
+++ b/tests/ComputeSharp.D2D1.Tests.SourceGenerators/Test_D2DPixelShaderSourceGenerator_Analyzers.cs
@@ -33,7 +33,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -69,7 +69,7 @@ public unsafe partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -98,7 +98,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -128,7 +128,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -156,7 +156,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -185,7 +185,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -215,7 +215,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -243,7 +243,7 @@ public partial class MyClass
}
"""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -262,6 +262,6 @@ public partial class MyClass
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
}
\ No newline at end of file
diff --git a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators.csproj b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators.csproj
index a053cda57..31d6a4c29 100644
--- a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators.csproj
+++ b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators.csproj
@@ -18,18 +18,19 @@
-
+
+
-
+
diff --git a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Helpers/CSharpCodeFixerTest{TAnalyzer,TCodeFixer}.cs b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Helpers/CSharpCodeFixerTest{TAnalyzer,TCodeFixer}.cs
new file mode 100644
index 000000000..54e832426
--- /dev/null
+++ b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Helpers/CSharpCodeFixerTest{TAnalyzer,TCodeFixer}.cs
@@ -0,0 +1,42 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+
+#pragma warning disable IDE0290
+
+namespace ComputeSharp.Tests.SourceGenerators.Helpers;
+
+///
+/// A custom that uses a specific C# language version to parse code.
+///
+/// The type of the analyzer to produce diagnostics.
+/// The type of code fix to test.
+internal sealed class CSharpCodeFixTest : CSharpCodeFixTest
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFixer : CodeFixProvider, new()
+{
+ ///
+ /// The C# language version to use to parse code.
+ ///
+ private readonly LanguageVersion languageVersion;
+
+ ///
+ /// Creates a new instance with the specified parameters.
+ ///
+ /// The C# language version to use to parse code.
+ public CSharpCodeFixTest(LanguageVersion languageVersion)
+ {
+ this.languageVersion = languageVersion;
+
+ TestState.AnalyzerConfigFiles.Add(("/.editorconfig", "[*]\nend_of_line = lf"));
+ }
+
+ ///
+ protected override ParseOptions CreateParseOptions()
+ {
+ return new CSharpParseOptions(this.languageVersion, DocumentationMode.Diagnose);
+ }
+}
diff --git a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator_Analyzers.cs b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator_Analyzers.cs
index 72ff08143..0b2196c94 100644
--- a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator_Analyzers.cs
+++ b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_CanvasEffectPropertyGenerator_Analyzers.cs
@@ -22,7 +22,7 @@ public partial class MyEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -38,7 +38,7 @@ public partial class MyEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -54,7 +54,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -70,7 +70,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -86,7 +86,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -104,7 +104,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -124,7 +124,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -142,7 +142,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -158,7 +158,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -174,7 +174,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -191,7 +191,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
[TestMethod]
@@ -208,7 +208,7 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -225,6 +225,233 @@ public abstract partial class MyEffect : CanvasEffect
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_NormalProperty_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name { get; set; }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_SimilarProperty_NotObservableObject_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : MyBaseCanvasEffect
+ {
+ public string Name
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+
+ public abstract class MyBaseCanvasEffect
+ {
+ protected void SetPropertyAndInvalidateEffectGraph(ref T location, T value, CanvasEffectInvalidationType invalidationType = CanvasEffectInvalidationType.Update)
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_NoGetter_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name
+ {
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_NoSetter_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name
+ {
+ get => field;
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_OtherLocation_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name
+ {
+ get => field;
+ set
+ {
+ string test = field;
+
+ SetPropertyAndInvalidateEffectGraph(ref test, value);
+ }
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_OtherValue_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name
+ {
+ get => field;
+ set
+ {
+ string test = "Bob";
+
+ SetPropertyAndInvalidateEffectGraph(ref field, test);
+ }
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_ValidProperty_WithGeneratedCanvasEffectProperty_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public string Name
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_GetAccessorWithExpressionBody_DoesNotWarn()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string Name
+ {
+ get => "Hello world";
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_ValidProperty_Warns()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public string {|CMPSD2DWINUI0008:Name|}
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_ValidProperty_WithModifiers_Warns()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public new string {|CMPSD2DWINUI0008:Name|}
+ {
+ get => field;
+ private set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
+ }
+
+ [TestMethod]
+ public async Task UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer_ValidProperty_WithBlocks_Warns()
+ {
+ const string source = """
+ using ComputeSharp.D2D1.WinUI;
+
+ public abstract partial class SampleCanvasEffect : CanvasEffect
+ {
+ public new string {|CMPSD2DWINUI0008:Name|}
+ {
+ get
+ {
+ return field;
+ }
+ private set
+ {
+ SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ }
+ """;
+
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, languageVersion: LanguageVersion.Preview);
}
}
\ No newline at end of file
diff --git a/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs
new file mode 100644
index 000000000..33ecd76c6
--- /dev/null
+++ b/tests/ComputeSharp.D2D1.WinUI.Tests.SourceGenerators/Test_UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer.cs
@@ -0,0 +1,767 @@
+extern alias Core;
+extern alias D2D1_WinUI;
+extern alias D2D1;
+
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Windows.Foundation;
+using Windows.UI.ViewManagement;
+using CSharpCodeFixTest = ComputeSharp.Tests.SourceGenerators.Helpers.CSharpCodeFixTest<
+ ComputeSharp.D2D1.WinUI.SourceGenerators.UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyAnalyzer,
+ ComputeSharp.D2D1.WinUI.SourceGenerators.UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer>;
+
+namespace ComputeSharp.D2D1.WinUI.Tests.SourceGenerators;
+
+[TestClass]
+public class Test_UseGeneratedCanvasEffectPropertyOnSemiAutoPropertyCodeFixer
+{
+ [TestMethod]
+ [DataRow("get;")]
+ [DataRow("get => field;")]
+ public async Task SimpleProperty_ImplicitInvalidationType(string getAccessor)
+ {
+ string original = $$"""
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ public string? [|Name|]
+ {
+ {{getAccessor}}
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ [DataRow("get;")]
+ [DataRow("get => field;")]
+ public async Task SimpleProperty_InvalidationTypeUpdate(string getAccessor)
+ {
+ string original = $$"""
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ public string? [|Name|]
+ {
+ {{getAccessor}}
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value, CanvasEffectInvalidationType.Update);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ [DataRow("get;")]
+ [DataRow("get => field;")]
+ public async Task SimpleProperty_InvalidationTypeCreation(string getAccessor)
+ {
+ string original = $$"""
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ public string? [|Name|]
+ {
+ {{getAccessor}}
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value, CanvasEffectInvalidationType.Creation);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty(CanvasEffectInvalidationType.Creation)]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_WithMissingUsingDirective()
+ {
+ const string original = """
+ namespace MyApp;
+
+ public abstract class MyEffect : ComputeSharp.D2D1.WinUI.CanvasEffect
+ {
+ public string? [|Name|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : ComputeSharp.D2D1.WinUI.CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_WithLeadingTrivia()
+ {
+ const string original = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ ///
+ /// This is a property.
+ ///
+ public string? [|Name|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ ///
+ /// This is a property.
+ ///
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_WithLeadingTrivia_AndAttribute()
+ {
+ const string original = """
+ using System;
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ ///
+ /// This is a property.
+ ///
+ [Test("Targeting property")]
+ [field: Test("Targeting field")]
+ public string? [|Name|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+
+ public class TestAttribute(string text) : Attribute;
+ """;
+
+ const string @fixed = """
+ using System;
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ ///
+ /// This is a property.
+ ///
+ [GeneratedCanvasEffectProperty]
+ [Test("Targeting property")]
+ [field: Test("Targeting field")]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+
+ public class TestAttribute(string text) : Attribute;
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_Multiple()
+ {
+ const string original = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ public string? [|FirstName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ public string? [|LastName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:FirstName|} { get; set; }
+
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:LastName|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_Multiple_OnlyTriggersOnFirstOne()
+ {
+ const string original = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ public string? [|FirstName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ private string? lastName;
+
+ public string? LastName
+ {
+ get => this.lastName;
+ set => SetPropertyAndInvalidateEffectGraph(ref this.lastName, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:FirstName|} { get; set; }
+
+ private string? lastName;
+
+ public string? LastName
+ {
+ get => this.lastName;
+ set => SetPropertyAndInvalidateEffectGraph(ref this.lastName, value);
+ }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_Multiple_OnlyTriggersOnSecondOne()
+ {
+ const string original = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ private string? firstName;
+
+ public string? FirstName
+ {
+ get => this.firstName;
+ set => SetPropertyAndInvalidateEffectGraph(ref this.firstName, value);
+ }
+
+ public string? [|LastName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ private string? firstName;
+
+ public string? FirstName
+ {
+ get => this.firstName;
+ set => SetPropertyAndInvalidateEffectGraph(ref this.firstName, value);
+ }
+
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:LastName|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_WithinPartialType()
+ {
+ const string original = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ public string? [|Name|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:Name|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_WithMissingUsingDirective_Multiple()
+ {
+ const string original = """
+ namespace MyApp;
+
+ public abstract class MyEffect : ComputeSharp.D2D1.WinUI.CanvasEffect
+ {
+ public string? [|FirstName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ public string? [|LastName|]
+ {
+ get;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+ """;
+
+ const string @fixed = """
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : ComputeSharp.D2D1.WinUI.CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:FirstName|} { get; set; }
+
+ [GeneratedCanvasEffectProperty]
+ public partial string? {|CS9248:LastName|} { get; set; }
+ }
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+
+ [TestMethod]
+ public async Task SimpleProperty_Multiple_MixedScenario()
+ {
+ const string original = """
+ using System;
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract class MyEffect : CanvasEffect
+ {
+ [Test("This is an attribute")]
+ public string [|Prop1|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ // Single comment
+ public string [|Prop2|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ ///
+ /// This is a property.
+ ///
+ public string [|Prop3|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ ///
+ /// This is another property.
+ ///
+ [Test("Another attribute")]
+ public string [|Prop4|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ // Some other single comment
+ [Test("Yet another attribute")]
+ public string [|Prop5|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ [Test("Attribute without trivia")]
+ public string [|Prop6|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+
+ public string [|Prop7|]
+ {
+ get => field;
+ set => SetPropertyAndInvalidateEffectGraph(ref field, value);
+ }
+ }
+
+ public class TestAttribute(string text) : Attribute;
+ """;
+
+ const string @fixed = """
+ using System;
+ using ComputeSharp.D2D1.WinUI;
+
+ namespace MyApp;
+
+ public abstract partial class MyEffect : CanvasEffect
+ {
+ [GeneratedCanvasEffectProperty]
+ [Test("This is an attribute")]
+ public partial string {|CS9248:Prop1|} { get; set; }
+
+ // Single comment
+ [GeneratedCanvasEffectProperty]
+ public partial string {|CS9248:Prop2|} { get; set; }
+
+ ///
+ /// This is a property.
+ ///
+ [GeneratedCanvasEffectProperty]
+ public partial string {|CS9248:Prop3|} { get; set; }
+
+ ///
+ /// This is another property.
+ ///
+ [GeneratedCanvasEffectProperty]
+ [Test("Another attribute")]
+ public partial string {|CS9248:Prop4|} { get; set; }
+
+ // Some other single comment
+ [GeneratedCanvasEffectProperty]
+ [Test("Yet another attribute")]
+ public partial string {|CS9248:Prop5|} { get; set; }
+
+ [GeneratedCanvasEffectProperty]
+ [Test("Attribute without trivia")]
+ public partial string {|CS9248:Prop6|} { get; set; }
+
+ [GeneratedCanvasEffectProperty]
+ public partial string {|CS9248:Prop7|} { get; set; }
+ }
+
+ public class TestAttribute(string text) : Attribute;
+ """;
+
+ CSharpCodeFixTest test = new(LanguageVersion.Preview)
+ {
+ TestCode = original,
+ FixedCode = @fixed,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
+ TestState = { AdditionalReferences =
+ {
+ MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Button).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1_WinUI::ComputeSharp.D2D1.WinUI.CanvasEffect).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(D2D1::ComputeSharp.D2D1.ID2D1PixelShader).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Float4).Assembly.Location)
+ } }
+ };
+
+ await test.RunAsync();
+ }
+}
diff --git a/tests/ComputeSharp.Tests.SourceGenerators/ComputeSharp.Tests.SourceGenerators.csproj b/tests/ComputeSharp.Tests.SourceGenerators/ComputeSharp.Tests.SourceGenerators.csproj
index 77c046aa8..04f866bc0 100644
--- a/tests/ComputeSharp.Tests.SourceGenerators/ComputeSharp.Tests.SourceGenerators.csproj
+++ b/tests/ComputeSharp.Tests.SourceGenerators/ComputeSharp.Tests.SourceGenerators.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs b/tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
similarity index 86%
rename from tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs
rename to tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
index 11cfc23f5..9cf93ad12 100644
--- a/tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs
+++ b/tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
@@ -22,7 +22,7 @@ namespace ComputeSharp.Tests.SourceGenerators.Helpers;
/// A custom that uses a specific C# language version to parse code.
///
/// The type of the analyzer to test.
-internal sealed class CSharpAnalyzerWithLanguageVersionTest : CSharpAnalyzerTest
+internal sealed class CSharpAnalyzerTest : CSharpAnalyzerTest
where TAnalyzer : DiagnosticAnalyzer, new()
{
///
@@ -36,11 +36,11 @@ internal sealed class CSharpAnalyzerWithLanguageVersionTest : CSharpA
private readonly LanguageVersion languageVersion;
///
- /// Creates a new instance with the specified paramaters.
+ /// Creates a new instance with the specified paramaters.
///
/// Whether to enable unsafe blocks.
/// The C# language version to use to parse code.
- private CSharpAnalyzerWithLanguageVersionTest(bool allowUnsafeBlocks, LanguageVersion languageVersion)
+ private CSharpAnalyzerTest(bool allowUnsafeBlocks, LanguageVersion languageVersion)
{
this.allowUnsafeBlocks = allowUnsafeBlocks;
this.languageVersion = languageVersion;
@@ -67,7 +67,7 @@ public static Task VerifyAnalyzerAsync(
bool allowUnsafeBlocks = true,
LanguageVersion languageVersion = LanguageVersion.CSharp12)
{
- CSharpAnalyzerWithLanguageVersionTest test = new(allowUnsafeBlocks, languageVersion) { TestCode = source };
+ CSharpAnalyzerTest test = new(allowUnsafeBlocks, languageVersion) { TestCode = source };
test.TestState.ReferenceAssemblies = ReferenceAssemblies.Net.Net80;
test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(Core::ComputeSharp.Hlsl).Assembly.Location));
diff --git a/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator.cs b/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator.cs
index 3f9c5fe9c..9c420dc41 100644
--- a/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator.cs
+++ b/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator.cs
@@ -864,18 +864,18 @@ protected override void Dispose(bool disposing)
/// The task for the operation.
private static async Task VerifyGeneratedDiagnosticsAsync(string source, (string Filename, string Source) result)
{
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
CSharpGeneratorTest.VerifySources(source, result);
}
diff --git a/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator_Analyzers.cs b/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator_Analyzers.cs
index 67f65a418..f4c7f5932 100644
--- a/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator_Analyzers.cs
+++ b/tests/ComputeSharp.Tests.SourceGenerators/Test_ComputeShaderDescriptorGenerator_Analyzers.cs
@@ -26,7 +26,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, allowUnsafeBlocks: false);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, allowUnsafeBlocks: false);
}
[TestMethod]
@@ -45,7 +45,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -64,7 +64,7 @@ public float4 Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -110,7 +110,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -135,7 +135,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -166,7 +166,7 @@ private struct Bar
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -187,7 +187,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -205,7 +205,7 @@ internal partial struct MyType
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -227,7 +227,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -249,7 +249,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -271,7 +271,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -289,7 +289,7 @@ internal partial struct MyType
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -311,7 +311,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -333,7 +333,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -355,7 +355,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -370,7 +370,7 @@ internal partial struct MyType
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -388,7 +388,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -409,7 +409,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -432,7 +432,7 @@ Float4 IComputeShader.Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -449,7 +449,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -467,7 +467,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -485,7 +485,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -503,7 +503,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -521,7 +521,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -546,7 +546,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -564,7 +564,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -630,7 +630,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -655,7 +655,7 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
[TestMethod]
@@ -720,6 +720,6 @@ public void Execute()
}
""";
- await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source);
+ await CSharpAnalyzerTest.VerifyAnalyzerAsync(source);
}
}
\ No newline at end of file