Skip to content

Commit

Permalink
Merge pull request #704 from CommunityToolkit/dev/improve-generator-c…
Browse files Browse the repository at this point in the history
…ancellation

Check for cancellation more often in generators
  • Loading branch information
Sergio0694 committed May 17, 2023
2 parents 327f07b + fa6ef5b commit 4745ec2
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 4 deletions.
Expand Up @@ -64,6 +64,8 @@ internal static class Execute
return false;
}

token.ThrowIfCancellationRequested();

// Get the property type and name
string typeNameWithNullabilityAnnotations = fieldSymbol.Type.GetFullyQualifiedNameWithNullabilityAnnotations();
string fieldName = fieldSymbol.Name;
Expand All @@ -87,6 +89,8 @@ internal static class Execute
return false;
}

token.ThrowIfCancellationRequested();

// Check for special cases that are explicitly not allowed
if (IsGeneratedPropertyInvalid(propertyName, fieldSymbol.Type))
{
Expand All @@ -102,6 +106,8 @@ internal static class Execute
return false;
}

token.ThrowIfCancellationRequested();

using ImmutableArrayBuilder<string> propertyChangedNames = ImmutableArrayBuilder<string>.Rent();
using ImmutableArrayBuilder<string> propertyChangingNames = ImmutableArrayBuilder<string>.Rent();
using ImmutableArrayBuilder<string> notifiedCommandNames = ImmutableArrayBuilder<string>.Rent();
Expand All @@ -114,13 +120,17 @@ internal static class Execute
bool hasAnyValidationAttributes = false;
bool isOldPropertyValueDirectlyReferenced = IsOldPropertyValueDirectlyReferenced(fieldSymbol, propertyName);

token.ThrowIfCancellationRequested();

// Get the nullability info for the property
GetNullabilityInfo(
fieldSymbol,
semanticModel,
out bool isReferenceTypeOrUnconstraindTypeParameter,
out bool includeMemberNotNullOnSetAccessor);

token.ThrowIfCancellationRequested();

// Track the property changing event for the property, if the type supports it
if (shouldInvokeOnPropertyChanging)
{
Expand All @@ -137,16 +147,22 @@ internal static class Execute
hasOrInheritsClassLevelNotifyPropertyChangedRecipients = true;
}

token.ThrowIfCancellationRequested();

// Get the class-level [NotifyDataErrorInfo] setting, if any
if (TryGetNotifyDataErrorInfo(fieldSymbol, out bool isValidationTargetValid))
{
notifyDataErrorInfo = isValidationTargetValid;
hasOrInheritsClassLevelNotifyDataErrorInfo = true;
}

token.ThrowIfCancellationRequested();

// Gather attributes info
foreach (AttributeData attributeData in fieldSymbol.GetAttributes())
{
token.ThrowIfCancellationRequested();

// Gather dependent property and command names
if (TryGatherDependentPropertyChangedNames(fieldSymbol, attributeData, in propertyChangedNames, in builder) ||
TryGatherDependentCommandNames(fieldSymbol, attributeData, in notifiedCommandNames, in builder))
Expand Down Expand Up @@ -194,6 +210,8 @@ internal static class Execute
}
}

token.ThrowIfCancellationRequested();

// Gather explicit forwarded attributes info
foreach (AttributeListSyntax attributeList in fieldSyntax.AttributeLists)
{
Expand All @@ -205,6 +223,8 @@ internal static class Execute
continue;
}

token.ThrowIfCancellationRequested();

foreach (AttributeSyntax attribute in attributeList.Attributes)
{
// Roslyn ignores attributes in an attribute list with an invalid target, so we can't get the AttributeData as usual.
Expand Down Expand Up @@ -250,6 +270,8 @@ internal static class Execute
}
}

token.ThrowIfCancellationRequested();

// Log the diagnostic for missing ObservableValidator, if needed
if (hasAnyValidationAttributes &&
!fieldSymbol.ContainingType.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableValidator"))
Expand All @@ -272,6 +294,8 @@ internal static class Execute
fieldSymbol.Name);
}

token.ThrowIfCancellationRequested();

propertyInfo = new PropertyInfo(
typeNameWithNullabilityAnnotations,
fieldName,
Expand Down
Expand Up @@ -42,8 +42,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Get the hierarchy info for the target symbol, and try to gather the property info
HierarchyInfo hierarchy = HierarchyInfo.From(fieldSymbol.ContainingType);
token.ThrowIfCancellationRequested();
_ = Execute.TryGetInfo(fieldDeclaration, fieldSymbol, context.SemanticModel, token, out PropertyInfo? propertyInfo, out ImmutableArray<DiagnosticInfo> diagnostics);
token.ThrowIfCancellationRequested();
return (Hierarchy: hierarchy, new Result<PropertyInfo?>(propertyInfo, diagnostics));
})
.Where(static item => item.Hierarchy is not null);
Expand Down
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
Expand Down Expand Up @@ -37,8 +38,9 @@ public static bool IsObservableValidator(INamedTypeSymbol typeSymbol)
/// Gets the <see cref="ValidationInfo"/> instance from an input symbol.
/// </summary>
/// <param name="typeSymbol">The input <see cref="INamedTypeSymbol"/> instance to inspect.</param>
/// <param name="token">The cancellation token for the current operation.</param>
/// <returns>The resulting <see cref="ValidationInfo"/> instance for <paramref name="typeSymbol"/>.</returns>
public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol, CancellationToken token)
{
using ImmutableArrayBuilder<string> propertyNames = ImmutableArrayBuilder<string>.Rent();

Expand All @@ -49,6 +51,8 @@ public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
continue;
}

token.ThrowIfCancellationRequested();

ImmutableArray<AttributeData> attributes = memberSymbol.GetAttributes();

// Also include fields that are annotated with [ObservableProperty]. This is necessary because
Expand Down Expand Up @@ -79,6 +83,8 @@ public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
propertyNames.Add(propertyName);
}

token.ThrowIfCancellationRequested();

return new(
typeSymbol.GetFullyQualifiedMetadataName(),
typeSymbol.GetFullyQualifiedName(),
Expand Down
Expand Up @@ -48,13 +48,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return default;
}
token.ThrowIfCancellationRequested();
// Only select types inheriting from ObservableValidator
if (!Execute.IsObservableValidator(typeSymbol))
{
return default;
}
return Execute.GetInfo(typeSymbol);
token.ThrowIfCancellationRequested();
return Execute.GetInfo(typeSymbol, token);
})
.Where(static item => item is not null)!;

Expand Down
Expand Up @@ -77,15 +77,22 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Gather all generation info, and any diagnostics
TInfo? info = ValidateTargetTypeAndGetInfo(typeSymbol, context.Attributes[0], context.SemanticModel.Compilation, out ImmutableArray<DiagnosticInfo> diagnostics);
token.ThrowIfCancellationRequested();
// If there are any diagnostics, there's no need to compute the hierarchy info at all, just return them
if (diagnostics.Length > 0)
{
return new Result<(HierarchyInfo, MetadataInfo?, TInfo?)>(default, diagnostics);
}
HierarchyInfo hierarchy = HierarchyInfo.From(typeSymbol);
token.ThrowIfCancellationRequested();
MetadataInfo metadataInfo = new(typeSymbol.IsSealed, Execute.IsNullabilitySupported(context.SemanticModel.Compilation));
token.ThrowIfCancellationRequested();
return new Result<(HierarchyInfo, MetadataInfo?, TInfo?)>((hierarchy, metadataInfo, info), diagnostics);
})
.Where(static item => item is not null)!;
Expand Down
Expand Up @@ -56,9 +56,13 @@ internal static class Execute
goto Failure;
}

token.ThrowIfCancellationRequested();

// Get the command field and property names
(string fieldName, string propertyName) = GetGeneratedFieldAndPropertyNames(methodSymbol);

token.ThrowIfCancellationRequested();

// Get the command type symbols
if (!TryMapCommandTypesFromMethod(
methodSymbol,
Expand All @@ -74,6 +78,8 @@ internal static class Execute
goto Failure;
}

token.ThrowIfCancellationRequested();

// Check the switch to allow concurrent executions
if (!TryGetAllowConcurrentExecutionsSwitch(
methodSymbol,
Expand All @@ -85,6 +91,8 @@ internal static class Execute
goto Failure;
}

token.ThrowIfCancellationRequested();

// Check the switch to control exception flow
if (!TryGetFlowExceptionsToTaskSchedulerSwitch(
methodSymbol,
Expand All @@ -96,18 +104,23 @@ internal static class Execute
goto Failure;
}

token.ThrowIfCancellationRequested();

// Get the CanExecute expression type, if any
if (!TryGetCanExecuteExpressionType(
methodSymbol,
attributeData,
commandTypeArguments,
token,
in builder,
out string? canExecuteMemberName,
out CanExecuteExpressionType? canExecuteExpressionType))
{
goto Failure;
}

token.ThrowIfCancellationRequested();

// Get the option to include a cancel command, if any
if (!TryGetIncludeCancelCommandSwitch(
methodSymbol,
Expand All @@ -120,6 +133,8 @@ internal static class Execute
goto Failure;
}

token.ThrowIfCancellationRequested();

// Get all forwarded attributes (don't stop in case of errors, just ignore faulting attributes)
GatherForwardedAttributes(
methodSymbol,
Expand All @@ -129,6 +144,8 @@ internal static class Execute
out ImmutableArray<AttributeInfo> fieldAttributes,
out ImmutableArray<AttributeInfo> propertyAttributes);

token.ThrowIfCancellationRequested();

commandInfo = new CommandInfo(
methodSymbol.Name,
fieldName,
Expand Down Expand Up @@ -749,6 +766,7 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
/// <param name="methodSymbol">The input <see cref="IMethodSymbol"/> instance to process.</param>
/// <param name="attributeData">The <see cref="AttributeData"/> instance for <paramref name="methodSymbol"/>.</param>
/// <param name="commandTypeArguments">The command type arguments, if any.</param>
/// <param name="token">The cancellation token for the current operation.</param>
/// <param name="diagnostics">The current collection of gathered diagnostics.</param>
/// <param name="canExecuteMemberName">The resulting can execute member name, if available.</param>
/// <param name="canExecuteExpressionType">The resulting expression type, if available.</param>
Expand All @@ -757,6 +775,7 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
IMethodSymbol methodSymbol,
AttributeData attributeData,
ImmutableArray<string> commandTypeArguments,
CancellationToken token,
in ImmutableArrayBuilder<DiagnosticInfo> diagnostics,
out string? canExecuteMemberName,
out CanExecuteExpressionType? canExecuteExpressionType)
Expand All @@ -782,7 +801,7 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
if (canExecuteSymbols.IsEmpty)
{
// Special case for when the target member is a generated property from [ObservableProperty]
if (TryGetCanExecuteMemberFromGeneratedProperty(memberName, methodSymbol.ContainingType, commandTypeArguments, out canExecuteExpressionType))
if (TryGetCanExecuteMemberFromGeneratedProperty(memberName, methodSymbol.ContainingType, commandTypeArguments, token, out canExecuteExpressionType))
{
canExecuteMemberName = memberName;

Expand Down Expand Up @@ -892,12 +911,14 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
/// <param name="memberName">The member name passed to <c>[RelayCommand(CanExecute = ...)]</c>.</param>
/// <param name="containingType">The containing type for the method annotated with <c>[RelayCommand]</c>.</param>
/// <param name="commandTypeArguments">The type arguments for the command interface, if any.</param>
/// <param name="token">The cancellation token for the current operation.</param>
/// <param name="canExecuteExpressionType">The resulting can execute expression type, if available.</param>
/// <returns>Whether or not <paramref name="canExecuteExpressionType"/> was set and the input symbol was valid.</returns>
private static bool TryGetCanExecuteMemberFromGeneratedProperty(
string memberName,
INamedTypeSymbol containingType,
ImmutableArray<string> commandTypeArguments,
CancellationToken token,
[NotNullWhen(true)] out CanExecuteExpressionType? canExecuteExpressionType)
{
foreach (ISymbol memberSymbol in containingType.GetAllMembers())
Expand All @@ -908,6 +929,8 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
continue;
}

token.ThrowIfCancellationRequested();

ImmutableArray<AttributeData> attributes = memberSymbol.GetAttributes();

// Only filter fields with the [ObservableProperty] attribute
Expand Down Expand Up @@ -991,6 +1014,8 @@ public static (string FieldName, string PropertyName) GetGeneratedFieldAndProper
continue;
}

token.ThrowIfCancellationRequested();

foreach (AttributeSyntax attribute in attributeList.Attributes)
{
// Get the symbol info for the attribute (once again just like in the [ObservableProperty] generator)
Expand Down
Expand Up @@ -40,6 +40,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// Get the hierarchy info for the target symbol, and try to gather the command info
HierarchyInfo? hierarchy = HierarchyInfo.From(methodSymbol.ContainingType);
token.ThrowIfCancellationRequested();
_ = Execute.TryGetInfo(
methodSymbol,
context.Attributes[0],
Expand All @@ -48,6 +50,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
out CommandInfo? commandInfo,
out ImmutableArray<DiagnosticInfo> diagnostics);
token.ThrowIfCancellationRequested();
return (Hierarchy: hierarchy, new Result<CommandInfo?>(commandInfo, diagnostics));
})
.Where(static item => item.Hierarchy is not null)!;
Expand Down
Expand Up @@ -52,13 +52,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
ImmutableArray<INamedTypeSymbol> interfaceSymbols = Execute.GetInterfaces(typeSymbol);
token.ThrowIfCancellationRequested();
// Check that the type implements at least one IRecipient<TMessage> interface
if (interfaceSymbols.IsEmpty)
{
return default;
}
return Execute.GetInfo(typeSymbol, interfaceSymbols);
RecipientInfo info = Execute.GetInfo(typeSymbol, interfaceSymbols);
token.ThrowIfCancellationRequested();
return info;
})
.Where(static item => item is not null)!;

Expand Down

0 comments on commit 4745ec2

Please sign in to comment.