Skip to content

Commit 7cd82d6

Browse files
authored
Fix Attribute On Partial Reactive Property Is Duplicated (#251)
Fixes #245
1 parent bd7a2a1 commit 7cd82d6

File tree

5 files changed

+52
-30
lines changed

5 files changed

+52
-30
lines changed

src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ public TestViewModel()
238238
/// </value>
239239
[Reactive]
240240
[field: JsonInclude]
241+
[JsonPropertyName("test")]
241242
public partial string? PartialPropertyTest { get; set; }
242243

243244
/// <summary>

src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,40 @@ internal static void GetForwardedAttributes(
2727
{
2828
using var forwardedAttributeBuilder = ImmutableArrayBuilder<AttributeInfo>.Rent();
2929

30-
// Gather attributes info
31-
foreach (var attribute in symbol.GetAttributes())
32-
{
33-
token.ThrowIfCancellationRequested();
30+
var symbolAttributes = symbol.GetAttributes();
3431

35-
// Track the current attribute for forwarding if it is a validation attribute
36-
if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute") == true)
32+
// if attributes contains the [Reactive] attribute, we should not forward any attributes without field targets
33+
var isReactiveFromPartialProperty = symbol is IPropertySymbol && symbolAttributes.Any(a => a.AttributeClass?.HasFullyQualifiedMetadataName(AttributeDefinitions.ReactiveAttributeType) == true);
34+
if (!isReactiveFromPartialProperty && symbolAttributes.Length > 1)
35+
{
36+
// Gather attributes info
37+
foreach (var attribute in symbolAttributes)
3738
{
38-
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
39-
}
39+
token.ThrowIfCancellationRequested();
4040

41-
// Track the current attribute for forwarding if it is a Json Serialization attribute
42-
if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.Text.Json.Serialization.JsonAttribute") == true)
43-
{
44-
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
45-
}
41+
// Track the current attribute for forwarding if it is a validation attribute
42+
if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute") == true)
43+
{
44+
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
45+
}
4646

47-
// Also track the current attribute for forwarding if it is of any of the following types:
48-
if (attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.UIHintAttribute") == true ||
49-
attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ScaffoldColumnAttribute") == true ||
50-
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.DisplayAttribute") == true ||
51-
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.EditableAttribute") == true ||
52-
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.KeyAttribute") == true ||
53-
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.DataMemberAttribute") == true ||
54-
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute") == true)
55-
{
56-
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
47+
// Track the current attribute for forwarding if it is a Json Serialization attribute
48+
if (attribute.AttributeClass?.InheritsFromFullyQualifiedMetadataName("System.Text.Json.Serialization.JsonAttribute") == true)
49+
{
50+
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
51+
}
52+
53+
// Also track the current attribute for forwarding if it is of any of the following types:
54+
if (attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.UIHintAttribute") == true ||
55+
attribute.AttributeClass?.HasOrInheritsFromFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.ScaffoldColumnAttribute") == true ||
56+
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.DisplayAttribute") == true ||
57+
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.EditableAttribute") == true ||
58+
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.ComponentModel.DataAnnotations.KeyAttribute") == true ||
59+
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.DataMemberAttribute") == true ||
60+
attribute.AttributeClass?.HasFullyQualifiedMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute") == true)
61+
{
62+
forwardedAttributeBuilder.Add(AttributeInfo.Create(attribute));
63+
}
5764
}
5865
}
5966

src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public static bool IsObservableReturnType(this ITypeSymbol? typeSymbol)
210210
return false;
211211
}
212212

213-
public static bool IsIShedulerType(this ITypeSymbol? typeSymbol)
213+
public static bool IsISchedulerType(this ITypeSymbol? typeSymbol)
214214
{
215215
var nameFormat = SymbolDisplayFormat.FullyQualifiedFormat;
216216
do

src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,16 +378,30 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo)
378378

379379
var fieldSyntax = string.Empty;
380380
var partialModifier = propertyInfo.IsProperty ? "partial " : string.Empty;
381+
string propertyAttributes;
382+
381383
if (propertyInfo.IsProperty && propertyInfo.FieldName != "field")
382384
{
383-
fieldSyntax = $"private {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.FieldName};";
385+
propertyAttributes = string.Join("\n ", propertyInfo.ForwardedAttributes);
386+
fieldSyntax =
387+
$$"""
388+
{{propertyAttributes}}
389+
private {{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.FieldName}};
390+
""";
391+
}
392+
393+
if (propertyInfo.IsProperty)
394+
{
395+
propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage);
396+
}
397+
else
398+
{
399+
propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes));
384400
}
385401

386402
var accessModifier = propertyInfo.PropertyAccessModifier;
387403
var setAccessModifier = propertyInfo.SetAccessModifier;
388404

389-
var propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes));
390-
391405
if (propertyInfo.IncludeMemberNotNullOnSetAccessor || propertyInfo.IsReferenceTypeOrUnconstrainedTypeParameter)
392406
{
393407
return

src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ private static bool TryGetOutputSchedulerFromSymbol(
352352
if (outputSchedulerSymbol is IFieldSymbol outputSchedulerFieldSymbol)
353353
{
354354
// The property type must always be a bool
355-
if (!outputSchedulerFieldSymbol.Type.IsIShedulerType())
355+
if (!outputSchedulerFieldSymbol.Type.IsISchedulerType())
356356
{
357357
goto Failure;
358358
}
@@ -363,7 +363,7 @@ private static bool TryGetOutputSchedulerFromSymbol(
363363
else if (outputSchedulerSymbol is IPropertySymbol { GetMethod: not null } outputSchedulerPropertySymbol)
364364
{
365365
// The property type must always be a bool
366-
if (!outputSchedulerPropertySymbol.Type.IsIShedulerType())
366+
if (!outputSchedulerPropertySymbol.Type.IsISchedulerType())
367367
{
368368
goto Failure;
369369
}
@@ -374,7 +374,7 @@ private static bool TryGetOutputSchedulerFromSymbol(
374374
else if (outputSchedulerSymbol is IMethodSymbol outputSchedulerMethodSymbol)
375375
{
376376
// The return type must always be a bool
377-
if (!outputSchedulerMethodSymbol.ReturnType.IsIShedulerType())
377+
if (!outputSchedulerMethodSymbol.ReturnType.IsISchedulerType())
378378
{
379379
goto Failure;
380380
}

0 commit comments

Comments
 (0)