Skip to content

Commit

Permalink
Merge pull request #915 from CommunityToolkit/dev/trim-generated-code
Browse files Browse the repository at this point in the history
Don't generate 'INotifyPropertyChanging' code if not requested
  • Loading branch information
Sergio0694 authored Aug 21, 2024
2 parents 2c2a1c3 + 761a22c commit cc2aa2d
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\CompilationExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\DiagnosticsExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\INamedTypeSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\GeneratorAttributeSyntaxContextWithOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalGeneratorInitializationContextExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalValuesProviderExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MethodDeclarationSyntaxExtensions.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

Expand All @@ -35,6 +36,7 @@ internal static class Execute
/// <param name="fieldSyntax">The <see cref="FieldDeclarationSyntax"/> instance to process.</param>
/// <param name="fieldSymbol">The input <see cref="IFieldSymbol"/> instance to process.</param>
/// <param name="semanticModel">The <see cref="SemanticModel"/> instance for the current run.</param>
/// <param name="options">The options in use for the generator.</param>
/// <param name="token">The cancellation token for the current operation.</param>
/// <param name="propertyInfo">The resulting <see cref="PropertyInfo"/> value, if successfully retrieved.</param>
/// <param name="diagnostics">The resulting diagnostics from the processing operation.</param>
Expand All @@ -43,6 +45,7 @@ public static bool TryGetInfo(
FieldDeclarationSyntax fieldSyntax,
IFieldSymbol fieldSymbol,
SemanticModel semanticModel,
AnalyzerConfigOptions options,
CancellationToken token,
[NotNullWhen(true)] out PropertyInfo? propertyInfo,
out ImmutableArray<DiagnosticInfo> diagnostics)
Expand All @@ -66,6 +69,11 @@ public static bool TryGetInfo(

token.ThrowIfCancellationRequested();

// Override the property changing support if explicitly disabled
shouldInvokeOnPropertyChanging &= GetEnableINotifyPropertyChangingSupport(options);

token.ThrowIfCancellationRequested();

// Get the property type and name
string typeNameWithNullabilityAnnotations = fieldSymbol.Type.GetFullyQualifiedNameWithNullabilityAnnotations();
string fieldName = fieldSymbol.Name;
Expand Down Expand Up @@ -320,6 +328,27 @@ public static bool TryGetInfo(
return true;
}

/// <summary>
/// Gets the value for the "MvvmToolkitEnableINotifyPropertyChangingSupport" property.
/// </summary>
/// <param name="options">The options in use for the generator.</param>
/// <returns>The value for the "MvvmToolkitEnableINotifyPropertyChangingSupport" property.</returns>
public static bool GetEnableINotifyPropertyChangingSupport(AnalyzerConfigOptions options)
{
if (options.TryGetValue("build_property.MvvmToolkitEnableINotifyPropertyChangingSupport", out string? propertyValue))
{
if (bool.TryParse(propertyValue, out bool enableINotifyPropertyChangingSupport))
{
return enableINotifyPropertyChangingSupport;
}
}

// This setting is enabled by default, for backwards compatibility.
// Note that this path should never be reached, as the default
// value is also set in a .targets file bundled in the package.
return true;
}

/// <summary>
/// Validates the containing type for a given field being annotated.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
// Gather info for all annotated fields
IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result<PropertyInfo?> Info)> propertyInfoWithErrors =
context.SyntaxProvider
.ForAttributeWithMetadataName(
context.ForAttributeWithMetadataNameAndOptions(
"CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute",
static (node, _) => node is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: FieldDeclarationSyntax { Parent: ClassDeclarationSyntax or RecordDeclarationSyntax, AttributeLists.Count: > 0 } } },
static (context, token) =>
Expand All @@ -44,7 +43,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

token.ThrowIfCancellationRequested();

_ = Execute.TryGetInfo(fieldDeclaration, fieldSymbol, context.SemanticModel, token, out PropertyInfo? propertyInfo, out ImmutableArray<DiagnosticInfo> diagnostics);
_ = Execute.TryGetInfo(
fieldDeclaration,
fieldSymbol,
context.SemanticModel,
context.GlobalOptions,
token,
out PropertyInfo? propertyInfo,
out ImmutableArray<DiagnosticInfo> diagnostics);

token.ThrowIfCancellationRequested();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;

/// <summary>
/// <inheritdoc cref="GeneratorAttributeSyntaxContext" path="/summary/node()"/>
/// </summary>
/// <param name="syntaxContext">The original <see cref="GeneratorAttributeSyntaxContext"/> value.</param>
/// <param name="globalOptions">The original <see cref="AnalyzerConfigOptions"/> value.</param>
internal readonly struct GeneratorAttributeSyntaxContextWithOptions(
GeneratorAttributeSyntaxContext syntaxContext,
AnalyzerConfigOptions globalOptions)
{
/// <inheritdoc cref="GeneratorAttributeSyntaxContext.TargetNode"/>
public SyntaxNode TargetNode { get; } = syntaxContext.TargetNode;

/// <inheritdoc cref="GeneratorAttributeSyntaxContext.TargetSymbol"/>
public ISymbol TargetSymbol { get; } = syntaxContext.TargetSymbol;

/// <inheritdoc cref="GeneratorAttributeSyntaxContext.SemanticModel"/>
public SemanticModel SemanticModel { get; } = syntaxContext.SemanticModel;

/// <inheritdoc cref="GeneratorAttributeSyntaxContext.Attributes"/>
public ImmutableArray<AttributeData> Attributes { get; } = syntaxContext.Attributes;

/// <inheritdoc cref="AnalyzerConfigOptionsProvider.GlobalOptions"/>
public AnalyzerConfigOptions GlobalOptions { get; } = globalOptions;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

using System;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

#pragma warning disable CS1574

namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;

Expand All @@ -13,6 +17,31 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;
/// </summary>
internal static class IncrementalGeneratorInitializationContextExtensions
{
/// <inheritdoc cref="SyntaxValueProvider.ForAttributeWithMetadataName"/>
public static IncrementalValuesProvider<T> ForAttributeWithMetadataNameAndOptions<T>(
this IncrementalGeneratorInitializationContext context,
string fullyQualifiedMetadataName,
Func<SyntaxNode, CancellationToken, bool> predicate,
Func<GeneratorAttributeSyntaxContextWithOptions, CancellationToken, T> transform)
{
// Invoke 'ForAttributeWithMetadataName' normally, but just return the context directly
IncrementalValuesProvider<GeneratorAttributeSyntaxContext> syntaxContext = context.SyntaxProvider.ForAttributeWithMetadataName(
fullyQualifiedMetadataName,
predicate,
static (context, token) => context);

// Do the same for the analyzer config options
IncrementalValueProvider<AnalyzerConfigOptions> configOptions = context.AnalyzerConfigOptionsProvider.Select(static (provider, token) => provider.GlobalOptions);

// Merge the two and invoke the provided transform on these two values. Neither value
// is equatable, meaning the pipeline will always re-run until this point. This is
// intentional: we don't want any symbols or other expensive objects to be kept alive
// across incremental steps, especially if they could cause entire compilations to be
// rooted, which would significantly increase memory use and introduce more GC pauses.
// In this specific case, flowing non equatable values in a pipeline is therefore fine.
return syntaxContext.Combine(configOptions).Select((input, token) => transform(new GeneratorAttributeSyntaxContextWithOptions(input.Left, input.Right), token));
}

/// <summary>
/// Conditionally invokes <see cref="IncrementalGeneratorInitializationContext.RegisterSourceOutput{TSource}(IncrementalValueProvider{TSource}, Action{SourceProductionContext, TSource})"/>
/// if the value produced by the input <see cref="IncrementalValueProvider{TValue}"/> is <see langword="true"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
<MvvmToolkitEnableINotifyPropertyChangingSupport Condition="'$(MvvmToolkitEnableINotifyPropertyChangingSupport)' == ''">true</MvvmToolkitEnableINotifyPropertyChangingSupport>
</PropertyGroup>

<!--
Make the properties visible to the source generators as well, to allow
them to emit optimized codegen ahead of time depending on their values.
-->
<ItemGroup>
<CompilerVisibleProperty Include="MvvmToolkitEnableINotifyPropertyChangingSupport" />
</ItemGroup>

<!--
Configuration for the feature switches (to support IL trimming).
See the 'ILLink.Substitutions.xml' file for more details on that.
Expand Down

0 comments on commit cc2aa2d

Please sign in to comment.