|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Windows.Forms.Analyzers;
using System.Windows.Forms.CSharp.Analyzers.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace System.Windows.Forms.CSharp.Generators.ApplicationConfiguration;
[Generator(LanguageNames.CSharp)]
internal class ApplicationConfigurationGenerator : IIncrementalGenerator
{
private static void Execute(
SourceProductionContext context,
bool hasSupportedSyntaxNode,
string? projectNamespace,
OutputKind outputKind,
ApplicationConfig? applicationConfig,
Diagnostic? applicationConfigDiagnostics)
{
if (!hasSupportedSyntaxNode)
{
return;
}
if (applicationConfig is null)
{
if (applicationConfigDiagnostics is not null)
{
context.ReportDiagnostic(applicationConfigDiagnostics);
}
return;
}
if (outputKind is not OutputKind.WindowsApplication
// Starting in the 5.0.100 version of the .NET SDK, when OutputType is set to Exe, it is automatically changed to WinExe
// for WPF and Windows Forms apps that target any framework version, including .NET Framework.
// https://docs.microsoft.com/en-us/dotnet/core/compatibility/sdk/5.0/automatically-infer-winexe-output-type
and not OutputKind.ConsoleApplication)
{
context.ReportDiagnostic(Diagnostic.Create(
CSharpDiagnosticDescriptors.s_errorUnsupportedProjectType,
Location.None, nameof(OutputKind.WindowsApplication)));
return;
}
string? code = ApplicationConfigurationInitializeBuilder.GenerateInitialize(projectNamespace, applicationConfig);
if (code is not null)
{
context.AddSource("ApplicationConfiguration.g.cs", code);
}
}
private static string? GetUserProjectNamespace(SyntaxNode node)
{
string? ns = null;
if (node
.Ancestors()
.FirstOrDefault(a => a is NamespaceDeclarationSyntax) is NamespaceDeclarationSyntax namespaceSyntax)
{
ns = namespaceSyntax.Name.ToString();
}
return ns;
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<OutputKind> outputKindProvider = context.CompilationProvider.Select((compilation, _)
=> compilation.Options.OutputKind);
IncrementalValuesProvider<string?> syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (syntaxNode, _) => IsSupportedSyntaxNode(syntaxNode),
transform: static (generatorSyntaxContext, _) => GetUserProjectNamespace(generatorSyntaxContext.Node));
IncrementalValueProvider<(ApplicationConfig? ApplicationConfig, Diagnostic? Diagnostic)> globalConfig
= ProjectFileReader.ReadApplicationConfig(context.AnalyzerConfigOptionsProvider);
IncrementalValueProvider<(OutputKind OutputKind, Collections.Immutable.ImmutableArray<string?> ProjectNamespaces, ApplicationConfig? ApplicationConfig, Diagnostic? ApplicationConfigDiagnostics)> inputs
= outputKindProvider
.Combine(syntaxProvider.Collect())
.Combine(globalConfig)
.Select((data, cancellationToken)
=> (OutputKind: data.Left.Left,
ProjectNamespaces: data.Left.Right,
data.Right.ApplicationConfig,
ApplicationConfigDiagnostics: data.Right.Diagnostic));
context.RegisterSourceOutput(
inputs,
(context, source)
=> Execute(
context: context,
hasSupportedSyntaxNode: source.ProjectNamespaces.Length > 0,
projectNamespace: source.ProjectNamespaces.Length > 0
? source.ProjectNamespaces[0]
: null,
outputKind: source.OutputKind,
applicationConfig: source.ApplicationConfig,
applicationConfigDiagnostics: source.ApplicationConfigDiagnostics));
}
public static bool IsSupportedSyntaxNode(SyntaxNode syntaxNode) =>
syntaxNode is InvocationExpressionSyntax
{
ArgumentList.Arguments.Count: 0,
Expression: MemberAccessExpressionSyntax
{
Name.Identifier.ValueText: "Initialize",
Expression:
MemberAccessExpressionSyntax // For: SourceGenerated.ApplicationConfiguration.Initialize()
{
Name.Identifier.ValueText: "ApplicationConfiguration"
}
or
IdentifierNameSyntax // For: ApplicationConfiguration.Initialize() with a using statement
{
Identifier.ValueText: "ApplicationConfiguration"
}
}
};
}
|