// 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; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions; /// /// Extension methods for . /// internal static class IncrementalGeneratorInitializationContextExtensions { /// /// Implements a gate for a language version over items in an input source. /// /// The type of items in the input source. /// The input value being used. /// The source instance. /// The minimum language version to gate for. /// The to emit if the gate detects invalid usage. /// /// Items in will be filtered out if the gate fails. If it passes, items will remain untouched. /// public static void FilterWithLanguageVersion( this IncrementalGeneratorInitializationContext context, ref IncrementalValuesProvider source, LanguageVersion languageVersion, DiagnosticDescriptor diagnosticDescriptor) { // Check whether the target language version is supported IncrementalValueProvider isGeneratorSupported = context.ParseOptionsProvider .Select((item, _) => item is CSharpParseOptions options && options.LanguageVersion >= languageVersion); // Combine each data item with the supported flag IncrementalValuesProvider<(T Data, bool IsGeneratorSupported)> dataWithSupportedInfo = source .Combine(isGeneratorSupported); // Get a marker node to show whether an invalid attribute is used IncrementalValueProvider isUnsupportedAttributeUsed = dataWithSupportedInfo .Select(static (item, _) => item.IsGeneratorSupported) .Where(static item => !item) .Collect() .Select(static (item, _) => item.Length > 0); // Report them to the output context.RegisterConditionalSourceOutput(isUnsupportedAttributeUsed, context => { context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, null)); }); // Only let data through if the minimum language version is supported source = dataWithSupportedInfo .Where(static item => item.IsGeneratorSupported) .Select(static (item, _) => item.Data); } /// /// Conditionally invokes /// if the value produced by the input is . /// /// The input value being used. /// The source instance. /// The conditional to invoke. public static void RegisterConditionalSourceOutput( this IncrementalGeneratorInitializationContext context, IncrementalValueProvider source, Action action) { context.RegisterSourceOutput(source, (context, condition) => { if (condition) { action(context); } }); } /// /// Conditionally invokes /// if the value produced by the input is , and also supplying a given input state. /// /// The type of state to pass to the source production callback to invoke. /// The input value being used. /// The source instance. /// The conditional to invoke. public static void RegisterConditionalImplementationSourceOutput( this IncrementalGeneratorInitializationContext context, IncrementalValueProvider<(bool Condition, T State)> source, Action action) { context.RegisterImplementationSourceOutput(source, (context, item) => { if (item.Condition) { action(context, item.State); } }); } /// /// Registers an output node into an to output diagnostics. /// /// The input instance. /// The input sequence of diagnostics. public static void ReportDiagnostics(this IncrementalGeneratorInitializationContext context, IncrementalValuesProvider> diagnostics) { context.RegisterSourceOutput(diagnostics, static (context, diagnostics) => { foreach (Diagnostic diagnostic in diagnostics) { context.ReportDiagnostic(diagnostic); } }); } /// /// Registers an output node into an to output diagnostics. /// /// The input instance. /// The input sequence of diagnostics. public static void ReportDiagnostics(this IncrementalGeneratorInitializationContext context, IncrementalValuesProvider diagnostics) { context.RegisterSourceOutput(diagnostics, static (context, diagnostic) => context.ReportDiagnostic(diagnostic)); } }