using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; using System.Linq; namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator; [Generator] public class ImplementAutoGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { //var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl", //static (x, _) => x is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Any(IsCreateImplAttribute), //GetCreateImpl); //context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate); context.RegisterSourceOutput(context.CompilationProvider, Generate); } //public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args) //{ // if (args.ImplementationInfo is null) // return; // var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot(); // var usingDirectives = root.DescendantNodes().OfType().ToArray(); // FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null; // var namespaceResults = root.DescendantNodes().OfType(); // if (namespaceResults != null && namespaceResults.Count() > 0) // fileScopedNamespaceDeclarationSyntax = namespaceResults.First(); // var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace); // context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString()); //} public void Generate(SourceProductionContext context, Compilation compilation) { var syntaxTrees = compilation.SyntaxTrees; foreach (var syntaxTree in syntaxTrees) { var root = syntaxTree.GetRoot(); var classDeclarations = root.DescendantNodes().OfType() .Where(classDeclaration => classDeclaration.AttributeLists.Any(IsCreateImplAttribute)); var usingDirectives = root.DescendantNodes().OfType().ToArray(); FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null; var namespaceResults = root.DescendantNodes().OfType(); if (namespaceResults != null && namespaceResults.Count() > 0) fileScopedNamespaceDeclarationSyntax = namespaceResults.First(); foreach (var classDeclaration in classDeclarations) { foreach (var attribute in GetCreateImplAttributeList(classDeclaration)) { var className = $"{classDeclaration.Identifier.ValueText}Impl"; var modifiers = new string[] { "internal", "partial" }; var nameSpace = string.Empty; if (attribute.ArgumentList is not null && attribute.ArgumentList.Arguments.Count > 0) { foreach (var argument in attribute.ArgumentList.Arguments) { if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax && literalExpressionSyntax.IsKind(SyntaxKind.StringLiteralExpression)) { className = literalExpressionSyntax.Token.ValueText; } else if (argument.NameEquals is NameEqualsSyntax nameEqualsSyntax) { var argumentName = nameEqualsSyntax.Name.Identifier.ValueText; if (argumentName == "ClassName" && argument.Expression is LiteralExpressionSyntax classNameLiteral && classNameLiteral.IsKind(SyntaxKind.StringLiteralExpression)) { className = classNameLiteral.Token.ValueText; } else if (argumentName == "Modifiers") { if (argument.Expression is ArrayCreationExpressionSyntax arrayCreationExpressionSyntax) { if (arrayCreationExpressionSyntax.Initializer is InitializerExpressionSyntax initializerExpressionSyntax) { modifiers = [.. initializerExpressionSyntax.Expressions.OfType() .Where(literal => literal.IsKind(SyntaxKind.StringLiteralExpression)) .Select(literal => literal.Token.ValueText) .Where(s => !string.IsNullOrEmpty(s))]; } } else if (argument.Expression is CollectionExpressionSyntax collectionExpressionSyntax) { modifiers = [.. collectionExpressionSyntax.ChildNodes().OfType() .Select(element => element.Expression.ToString().Replace("\"","")) .Where(s => !string.IsNullOrEmpty(s))]; } } else if (argumentName == "NameSpace" && argument.Expression is LiteralExpressionSyntax nameSpaceLiteral && nameSpaceLiteral.IsKind(SyntaxKind.StringLiteralExpression)) { nameSpace = nameSpaceLiteral.Token.ValueText; } } } } var implementationSyntaxTree = GenerateImplementationSyntaxTree(classDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, className, modifiers, nameSpace); context.AddSource($"{className}.g.cs", implementationSyntaxTree.ToString()); } } } } private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl"); public static List GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)]; //public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token) //{ // if (context is { TargetSymbol: INamedTypeSymbol }) // Debugger.Launch(); // token.ThrowIfCancellationRequested(); // var classDeclaration = (ClassDeclarationSyntax)context.TargetNode; // var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl"); // if (attributeData is null) // return null; // var className = classDeclaration.Identifier.ValueText; // var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl"; // //var targetClassName = $"{className}Impl"; // var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty; // //var nameSpace = string.Empty; // //var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value; // //var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? []; // var modifiers = Array.Empty(); // //foreach (var attributeList in classDeclaration.AttributeLists) // //{ // // foreach (var attribute in attributeList.Attributes) // // { // // if (attribute is null || attribute.Name.ToString() != "CreateImpl") // // continue; // // foreach (var argument in attribute.ArgumentList.Arguments) // // { // // } // // } // //} // if (modifiers.Length == 0) // modifiers = ["internal", "partial"]; // return new ImplementationInfo // { // ClassDeclaration = classDeclaration, // TargetClassName = targetClassName, // Modifiers = modifiers, // NameSpace = nameSpace // }; //} private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace) { var fatherClassName = classDeclaration.Identifier.ValueText; var modifiers = modifierParameters.Select(modifier => SyntaxFactory.ParseToken(modifier)).ToArray(); ClassDeclarationSyntax implementationClass; if (classDeclaration.ParameterList is null) { implementationClass = SyntaxFactory.ClassDeclaration(className) .AddModifiers(modifiers) .AddMembers([.. classDeclaration.Members.OfType().Select(constructor => { return SyntaxFactory.ConstructorDeclaration(className) .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword)) .WithBody(SyntaxFactory.Block()) .WithParameterList(constructor.ParameterList) .WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(constructor.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))); })]) .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// /// 是根据 自动生成的实现类 /// ")) .NormalizeWhitespace(); } else { implementationClass = SyntaxFactory.ClassDeclaration(className) .AddModifiers(modifiers) .AddMembers(SyntaxFactory.ConstructorDeclaration(className) .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword)) .WithBody(SyntaxFactory.Block()) .WithParameterList(classDeclaration.ParameterList) .WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier)))))))) .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// /// 是根据 自动生成的实现类 /// ")) .NormalizeWhitespace(); } if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0) { implementationClass = implementationClass.AddTypeParameterListParameters([.. typeParameterListSyntax.Parameters]) .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{fatherClassName}{typeParameterListSyntax}"))) .AddConstraintClauses([.. classDeclaration.ConstraintClauses]); } else { implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(fatherClassName))); } MemberDeclarationSyntax memberDeclaration; if (nameSpace != string.Empty) { memberDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(nameSpace)) .AddMembers(implementationClass); } else if (fileScopedNamespaceDeclarationSyntax is null) { var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf(); if (namespaceDeclaration is null) memberDeclaration = implementationClass; else memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name) .AddMembers(implementationClass); } else { memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name) .AddMembers(implementationClass); } var implementationCompilationUnit = SyntaxFactory.CompilationUnit() .AddUsings(usingDirectiveSyntaxes) .AddMembers(memberDeclaration) .NormalizeWhitespace(); return SyntaxFactory.SyntaxTree(implementationCompilationUnit); } }