Improve custom filtering support in TransitiveMembersGenerator<TInfo>

This commit is contained in:
Sergio Pedri 2021-12-29 20:52:50 +01:00
Родитель a7e0f2c370
Коммит afc7232dc7
5 изменённых файлов: 55 добавлений и 25 удалений

Просмотреть файл

@ -2,14 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using CommunityToolkit.Mvvm.SourceGenerators.Input.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
@ -30,11 +28,18 @@ public sealed class INotifyPropertyChangedGenerator : TransitiveMembersGenerator
}
/// <inheritdoc/>
protected override INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, INotifyPropertyChangedInfo Info)> GetInfo(
IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
{
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
static INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
{
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
return new(includeAdditionalHelperMethods);
return new(includeAdditionalHelperMethods);
}
return source.Select(static (item, _) => (item.Symbol, GetInfo(item.Symbol, item.AttributeData)));
}
/// <inheritdoc/>

Просмотреть файл

@ -28,9 +28,11 @@ public sealed class ObservableObjectGenerator : TransitiveMembersGenerator<objec
}
/// <inheritdoc/>
protected override object? GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, object? Info)> GetInfo(
IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
{
return null;
return source.Select(static (item, _) => (item.Symbol, (object?)null));
}
/// <inheritdoc/>

Просмотреть файл

@ -30,18 +30,25 @@ public sealed class ObservableRecipientGenerator : TransitiveMembersGenerator<Ob
}
/// <inheritdoc/>
protected override ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, ObservableRecipientInfo Info)> GetInfo(
IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
{
string typeName = typeSymbol.Name;
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
bool isAbstract = typeSymbol.IsAbstract;
bool isObservableValidator = typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
static ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
{
string typeName = typeSymbol.Name;
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
bool isAbstract = typeSymbol.IsAbstract;
bool isObservableValidator = typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
return new(
typeName,
hasExplicitConstructors,
isAbstract,
isObservableValidator);
return new(
typeName,
hasExplicitConstructors,
isAbstract,
isObservableValidator);
}
return source.Select(static (item, _) => (item.Symbol, GetInfo(item.Symbol, item.AttributeData)));
}
/// <inheritdoc/>

Просмотреть файл

@ -80,13 +80,15 @@ public abstract partial class TransitiveMembersGenerator<TInfo> : IIncrementalGe
static (context, _) => (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!);
// Filter the types with the target attribute
IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> typeSymbolsWithInfo =
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> typeSymbolsWithAttributeData =
typeSymbols
.Select((item, _) => (
Symbol: item,
Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName(this.attributeType) == true)))
.Where(static item => item.Attribute is not null)!
.Select((item, _) => (item.Symbol, GetInfo(item.Symbol, item.Attribute!)));
.Where(static item => item.Attribute is not null)!;
// Transform the input data
IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> typeSymbolsWithInfo = GetInfo(context, typeSymbolsWithAttributeData);
// Filter by language version
context.FilterWithLanguageVersion(ref typeSymbolsWithInfo, LanguageVersion.CSharp8, UnsupportedCSharpLanguageVersionError);
@ -129,12 +131,14 @@ public abstract partial class TransitiveMembersGenerator<TInfo> : IIncrementalGe
}
/// <summary>
/// Gets an info model from a retrieved <see cref="AttributeData"/> instance.
/// Gathers info from a source <see cref="IncrementalValuesProvider{TValues}"/> input.
/// </summary>
/// <param name="typeSymbol">The <see cref="INamedTypeSymbol"/> instance for the target type.</param>
/// <param name="attributeData">The input <see cref="AttributeData"/> to get info from.</param>
/// <returns>A <typeparamref name="TInfo"/> instance with data extracted from <paramref name="attributeData"/>.</returns>
protected abstract TInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData);
/// <param name="context">The <see cref="IncrementalGeneratorInitializationContext"/> instance in use.</param>
/// <param name="source">The source <see cref="IncrementalValuesProvider{TValues}"/> input.</param>
/// <returns>A transformed <see cref="IncrementalValuesProvider{TValues}"/> instance with the gathered data.</returns>
protected abstract IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> GetInfo(
IncrementalGeneratorInitializationContext context,
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source);
/// <summary>
/// Validates a target type being processed.

Просмотреть файл

@ -42,6 +42,13 @@ public abstract class ObservableRecipient
public bool IsActive
{
get => this.isActive;
[global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(
"When this property is set to true, the OnActivated() method will be invoked, which will register all necessary message handlers for this recipient. " +
"This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " +
"Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
set
{
if (SetProperty(ref this.isActive, value, true))
@ -69,6 +76,11 @@ public abstract class ObservableRecipient
/// If you need more fine tuned control, want to register messages individually or just prefer
/// the lambda-style syntax for message registration, override this method and register manually.
/// </remarks>
[global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(
"This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " +
"Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
protected virtual void OnActivated()
{
global::CommunityToolkit.Mvvm.Messaging.IMessengerExtensions.RegisterAll(Messenger, this);