diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs index b7c9b25..39a4509 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs @@ -237,6 +237,7 @@ public TestViewModel() /// [Reactive] [field: JsonInclude] + [JsonPropertyName("test")] public partial string? PartialPropertyTest { get; set; } /// diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs index 67b6933..453368d 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs @@ -27,33 +27,40 @@ internal static void GetForwardedAttributes( { using var forwardedAttributeBuilder = ImmutableArrayBuilder.Rent(); - // Gather attributes info - foreach (var attribute in symbol.GetAttributes()) - { - token.ThrowIfCancellationRequested(); + var symbolAttributes = symbol.GetAttributes(); - // Track the current attribute for forwarding if it is a validation attribute - if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute") == true) + // if attributes contains the [Reactive] attribute, we should not forward any attributes without field targets + var isReactiveFromPartialProperty = symbol is IPropertySymbol && symbolAttributes.Any(a => a.AttributeClass?.HasFullyQualifiedMetadataName(AttributeDefinitions.ReactiveAttributeType) == true); + if (!isReactiveFromPartialProperty && symbolAttributes.Length > 1) + { + // Gather attributes info + foreach (var attribute in symbolAttributes) { - forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); - } + token.ThrowIfCancellationRequested(); - // Track the current attribute for forwarding if it is a Json Serialization attribute - if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.Text.Json.Serialization.JsonAttribute") == true) - { - forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); - } + // Track the current attribute for forwarding if it is a validation attribute + if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute") == true) + { + forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); + } - // Also track the current attribute for forwarding if it is of any of the following types: - if (attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.UIHintAttribute") == true || - attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ScaffoldColumnAttribute") == true || - attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.DisplayAttribute") == true || - attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.EditableAttribute") == true || - attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.KeyAttribute") == true || - attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.DataMemberAttribute") == true || - attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute") == true) - { - forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); + // Track the current attribute for forwarding if it is a Json Serialization attribute + if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.Text.Json.Serialization.JsonAttribute") == true) + { + forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); + } + + // Also track the current attribute for forwarding if it is of any of the following types: + if (attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.UIHintAttribute") == true || + attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ScaffoldColumnAttribute") == true || + attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.DisplayAttribute") == true || + attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.EditableAttribute") == true || + attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.KeyAttribute") == true || + attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.DataMemberAttribute") == true || + attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute") == true) + { + forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute)); + } } } diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs index 2c7b853..d68f631 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs @@ -210,7 +210,7 @@ public static bool IsObservableReturnType(this ITypeSymbol? typeSymbol) return false; } - public static bool IsIShedulerType(this ITypeSymbol? typeSymbol) + public static bool IsISchedulerType(this ITypeSymbol? typeSymbol) { var nameFormat = SymbolDisplayFormat.FullyQualifiedFormat; do diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs index e66acf9..2614edd 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs @@ -378,16 +378,30 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) var fieldSyntax = string.Empty; var partialModifier = propertyInfo.IsProperty ? "partial " : string.Empty; + string propertyAttributes; + if (propertyInfo.IsProperty && propertyInfo.FieldName != "field") { - fieldSyntax = $"private {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.FieldName};"; + propertyAttributes = string.Join("\n ", propertyInfo.ForwardedAttributes); + fieldSyntax = +$$""" +{{propertyAttributes}} + private {{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.FieldName}}; +"""; + } + + if (propertyInfo.IsProperty) + { + propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage); + } + else + { + propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes)); } var accessModifier = propertyInfo.PropertyAccessModifier; var setAccessModifier = propertyInfo.SetAccessModifier; - var propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes)); - if (propertyInfo.IncludeMemberNotNullOnSetAccessor || propertyInfo.IsReferenceTypeOrUnconstrainedTypeParameter) { return diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs index b23cd34..f311734 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs @@ -352,7 +352,7 @@ private static bool TryGetOutputSchedulerFromSymbol( if (outputSchedulerSymbol is IFieldSymbol outputSchedulerFieldSymbol) { // The property type must always be a bool - if (!outputSchedulerFieldSymbol.Type.IsIShedulerType()) + if (!outputSchedulerFieldSymbol.Type.IsISchedulerType()) { goto Failure; } @@ -363,7 +363,7 @@ private static bool TryGetOutputSchedulerFromSymbol( else if (outputSchedulerSymbol is IPropertySymbol { GetMethod: not null } outputSchedulerPropertySymbol) { // The property type must always be a bool - if (!outputSchedulerPropertySymbol.Type.IsIShedulerType()) + if (!outputSchedulerPropertySymbol.Type.IsISchedulerType()) { goto Failure; } @@ -374,7 +374,7 @@ private static bool TryGetOutputSchedulerFromSymbol( else if (outputSchedulerSymbol is IMethodSymbol outputSchedulerMethodSymbol) { // The return type must always be a bool - if (!outputSchedulerMethodSymbol.ReturnType.IsIShedulerType()) + if (!outputSchedulerMethodSymbol.ReturnType.IsISchedulerType()) { goto Failure; }