Skip to content

Commit b3b60f4

Browse files
authored
Merge pull request #435 from CommunityToolkit/dev/no-symbols-ivp
Don't use ISymbol in IncrementalValuesProvider<T> nodes
2 parents 5876242 + b6dd39b commit b3b60f4

10 files changed

+233
-215
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/ObservablePropertyGenerator.cs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,33 @@ public sealed partial class ObservablePropertyGenerator : IIncrementalGenerator
2424
/// <inheritdoc/>
2525
public void Initialize(IncrementalGeneratorInitializationContext context)
2626
{
27-
// Get all field declarations with at least one attribute
28-
IncrementalValuesProvider<IFieldSymbol> fieldSymbols =
27+
// Gather info for all annotated fields
28+
IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result<PropertyInfo?> Info)> propertyInfoWithErrors =
2929
context.SyntaxProvider
3030
.CreateSyntaxProvider(
31-
static (node, _) => node is FieldDeclarationSyntax { Parent: ClassDeclarationSyntax or RecordDeclarationSyntax, AttributeLists.Count: > 0 },
32-
static (context, _) =>
31+
static (node, _) => node is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: FieldDeclarationSyntax { Parent: ClassDeclarationSyntax or RecordDeclarationSyntax, AttributeLists.Count: > 0 } } },
32+
static (context, token) =>
3333
{
3434
if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8))
3535
{
3636
return default;
3737
}
3838

39-
return ((FieldDeclarationSyntax)context.Node).Declaration.Variables.Select(v => (IFieldSymbol)context.SemanticModel.GetDeclaredSymbol(v)!);
40-
})
41-
.Where(static items => items is not null)
42-
.SelectMany(static (item, _) => item!)!;
39+
IFieldSymbol fieldSymbol = (IFieldSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, token)!;
4340

44-
// Filter the fields using [ObservableProperty]
45-
IncrementalValuesProvider<IFieldSymbol> fieldSymbolsWithAttribute =
46-
fieldSymbols
47-
.Where(static item => item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"));
41+
// Filter the fields using [ObservableProperty]
42+
if (!fieldSymbol.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"))
43+
{
44+
return default;
45+
}
4846

49-
// Gather info for all annotated fields
50-
IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result<PropertyInfo?> Info)> propertyInfoWithErrors =
51-
fieldSymbolsWithAttribute
52-
.Select(static (item, _) =>
53-
{
54-
HierarchyInfo hierarchy = HierarchyInfo.From(item.ContainingType);
55-
PropertyInfo? propertyInfo = Execute.TryGetInfo(item, out ImmutableArray<Diagnostic> diagnostics);
47+
// Produce the incremental models
48+
HierarchyInfo hierarchy = HierarchyInfo.From(fieldSymbol.ContainingType);
49+
PropertyInfo? propertyInfo = Execute.TryGetInfo(fieldSymbol, out ImmutableArray<Diagnostic> diagnostics);
5650

57-
return (hierarchy, new Result<PropertyInfo?>(propertyInfo, diagnostics));
58-
});
51+
return (Hierarchy: hierarchy, new Result<PropertyInfo?>(propertyInfo, diagnostics));
52+
})
53+
.Where(static item => item.Hierarchy is not null);
5954

6055
// Output the diagnostics
6156
context.ReportDiagnostics(propertyInfoWithErrors.Select(static (item, _) => item.Info.Errors));

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/ObservableValidatorValidateAllPropertiesGenerator.cs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,43 @@ public sealed partial class ObservableValidatorValidateAllPropertiesGenerator :
2121
/// <inheritdoc/>
2222
public void Initialize(IncrementalGeneratorInitializationContext context)
2323
{
24-
// Get all class declarations. We intentionally skip generating code for abstract types, as that would never be used.
25-
// The methods that are generated by this generator are retrieved through reflection using the type of the invoking
26-
// instance as discriminator, which means a type that is abstract could never be used (since it couldn't be instantiated).
27-
IncrementalValuesProvider<INamedTypeSymbol> typeSymbols =
24+
// Get the types that inherit from ObservableValidator and gather their info
25+
IncrementalValuesProvider<ValidationInfo> validationInfo =
2826
context.SyntaxProvider
2927
.CreateSyntaxProvider(
30-
static (node, _) => node is ClassDeclarationSyntax,
31-
static (context, _) =>
28+
static (node, _) => node is ClassDeclarationSyntax classDeclaration && classDeclaration.HasOrPotentiallyHasBaseTypes(),
29+
static (context, token) =>
3230
{
3331
if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8))
3432
{
3533
return default;
3634
}
3735

38-
return (context.Node, Symbol: (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!);
39-
})
40-
.Where(static item => item.Symbol is { IsAbstract: false, IsGenericType: false } && item.Node.IsFirstSyntaxDeclarationForSymbol(item.Symbol))
41-
.Select(static (item, _) => item.Symbol);
36+
INamedTypeSymbol typeSymbol = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, token)!;
4237

43-
// Get the types that inherit from ObservableValidator and gather their info
44-
IncrementalValuesProvider<ValidationInfo> validationInfo =
45-
typeSymbols
46-
.Where(Execute.IsObservableValidator)
47-
.Select(static (item, _) => Execute.GetInfo(item))
48-
.WithComparer(ValidationInfo.Comparer.Default);
38+
// Skip generating code for abstract types, as that would never be used. The methods that are generated by
39+
// this generator are retrieved through reflection using the type of the invoking instance as discriminator,
40+
// which means a type that is abstract could never be used (since it couldn't be instantiated).
41+
if (typeSymbol is not { IsAbstract: false, IsGenericType: false })
42+
{
43+
return default;
44+
}
45+
46+
// Just like in IMessengerRegisterAllGenerator, only select the first declaration for this type symbol
47+
if (!context.Node.IsFirstSyntaxDeclarationForSymbol(typeSymbol))
48+
{
49+
return default;
50+
}
51+
52+
// Only select types inheriting from ObservableValidator
53+
if (!Execute.IsObservableValidator(typeSymbol))
54+
{
55+
return default;
56+
}
57+
58+
return Execute.GetInfo(typeSymbol);
59+
})
60+
.Where(static item => item is not null)!;
4961

5062
// Check whether the header file is needed
5163
IncrementalValueProvider<bool> isHeaderFileNeeded =

0 commit comments

Comments
 (0)