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

Check for cancellation more often in generators #704

Merged
merged 1 commit into from May 17, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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