diff --git a/README.md b/README.md
index 333e9b9..5cd6605 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
# ReactiveUI.SourceGenerators
Use source generators to generate ReactiveUI objects.
+The minimum C# version is 12.0 and the minimum Visual Studio version is 17.8.0.
These Source Generators were designed to work in full with ReactiveUI V19.5.31 and newer supporting all features, currently:
- [Reactive]
@@ -15,6 +16,8 @@ These Source Generators were designed to work in full with ReactiveUI V19.5.31 a
Versions older than V19.5.31 to this:
- [ReactiveCommand] all options supported except Cancellation Token asnyc methods.
+For dot net framework 4.8 and older versions please add Polyfill or PolySharp package to your project to gain the IsExternalInit class and set the LangVersion to 12.0 or latest in your project file.
+
[analyzer codes](https://github.com/reactiveui/ReactiveUI.SourceGenerators/blob/main/src/ReactiveUI.SourceGenerators/AnalyzerReleases.Shipped.md)
# Historical ways
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 3d2f431..8f4d1d7 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -2,7 +2,7 @@
true
true
- 4.6.0
+ 4.8.0
@@ -13,8 +13,11 @@
+
+
+
diff --git a/src/Directory.build.props b/src/Directory.build.props
index d6a248a..31dd74b 100644
--- a/src/Directory.build.props
+++ b/src/Directory.build.props
@@ -17,7 +17,7 @@
https://github.com/reactiveui/ReactiveUI.SourceGenerators/releases
https://github.com/reactiveui/reactiveui.sourcegenerators
git
- $(NoWarn);IDE0060;IDE1006;IDE0130;VSSpell001;RS2007
+ $(NoWarn);IDE0060;IDE1006;IDE0130;VSSpell001;RS2007;NU5128
true
diff --git a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj
index ce8b968..21fe34d 100644
--- a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj
+++ b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj
@@ -19,9 +19,11 @@
-
+
+
+
diff --git a/src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.CodeFixes.csproj b/src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.SourceGenerators.Analyzers.CodeFixes.csproj
similarity index 67%
rename from src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.CodeFixes.csproj
rename to src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.SourceGenerators.Analyzers.CodeFixes.csproj
index b6fc69c..ba43a47 100644
--- a/src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.CodeFixes.csproj
+++ b/src/ReactiveUI.SourceGenerators.CodeFixers/ReactiveUI.SourceGenerators.Analyzers.CodeFixes.csproj
@@ -3,18 +3,24 @@
netstandard2.0
enable
+ latest
true
true
- latest
true
true
false
- A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI
+ true
+ true
+ true
+ $(NoWarn);RS1038;RS2007;NU5128
+ A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI
-
+
+
+
diff --git a/src/ReactiveUI.SourceGenerators.sln b/src/ReactiveUI.SourceGenerators.sln
index eb7af8c..4ed07cd 100644
--- a/src/ReactiveUI.SourceGenerators.sln
+++ b/src/ReactiveUI.SourceGenerators.sln
@@ -24,7 +24,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.SourceGenerators
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestLibs", "TestLibs", "{B86ED9C1-AFFB-4854-AD80-F4B4050CAD0A}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.CodeFixes", "ReactiveUI.SourceGenerators.CodeFixers\ReactiveUI.CodeFixes.csproj", "{C89EE66E-E1DC-4A31-9322-20D95CB0D74D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.SourceGenerators.Analyzers.CodeFixes", "ReactiveUI.SourceGenerators.CodeFixers\ReactiveUI.SourceGenerators.Analyzers.CodeFixes.csproj", "{C89EE66E-E1DC-4A31-9322-20D95CB0D74D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
index fd22411..efdaeaa 100644
--- a/src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
@@ -37,7 +37,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0002",
title: "Invalid ReactiveCommand method signature",
messageFormat: "The method {0}.{1} cannot be used to generate a command property, as its signature isn't compatible with any of the existing reactive command types",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Cannot apply [ReactiveCommand] to methods with a signature that doesn't match any of the existing reactive command types.",
@@ -53,7 +53,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0003",
title: "Invalid ReactiveCommand.CanExecute member name",
messageFormat: "The CanExecute name must refer to a valid member, but \"{0}\" has no matches in type {1}",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "The CanExecute name in [ReactiveCommand] must refer to a valid member in its parent type.",
@@ -69,7 +69,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0004",
title: "Multiple ReactiveCommand.CanExecute member name matches",
messageFormat: "The CanExecute name must refer to a single member, but \"{0}\" has multiple matches in type {1}",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Cannot set the CanExecute name in [ReactiveCommand] to one that has multiple matches in its parent type (it must refer to a single compatible member).",
@@ -85,7 +85,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0005",
title: "No valid ReactiveCommand.CanExecute member match",
messageFormat: "The CanExecute name must refer to a compatible member, but no valid members were found for \"{0}\" in type {1}",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "The CanExecute name in [ReactiveCommand] must refer to a compatible member (either a property or a method) to be used in a generated command.",
@@ -101,7 +101,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0006",
title: "Invalid field or property targeted attribute type",
messageFormat: "The method {0} annotated with [ReactiveCommand] is using attribute \"{1}\" which was not recognized as a valid type (are you missing a using directive?)",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "All attributes targeting the generated field or property for a method annotated with [ReactiveCommand] must correctly be resolved to valid types.",
@@ -117,7 +117,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0007",
title: "Invalid field or property targeted attribute expression",
messageFormat: "The method {0} annotated with [ReactiveCommand] is using attribute \"{1}\" with an invalid expression (are you passing any incorrect parameters to the attribute constructor?)",
- category: "ReactiveUI.SourceGenerators.ReactiveCommandGenerator",
+ category: typeof(ReactiveCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "All attributes targeting the generated field or property for a method annotated with [ReactiveCommand] must be using valid expressions.",
@@ -149,7 +149,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0009",
title: "Name collision for generated property",
messageFormat: "The field {0}.{1} cannot be used to generate an reactive property, as its name would collide with the field name (instance fields should use the \"lowerCamel\", \"_lowerCamel\" or \"m_lowerCamel\" pattern)",
- category: "ReactiveUI.SourceGenerators.ReactiveGenerator",
+ category: typeof(ReactiveGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "The name of fields annotated with [Reactive] should use \"lowerCamel\", \"_lowerCamel\" or \"m_lowerCamel\" pattern to avoid collisions with the generated properties.",
@@ -165,7 +165,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0010",
title: "Invalid property targeted attribute type",
messageFormat: "The field {0} annotated with [Reactive] is using attribute \"{1}\" which was not recognized as a valid type (are you missing a using directive?)",
- category: "ReactiveUI.SourceGenerators.ReactiveGenerator",
+ category: typeof(ReactiveGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "All attributes targeting the generated property for a field annotated with [Reactive] must correctly be resolved to valid types.",
@@ -181,7 +181,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0011",
title: "Invalid property targeted attribute expression",
messageFormat: "The field {0} annotated with [Reactive] is using attribute \"{1}\" with an invalid expression (are you passing any incorrect parameters to the attribute constructor?)",
- category: "ReactiveUI.SourceGenerators.ReactiveGenerator",
+ category: typeof(ReactiveGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "All attributes targeting the generated property for a field annotated with [Reactive] must be using valid expressions.",
@@ -245,7 +245,7 @@ internal static class DiagnosticDescriptors
id: "RXUISG0015",
title: "Invalid generated property declaration",
messageFormat: "The field {0}.{1} cannot be used to generate an reactive property, as its name or type would cause conflicts with other generated members",
- category: "ReactiveUI.SourceGenerators.ReactiveGenerator",
+ category: typeof(ReactiveGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "The fields annotated with [Reactive] cannot result in a property name or have a type that would cause conflicts with other generated members.",
diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs
index 57c1307..b4aaa58 100644
--- a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs
+++ b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs
@@ -14,6 +14,7 @@
using ReactiveUI.SourceGenerators.Helpers;
using ReactiveUI.SourceGenerators.Models;
using ReactiveUI.SourceGenerators.Reactive.Models;
+using static ReactiveUI.SourceGenerators.Diagnostics.DiagnosticDescriptors;
namespace ReactiveUI.SourceGenerators;
@@ -23,8 +24,9 @@ namespace ReactiveUI.SourceGenerators;
///
public sealed partial class ObservableAsPropertyGenerator
{
- private static PropertyInfo? GetVariableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
+ private static Result? GetVariableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
{
+ using var builder = ImmutableArrayBuilder.Rent();
var symbol = context.TargetSymbol;
token.ThrowIfCancellationRequested();
@@ -42,7 +44,12 @@ public sealed partial class ObservableAsPropertyGenerator
// Validate the target type
if (!IsTargetTypeValid(fieldSymbol))
{
- return default;
+ builder.Add(
+ InvalidObservableAsPropertyError,
+ fieldSymbol,
+ fieldSymbol.ContainingType,
+ fieldSymbol.Name);
+ return new(default, builder.ToImmutable());
}
// Get the can PropertyName member, if any
@@ -55,15 +62,20 @@ public sealed partial class ObservableAsPropertyGenerator
var fieldName = fieldSymbol.Name;
var propertyName = GetGeneratedPropertyName(fieldSymbol);
- var fieldDeclaration = (FieldDeclarationSyntax)context.TargetNode.Parent!.Parent!;
- var initializer = fieldDeclaration.Declaration.Variables.FirstOrDefault()?.Initializer?.ToFullString();
-
// Check for name collisions
if (fieldName == propertyName)
{
- return default;
+ builder.Add(
+ ReactivePropertyNameCollisionError,
+ fieldSymbol,
+ fieldSymbol.ContainingType,
+ fieldSymbol.Name);
+ return new(default, builder.ToImmutable());
}
+ var fieldDeclaration = (FieldDeclarationSyntax)context.TargetNode.Parent!.Parent!;
+ var initializer = fieldDeclaration.Declaration.Variables.FirstOrDefault()?.Initializer?.ToFullString();
+
token.ThrowIfCancellationRequested();
using var forwardedAttributes = ImmutableArrayBuilder.Rent();
@@ -131,6 +143,11 @@ public sealed partial class ObservableAsPropertyGenerator
// lack of IntelliSense when constructing attributes over the field, but this is the best we can do from this end anyway.
if (!context.SemanticModel.GetSymbolInfo(attribute, token).TryGetAttributeTypeSymbol(out var attributeTypeSymbol))
{
+ builder.Add(
+ InvalidPropertyTargetedAttributeOnObservableAsPropertyField,
+ attribute,
+ fieldSymbol,
+ attribute.Name);
continue;
}
@@ -139,6 +156,11 @@ public sealed partial class ObservableAsPropertyGenerator
// Try to extract the forwarded attribute
if (!AttributeInfo.TryCreate(attributeTypeSymbol, context.SemanticModel, attributeArguments, token, out var attributeInfo))
{
+ builder.Add(
+ InvalidPropertyTargetedAttributeExpressionOnObservableAsPropertyField,
+ attribute,
+ fieldSymbol,
+ attribute.Name);
continue;
}
@@ -162,6 +184,7 @@ public sealed partial class ObservableAsPropertyGenerator
var targetInfo = TargetInfo.From(fieldSymbol.ContainingType);
return new(
+ new(
targetInfo.FileHintName,
targetInfo.TargetName,
targetInfo.TargetNamespace,
@@ -175,7 +198,8 @@ public sealed partial class ObservableAsPropertyGenerator
isReferenceTypeOrUnconstraindTypeParameter,
includeMemberNotNullOnSetAccessor,
forwardedPropertyAttributes,
- isReadonly == false ? string.Empty : "readonly");
+ isReadonly == false ? string.Empty : "readonly"),
+ builder.ToImmutable());
}
private static string GenerateSource(string containingTypeName, string containingNamespace, string containingClassVisibility, string containingType, PropertyInfo[] properties)
diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs
index b740c55..61d6aa4 100644
--- a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs
+++ b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs
@@ -32,7 +32,16 @@ private static void RunObservableAsPropertyFromField(in IncrementalGeneratorInit
// Generate the requested properties and methods
context.RegisterSourceOutput(propertyInfo, static (context, input) =>
{
- var groupedPropertyInfo = input.GroupBy(
+ foreach (var diagnostic in input.SelectMany(static x => x.Errors))
+ {
+ // Output the diagnostics
+ context.ReportDiagnostic(diagnostic.ToDiagnostic());
+ }
+
+ // Gather all the properties that are valid and group them by the target information.
+ var groupedPropertyInfo = input
+ .Where(static x => x.Value != null)
+ .Select(static x => x.Value!).GroupBy(
static info => (info.FileHintName, info.TargetName, info.TargetNamespace, info.TargetVisibility, info.TargetType),
static info => info)
.ToImmutableArray();
diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs
index f25338c..6e0817c 100644
--- a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs
+++ b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs
@@ -12,6 +12,7 @@
using ReactiveUI.SourceGenerators.Helpers;
using ReactiveUI.SourceGenerators.Models;
using ReactiveUI.SourceGenerators.ObservableAsProperty.Models;
+using static ReactiveUI.SourceGenerators.Diagnostics.DiagnosticDescriptors;
namespace ReactiveUI.SourceGenerators;
@@ -21,8 +22,9 @@ namespace ReactiveUI.SourceGenerators;
///
public sealed partial class ObservableAsPropertyGenerator
{
- private static ObservableMethodInfo? GetObservableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
+ private static Result? GetObservableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
{
+ using var diagnostics = ImmutableArrayBuilder.Rent();
var symbol = context.TargetSymbol;
token.ThrowIfCancellationRequested();
@@ -40,7 +42,11 @@ public sealed partial class ObservableAsPropertyGenerator
var methodSymbol = (IMethodSymbol)symbol!;
if (methodSymbol.Parameters.Length != 0)
{
- return default;
+ diagnostics.Add(
+ ObservableAsPropertyMethodHasParametersError,
+ methodSymbol,
+ methodSymbol.Name);
+ return new(default, diagnostics.ToImmutable());
}
var isObservable = methodSymbol.ReturnType.IsObservableReturnType();
@@ -68,6 +74,7 @@ public sealed partial class ObservableAsPropertyGenerator
var targetInfo = TargetInfo.From(methodSymbol.ContainingType);
return new(
+ new(
targetInfo.FileHintName,
targetInfo.TargetName,
targetInfo.TargetNamespace,
@@ -80,7 +87,8 @@ public sealed partial class ObservableAsPropertyGenerator
propertyName ?? (methodSymbol.Name + "Property"),
observableType,
false,
- propertyAttributes);
+ propertyAttributes),
+ diagnostics.ToImmutable());
}
if (context.TargetNode is PropertyDeclarationSyntax propertySyntax)
@@ -111,6 +119,7 @@ public sealed partial class ObservableAsPropertyGenerator
var targetInfo = TargetInfo.From(propertySymbol.ContainingType);
return new(
+ new(
targetInfo.FileHintName,
targetInfo.TargetName,
targetInfo.TargetNamespace,
@@ -123,7 +132,8 @@ public sealed partial class ObservableAsPropertyGenerator
propertyName ?? (propertySymbol.Name + "Property"),
observableType,
true,
- propertyAttributes);
+ propertyAttributes),
+ diagnostics.ToImmutable());
}
return default;
diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs
index ae930b2..72f54a7 100644
--- a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs
+++ b/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs
@@ -35,7 +35,16 @@ private static void RunObservableAsPropertyFromObservable(in IncrementalGenerato
// Generate the requested properties and methods
context.RegisterSourceOutput(propertyInfo, static (context, input) =>
{
- var groupedPropertyInfo = input.GroupBy(
+ foreach (var diagnostic in input.SelectMany(static x => x.Errors))
+ {
+ // Output the diagnostics
+ context.ReportDiagnostic(diagnostic.ToDiagnostic());
+ }
+
+ // Gather all the properties that are valid and group them by the target information.
+ var groupedPropertyInfo = input
+ .Where(static x => x.Value != null)
+ .Select(static x => x.Value!).GroupBy(
static info => (info.FileHintName, info.TargetName, info.TargetNamespace, info.TargetVisibility, info.TargetType),
static info => info)
.ToImmutableArray();
diff --git a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs
index 837af44..cc95503 100644
--- a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs
+++ b/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs
@@ -14,6 +14,7 @@
using ReactiveUI.SourceGenerators.Helpers;
using ReactiveUI.SourceGenerators.Models;
using ReactiveUI.SourceGenerators.Reactive.Models;
+using static ReactiveUI.SourceGenerators.Diagnostics.DiagnosticDescriptors;
namespace ReactiveUI.SourceGenerators;
@@ -31,9 +32,12 @@ public sealed partial class ReactiveGenerator
///
/// The context.
/// The token.
- /// The value.
- private static PropertyInfo? GetVariableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
+ ///
+ /// The value.
+ ///
+ private static Result? GetVariableInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token)
{
+ using var builder = ImmutableArrayBuilder.Rent();
var symbol = context.TargetSymbol;
if (!symbol.TryGetAttributeWithFullyQualifiedMetadataName(AttributeDefinitions.ReactiveAttributeType, out var attributeData))
@@ -48,7 +52,12 @@ public sealed partial class ReactiveGenerator
if (!IsTargetTypeValid(fieldSymbol))
{
- return default;
+ builder.Add(
+ InvalidReactiveError,
+ fieldSymbol,
+ fieldSymbol.ContainingType,
+ fieldSymbol.Name);
+ return new(default, builder.ToImmutable());
}
token.ThrowIfCancellationRequested();
@@ -73,6 +82,16 @@ public sealed partial class ReactiveGenerator
var fieldName = fieldSymbol.Name;
var propertyName = fieldSymbol.GetGeneratedPropertyName();
+ if (fieldName == propertyName)
+ {
+ builder.Add(
+ ReactivePropertyNameCollisionError,
+ fieldSymbol,
+ fieldSymbol.ContainingType,
+ fieldSymbol.Name);
+ return new(default, builder.ToImmutable());
+ }
+
token.ThrowIfCancellationRequested();
// Get the nullability info for the property
@@ -147,6 +166,11 @@ public sealed partial class ReactiveGenerator
// lack of IntelliSense when constructing attributes over the field, but this is the best we can do from this end anyway.
if (!context.SemanticModel.GetSymbolInfo(attribute, token).TryGetAttributeTypeSymbol(out var attributeTypeSymbol))
{
+ builder.Add(
+ InvalidPropertyTargetedAttributeOnReactiveField,
+ attribute,
+ fieldSymbol,
+ attribute.Name);
continue;
}
@@ -155,6 +179,11 @@ public sealed partial class ReactiveGenerator
// Try to extract the forwarded attribute
if (!AttributeInfo.TryCreate(attributeTypeSymbol, context.SemanticModel, attributeArguments, token, out var attributeInfo))
{
+ builder.Add(
+ InvalidPropertyTargetedAttributeExpressionOnReactiveField,
+ attribute,
+ fieldSymbol,
+ attribute.Name);
continue;
}
@@ -171,6 +200,7 @@ public sealed partial class ReactiveGenerator
token.ThrowIfCancellationRequested();
return new(
+ new(
targetInfo.FileHintName,
targetInfo.TargetName,
targetInfo.TargetNamespace,
@@ -184,7 +214,8 @@ public sealed partial class ReactiveGenerator
isReferenceTypeOrUnconstraindTypeParameter,
includeMemberNotNullOnSetAccessor,
forwardedAttributesString,
- accessModifier);
+ accessModifier),
+ builder.ToImmutable());
}
///
diff --git a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs b/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs
index ce39222..835eaac 100644
--- a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs
+++ b/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs
@@ -46,7 +46,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Generate the requested properties
context.RegisterSourceOutput(propertyInfo, static (context, input) =>
{
- var groupedPropertyInfo = input.GroupBy(
+ foreach (var diagnostic in input.SelectMany(static x => x.Errors))
+ {
+ // Output the diagnostics
+ context.ReportDiagnostic(diagnostic.ToDiagnostic());
+ }
+
+ // Gather all the properties that are valid and group them by the target information.
+ var groupedPropertyInfo = input
+ .Where(static x => x.Value != null)
+ .Select(static x => x.Value!).GroupBy(
static info => (info.FileHintName, info.TargetName, info.TargetNamespace, info.TargetVisibility, info.TargetType),
static info => info)
.ToImmutableArray();
diff --git a/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
index 280fafb..258efad 100644
--- a/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
+++ b/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
@@ -9,12 +9,12 @@
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using ReactiveUI.SourceGenerators.Extensions;
using ReactiveUI.SourceGenerators.Helpers;
using ReactiveUI.SourceGenerators.Input.Models;
using ReactiveUI.SourceGenerators.Models;
+using static ReactiveUI.SourceGenerators.Diagnostics.DiagnosticDescriptors;
namespace ReactiveUI.SourceGenerators;
diff --git a/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj b/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj
index bd89f71..6950971 100644
--- a/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj
+++ b/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj
@@ -3,16 +3,17 @@
netstandard2.0
enable
+ latest
true
- Generated
true
- latest
true
true
false
+ true
A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI
+ Generated