Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moar source generator optimizations #489

Merged
merged 6 commits into from
Oct 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ public INotifyPropertyChangedGenerator()
INotifyPropertyChangedInfo? info = null;

// Check if the type already implements INotifyPropertyChanged
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanged")))
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedMetadataName("System.ComponentModel.INotifyPropertyChanged")))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol));

goto End;
}

// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
if (typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
if (typeSymbol.HasOrInheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
typeSymbol.InheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(InvalidAttributeCombinationForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,24 @@ private protected override int ValidateTargetTypeAndGetInfo(INamedTypeSymbol typ
diagnostics = ImmutableArray<DiagnosticInfo>.Empty;

// Check if the type already implements INotifyPropertyChanged...
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanged")))
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedMetadataName("System.ComponentModel.INotifyPropertyChanged")))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(DuplicateINotifyPropertyChangedInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol));

goto End;
}

// ...or INotifyPropertyChanging
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanging")))
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedMetadataName("System.ComponentModel.INotifyPropertyChanging")))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(DuplicateINotifyPropertyChangingInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol));

goto End;
}

// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
if (typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
if (typeSymbol.InheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
typeSymbol.HasOrInheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(InvalidAttributeCombinationForObservableObjectAttributeError, typeSymbol, typeSymbol));

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Filter only the type symbols with [NotifyPropertyChangedRecipients] and create diagnostics for them
IncrementalValuesProvider<Diagnostic> notifyRecipientsErrors =
classSymbols
.Where(static item => item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.NotifyPropertyChangedRecipientsAttribute"))
.Where(static item => item.HasAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyPropertyChangedRecipientsAttribute"))
.Select(static (item, _) => Execute.GetIsNotifyingRecipientsDiagnosticForType(item))
.Where(static item => item is not null)!;

Expand All @@ -134,7 +134,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Filter only the type symbols with [NotifyDataErrorInfo] and create diagnostics for them
IncrementalValuesProvider<Diagnostic> notifyDataErrorInfoErrors =
classSymbols
.Where(static item => item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.NotifyDataErrorInfoAttribute"))
.Where(static item => item.HasAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyDataErrorInfoAttribute"))
.Select(static (item, _) => Execute.GetIsNotifyDataErrorInfoDiagnosticForType(item))
.Where(static item => item is not null)!;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ public ObservableRecipientGenerator()
ObservableRecipientInfo? info = null;

// Check if the type already inherits from ObservableRecipient
if (typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient"))
if (typeSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient"))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(DuplicateObservableRecipientError, typeSymbol, typeSymbol));

goto End;
}

// Check if the type already inherits [ObservableRecipient]
if (typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
if (typeSymbol.InheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(InvalidAttributeCombinationForObservableRecipientAttributeError, typeSymbol, typeSymbol));

Expand All @@ -55,10 +55,10 @@ public ObservableRecipientGenerator()

// In order to use [ObservableRecipient], the target type needs to inherit from ObservableObject,
// or be annotated with [ObservableObject] or [INotifyPropertyChanged] (with additional helpers).
if (!typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObject") &&
!typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") &&
if (!typeSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableObject") &&
!typeSymbol.HasOrInheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") &&
!typeSymbol.HasOrInheritsAttribute(static a =>
a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute") == true &&
a.AttributeClass?.HasFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute") == true &&
!a.HasNamedArgument("IncludeAdditionalHelperMethods", false)))
{
diagnostics = ImmutableArray.Create(DiagnosticInfo.Create(MissingBaseObservableObjectFunctionalityError, typeSymbol, typeSymbol));
Expand All @@ -70,7 +70,7 @@ public ObservableRecipientGenerator()
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.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
bool isRequiresUnreferencedCodeAttributeAvailable = compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute");
bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" });
bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
using CommunityToolkit.Mvvm.SourceGenerators.Messaging.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -31,7 +30,7 @@ private static class Execute
/// <returns>Whether <paramref name="typeSymbol"/> inherits from <c>ObservableValidator</c>.</returns>
public static bool IsObservableValidator(INamedTypeSymbol typeSymbol)
{
return typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
return typeSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
}

/// <summary>
Expand All @@ -56,15 +55,15 @@ public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
// all generators run in an undefined order and looking at the same original compilation, so the
// current one wouldn't be able to see generated properties from other generators directly.
if (memberSymbol is IFieldSymbol &&
!attributes.Any(static a => a.AttributeClass?.HasFullyQualifiedName(
"global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") == true))
!attributes.Any(static a => a.AttributeClass?.HasFullyQualifiedMetadataName(
"CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") == true))
{
continue;
}

// Skip the current member if there are no validation attributes applied to it
if (!attributes.Any(a => a.AttributeClass?.InheritsFromFullyQualifiedName(
"global::System.ComponentModel.DataAnnotations.ValidationAttribute") == true))
if (!attributes.Any(a => a.AttributeClass?.InheritsFromFullyQualifiedMetadataName(
"System.ComponentModel.DataAnnotations.ValidationAttribute") == true))
{
continue;
}
Expand All @@ -81,32 +80,11 @@ public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
}

return new(
typeSymbol.GetFullMetadataNameForFileName(),
typeSymbol.GetFullyQualifiedMetadataName(),
typeSymbol.GetFullyQualifiedName(),
propertyNames.ToImmutable());
}

/// <summary>
/// Gets the <see cref="RecipientInfo"/> instance from the given info.
/// </summary>
/// <param name="typeSymbol">The type symbol for the target type being inspected.</param>
/// <param name="interfaceSymbols">The input array of interface type symbols being handled.</param>
/// <returns>A <see cref="RecipientInfo"/> instance for the current type being inspected.</returns>
public static RecipientInfo GetInfo(INamedTypeSymbol typeSymbol, ImmutableArray<INamedTypeSymbol> interfaceSymbols)
{
using ImmutableArrayBuilder<string> names = ImmutableArrayBuilder<string>.Rent();

foreach (INamedTypeSymbol interfaceSymbol in interfaceSymbols)
{
names.Add(interfaceSymbol.TypeArguments[0].GetFullyQualifiedName());
}

return new(
typeSymbol.GetFullMetadataNameForFileName(),
typeSymbol.GetFullyQualifiedName(),
names.ToImmutable());
}

/// <summary>
/// Gets the head <see cref="CompilationUnitSyntax"/> instance.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
// 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;
using System.Collections.Generic;
using System.Text;
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
using Microsoft.CodeAnalysis;

namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;
Expand All @@ -15,55 +12,6 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;
/// </summary>
internal static class INamedTypeSymbolExtensions
{
/// <summary>
/// Gets a valid filename for a given <see cref="INamedTypeSymbol"/> instance.
/// </summary>
/// <param name="symbol">The input <see cref="INamedTypeSymbol"/> instance.</param>
/// <returns>The full metadata name for <paramref name="symbol"/> that is also a valid filename.</returns>
public static string GetFullMetadataNameForFileName(this INamedTypeSymbol symbol)
{
using ImmutableArrayBuilder<char> builder = ImmutableArrayBuilder<char>.Rent();

static void BuildFrom(ISymbol? symbol, in ImmutableArrayBuilder<char> builder)
{
switch (symbol)
{
// Namespaces that are nested also append a leading '.'
case INamespaceSymbol { ContainingNamespace.IsGlobalNamespace: false }:
BuildFrom(symbol.ContainingNamespace, in builder);
builder.Add('.');
builder.AddRange(symbol.MetadataName.AsSpan());
break;
// Other namespaces (ie. the one right before global) skip the leading '.'
case INamespaceSymbol { IsGlobalNamespace: false }:
builder.AddRange(symbol.MetadataName.AsSpan());
break;
// Types with no namespace just have their metadata name directly written
case ITypeSymbol { ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } }:
builder.AddRange(symbol.MetadataName.AsSpan());
break;
// Types with a containing non-global namespace also append a leading '.'
case ITypeSymbol { ContainingSymbol: INamespaceSymbol namespaceSymbol }:
BuildFrom(namespaceSymbol, in builder);
builder.Add('.');
builder.AddRange(symbol.MetadataName.AsSpan());
break;
// Nested types append a leading '+'
case ITypeSymbol { ContainingSymbol: ITypeSymbol typeSymbol }:
BuildFrom(typeSymbol, in builder);
builder.Add('+');
builder.AddRange(symbol.MetadataName.AsSpan());
break;
default:
break;
}
}

BuildFrom(symbol, in builder);

return builder.ToString();
}

/// <summary>
/// Gets all member symbols from a given <see cref="INamedTypeSymbol"/> instance, including inherited ones.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ public static bool HasFullyQualifiedName(this ISymbol symbol, string name)
}

/// <summary>
/// Checks whether or not a given symbol has an attribute with the specified full name.
/// Checks whether or not a given symbol has an attribute with the specified fully qualified metadata name.
/// </summary>
/// <param name="symbol">The input <see cref="ISymbol"/> instance to check.</param>
/// <param name="name">The attribute name to look for.</param>
/// <returns>Whether or not <paramref name="symbol"/> has an attribute with the specified name.</returns>
public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name)
public static bool HasAttributeWithFullyQualifiedMetadataName(this ISymbol symbol, string name)
{
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();

foreach (AttributeData attribute in attributes)
{
if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true)
if (attribute.AttributeClass?.HasFullyQualifiedMetadataName(name) == true)
{
return true;
}
Expand All @@ -69,19 +69,19 @@ public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, strin

#if !ROSLYN_4_3_1_OR_GREATER
/// <summary>
/// Tries to get an attribute with the specified full name.
/// Tries to get an attribute with the specified fully qualified metadata name.
/// </summary>
/// <param name="symbol">The input <see cref="ISymbol"/> instance to check.</param>
/// <param name="name">The attribute name to look for.</param>
/// <param name="attributeData">The resulting attribute, if it was found.</param>
/// <returns>Whether or not <paramref name="symbol"/> has an attribute with the specified name.</returns>
public static bool TryGetAttributeWithFullyQualifiedName(this ISymbol symbol, string name, [NotNullWhen(true)] out AttributeData? attributeData)
public static bool TryGetAttributeWithFullyQualifiedMetadataName(this ISymbol symbol, string name, [NotNullWhen(true)] out AttributeData? attributeData)
{
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();

foreach (AttributeData attribute in attributes)
{
if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true)
if (attribute.AttributeClass?.HasFullyQualifiedMetadataName(name) == true)
{
attributeData = attribute;

Expand Down
Loading