Skip to content

Commit 27cd6b4

Browse files
committed
Don't propagate symbols in TransitiveMembersGenerator
1 parent 5f90862 commit 27cd6b4

File tree

4 files changed

+73
-133
lines changed

4 files changed

+73
-133
lines changed

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,18 @@ public INotifyPropertyChangedGenerator()
2828
}
2929

3030
/// <inheritdoc/>
31-
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, INotifyPropertyChangedInfo Info)> GetInfo(
32-
IncrementalGeneratorInitializationContext context,
33-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
34-
{
35-
static INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
36-
{
37-
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument("IncludeAdditionalHelperMethods", true);
38-
39-
return new(includeAdditionalHelperMethods);
40-
}
41-
42-
return source.Select(static (item, _) => (item.Symbol, GetInfo(item.Symbol, item.AttributeData)));
43-
}
44-
45-
/// <inheritdoc/>
46-
protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyPropertyChangedInfo info, out ImmutableArray<Diagnostic> diagnostics)
31+
protected override INotifyPropertyChangedInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray<Diagnostic> diagnostics)
4732
{
4833
ImmutableArray<Diagnostic>.Builder builder = ImmutableArray.CreateBuilder<Diagnostic>();
4934

35+
INotifyPropertyChangedInfo? info = null;
36+
5037
// Check if the type already implements INotifyPropertyChanged
5138
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanged")))
5239
{
5340
builder.Add(DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol);
5441

55-
diagnostics = builder.ToImmutable();
56-
57-
return false;
42+
goto End;
5843
}
5944

6045
// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
@@ -63,14 +48,17 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyP
6348
{
6449
builder.Add(InvalidAttributeCombinationForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol);
6550

66-
diagnostics = builder.ToImmutable();
67-
68-
return false;
51+
goto End;
6952
}
7053

54+
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument("IncludeAdditionalHelperMethods", true);
55+
56+
info = new INotifyPropertyChangedInfo(includeAdditionalHelperMethods);
57+
58+
End:
7159
diagnostics = builder.ToImmutable();
7260

73-
return true;
61+
return info;
7462
}
7563

7664
/// <inheritdoc/>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,7 @@ public ObservableObjectGenerator()
2727
}
2828

2929
/// <inheritdoc/>
30-
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, object? Info)> GetInfo(
31-
IncrementalGeneratorInitializationContext context,
32-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
33-
{
34-
return source.Select(static (item, _) => (item.Symbol, (object?)null));
35-
}
36-
37-
/// <inheritdoc/>
38-
protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object? info, out ImmutableArray<Diagnostic> diagnostics)
30+
protected override object? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray<Diagnostic> diagnostics)
3931
{
4032
ImmutableArray<Diagnostic>.Builder builder = ImmutableArray.CreateBuilder<Diagnostic>();
4133

@@ -44,19 +36,15 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object?
4436
{
4537
builder.Add(DuplicateINotifyPropertyChangedInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol);
4638

47-
diagnostics = builder.ToImmutable();
48-
49-
return false;
39+
goto End;
5040
}
5141

5242
// ...or INotifyPropertyChanging
5343
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanging")))
5444
{
5545
builder.Add(DuplicateINotifyPropertyChangingInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol);
5646

57-
diagnostics = builder.ToImmutable();
58-
59-
return false;
47+
goto End;
6048
}
6149

6250
// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
@@ -65,14 +53,13 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object?
6553
{
6654
builder.Add(InvalidAttributeCombinationForObservableObjectAttributeError, typeSymbol, typeSymbol);
6755

68-
diagnostics = builder.ToImmutable();
69-
70-
return false;
56+
goto End;
7157
}
7258

59+
End:
7360
diagnostics = builder.ToImmutable();
7461

75-
return true;
62+
return null;
7663
}
7764

7865
/// <inheritdoc/>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,63 +30,26 @@ public ObservableRecipientGenerator()
3030
}
3131

3232
/// <inheritdoc/>
33-
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, ObservableRecipientInfo Info)> GetInfo(
34-
IncrementalGeneratorInitializationContext context,
35-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
36-
{
37-
static ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, bool isRequiresUnreferencedCodeAttributeAvailable)
38-
{
39-
string typeName = typeSymbol.Name;
40-
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
41-
bool isAbstract = typeSymbol.IsAbstract;
42-
bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
43-
bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" });
44-
bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" });
45-
46-
return new(
47-
typeName,
48-
hasExplicitConstructors,
49-
isAbstract,
50-
isObservableValidator,
51-
isRequiresUnreferencedCodeAttributeAvailable,
52-
hasOnActivatedMethod,
53-
hasOnDeactivatedMethod);
54-
}
55-
56-
// Check whether [RequiresUnreferencedCode] is available
57-
IncrementalValueProvider<bool> isRequiresUnreferencedCodeAttributeAvailable =
58-
context.CompilationProvider
59-
.Select(static (item, _) => item.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute") is { DeclaredAccessibility: Accessibility.Public });
60-
61-
return
62-
source
63-
.Combine(isRequiresUnreferencedCodeAttributeAvailable)
64-
.Select(static (item, _) => (item.Left.Symbol, GetInfo(item.Left.Symbol, item.Left.AttributeData, item.Right)));
65-
}
66-
67-
/// <inheritdoc/>
68-
protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, ObservableRecipientInfo info, out ImmutableArray<Diagnostic> diagnostics)
33+
protected override ObservableRecipientInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray<Diagnostic> diagnostics)
6934
{
7035
ImmutableArray<Diagnostic>.Builder builder = ImmutableArray.CreateBuilder<Diagnostic>();
7136

37+
ObservableRecipientInfo? info = null;
38+
7239
// Check if the type already inherits from ObservableRecipient
7340
if (typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient"))
7441
{
7542
builder.Add(DuplicateObservableRecipientError, typeSymbol, typeSymbol);
7643

77-
diagnostics = builder.ToImmutable();
78-
79-
return false;
44+
goto End;
8045
}
8146

8247
// Check if the type already inherits [ObservableRecipient]
8348
if (typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
8449
{
8550
builder.Add(InvalidAttributeCombinationForObservableRecipientAttributeError, typeSymbol, typeSymbol);
8651

87-
diagnostics = builder.ToImmutable();
88-
89-
return false;
52+
goto End;
9053
}
9154

9255
// In order to use [ObservableRecipient], the target type needs to inherit from ObservableObject,
@@ -99,14 +62,31 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, Observab
9962
{
10063
builder.Add(MissingBaseObservableObjectFunctionalityError, typeSymbol, typeSymbol);
10164

102-
diagnostics = builder.ToImmutable();
103-
104-
return false;
65+
goto End;
10566
}
10667

68+
// Gather all necessary info to propagate down the pipeline
69+
string typeName = typeSymbol.Name;
70+
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
71+
bool isAbstract = typeSymbol.IsAbstract;
72+
bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
73+
bool isRequiresUnreferencedCodeAttributeAvailable = compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute");
74+
bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" });
75+
bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" });
76+
77+
info = new ObservableRecipientInfo(
78+
typeName,
79+
hasExplicitConstructors,
80+
isAbstract,
81+
isObservableValidator,
82+
isRequiresUnreferencedCodeAttributeAvailable,
83+
hasOnActivatedMethod,
84+
hasOnDeactivatedMethod);
85+
86+
End:
10787
diagnostics = builder.ToImmutable();
10888

109-
return true;
89+
return info;
11090
}
11191

11292
/// <inheritdoc/>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using Microsoft.CodeAnalysis;
1212
using Microsoft.CodeAnalysis.CSharp;
1313
using Microsoft.CodeAnalysis.CSharp.Syntax;
14-
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1514

1615
namespace CommunityToolkit.Mvvm.SourceGenerators;
1716

@@ -70,46 +69,40 @@ private protected TransitiveMembersGenerator(string attributeType, IEqualityComp
7069
/// <inheritdoc/>
7170
public void Initialize(IncrementalGeneratorInitializationContext context)
7271
{
73-
// Get all class declarations
74-
IncrementalValuesProvider<INamedTypeSymbol> typeSymbols =
72+
// Gather all generation info, and any diagnostics
73+
IncrementalValuesProvider<Result<(HierarchyInfo Hierarchy, bool IsSealed, TInfo? Info)>> generationInfoWithErrors =
7574
context.SyntaxProvider
7675
.CreateSyntaxProvider(
7776
static (node, _) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 },
78-
static (context, _) =>
77+
(context, token) =>
7978
{
8079
if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8))
8180
{
8281
return default;
8382
}
8483

85-
return (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!;
86-
})
87-
.Where(static item => item is not null)!;
84+
INamedTypeSymbol typeSymbol = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, token)!;
8885

89-
// Filter the types with the target attribute
90-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> typeSymbolsWithAttributeData =
91-
typeSymbols
92-
.Select((item, _) => (
93-
Symbol: item,
94-
Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName(this.attributeType) == true)))
95-
.Where(static item => item.Attribute is not null)!;
86+
// Filter the types with the target attribute
87+
if (!typeSymbol.TryGetAttributeWithFullyQualifiedName(this.attributeType, out AttributeData? attributeData))
88+
{
89+
return default;
90+
}
9691

97-
// Transform the input data
98-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> typeSymbolsWithInfo = GetInfo(context, typeSymbolsWithAttributeData);
92+
// Gather all generation info, and any diagnostics
93+
TInfo? info = ValidateTargetTypeAndGetInfo(typeSymbol, attributeData, context.SemanticModel.Compilation, out ImmutableArray<Diagnostic> diagnostics);
9994

100-
// Gather all generation info, and any diagnostics
101-
IncrementalValuesProvider<Result<(HierarchyInfo Hierarchy, bool IsSealed, TInfo Info)>> generationInfoWithErrors =
102-
typeSymbolsWithInfo.Select((item, _) =>
103-
{
104-
if (ValidateTargetType(item.Symbol, item.Info, out ImmutableArray<Diagnostic> diagnostics))
105-
{
106-
return new Result<(HierarchyInfo, bool, TInfo)>(
107-
(HierarchyInfo.From(item.Symbol), item.Symbol.IsSealed, item.Info),
108-
ImmutableArray<Diagnostic>.Empty);
109-
}
95+
// If there are any diagnostics, there's no need to compute the hierarchy info at all, just return them
96+
if (diagnostics.Length > 0)
97+
{
98+
return new Result<(HierarchyInfo, bool, TInfo?)>(default, diagnostics);
99+
}
100+
101+
HierarchyInfo hierarchy = HierarchyInfo.From(typeSymbol);
110102

111-
return new Result<(HierarchyInfo, bool, TInfo)>(default, diagnostics);
112-
});
103+
return new Result<(HierarchyInfo, bool, TInfo?)>((hierarchy, typeSymbol.IsSealed, info), diagnostics);
104+
})
105+
.Where(static item => item is not null)!;
113106

114107
// Emit the diagnostic, if needed
115108
context.ReportDiagnostics(generationInfoWithErrors.Select(static (item, _) => item.Errors));
@@ -118,7 +111,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
118111
IncrementalValuesProvider<(HierarchyInfo Hierarchy, bool IsSealed, TInfo Info)> generationInfo =
119112
generationInfoWithErrors
120113
.Where(static item => item.Errors.IsEmpty)
121-
.Select(static (item, _) => item.Value)
114+
.Select(static (item, _) => item.Value)!
122115
.WithComparers(HierarchyInfo.Comparer.Default, EqualityComparer<bool>.Default, this.comparer);
123116

124117
// Generate the required members
@@ -133,23 +126,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
133126
}
134127

135128
/// <summary>
136-
/// Gathers info from a source <see cref="IncrementalValuesProvider{TValues}"/> input.
129+
/// Validates the target type being processes, gets the info if possible and produces all necessary diagnostics.
137130
/// </summary>
138-
/// <param name="context">The <see cref="IncrementalGeneratorInitializationContext"/> instance in use.</param>
139-
/// <param name="source">The source <see cref="IncrementalValuesProvider{TValues}"/> input.</param>
140-
/// <returns>A transformed <see cref="IncrementalValuesProvider{TValues}"/> instance with the gathered data.</returns>
141-
protected abstract IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> GetInfo(
142-
IncrementalGeneratorInitializationContext context,
143-
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source);
144-
145-
/// <summary>
146-
/// Validates a target type being processed.
147-
/// </summary>
148-
/// <param name="typeSymbol">The <see cref="INamedTypeSymbol"/> instance for the target type.</param>
149-
/// <param name="info">The <typeparamref name="TInfo"/> instance with the current processing info.</param>
150-
/// <param name="diagnostics">The resulting diagnostics from the processing operation.</param>
151-
/// <returns>Whether or not the target type is valid and can be processed normally.</returns>
152-
protected abstract bool ValidateTargetType(INamedTypeSymbol typeSymbol, TInfo info, out ImmutableArray<Diagnostic> diagnostics);
131+
/// <param name="typeSymbol">The <see cref="INamedTypeSymbol"/> instance currently being processed.</param>
132+
/// <param name="attributeData">The <see cref="AttributeData"/> instance for the attribute used over <paramref name="typeSymbol"/>.</param>
133+
/// <param name="compilation">The compilation that <paramref name="typeSymbol"/> belongs to.</param>
134+
/// <param name="diagnostics">The resulting diagnostics, if any.</param>
135+
/// <returns>The extracted info for the current type, if possible.</returns>
136+
/// <remarks>If <paramref name="diagnostics"/> is empty, the returned info will always be ignored and no sources will be produced.</remarks>
137+
protected abstract TInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray<Diagnostic> diagnostics);
153138

154139
/// <summary>
155140
/// Filters the <see cref="MemberDeclarationSyntax"/> nodes to generate from the input parsed tree.

0 commit comments

Comments
 (0)