Guest User

SourceGenerationProblemExample

a guest
Aug 16th, 2022
319
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.76 KB | None | 0 0
  1. using Microsoft.CodeAnalysis;
  2. using Microsoft.CodeAnalysis.CSharp;
  3. using Microsoft.CodeAnalysis.CSharp.Syntax;
  4. using Microsoft.CodeAnalysis.Text;
  5. using SourceGeneratorsAttributes.FactoryGenerationAttributes;
  6. using SourceGeneratorsCollection.Extensions;
  7. using SourceGeneratorsLibrary.Extensions;
  8. using System.Collections.Generic;
  9. using System.Collections.Immutable;
  10. using System.ComponentModel;
  11. using System.Linq;
  12. using System.Reflection;
  13. using System.Text;
  14.  
  15.  
  16. namespace System.Runtime.CompilerServices
  17. {
  18. [EditorBrowsable(EditorBrowsableState.Never)]
  19. internal static class IsExternalInit { }
  20. }
  21.  
  22.  
  23. namespace Generators.Factory
  24. {
  25. [Generator]
  26. public class AbstractFactoryGenerator : ISourceGenerator
  27. {
  28. record Combination(ClassDeclarationSyntax Target, ClassDeclarationSyntax Prototype, ICollection<ITypeSymbol> GenericTypes, string FactoryName);
  29.  
  30. static bool GetPartialClasses(ClassDeclarationSyntax x) => x.Modifiers.Any(m => m.ValueText == "partial");
  31. static bool GetStaticClasses(ClassDeclarationSyntax x) => x.Modifiers.Any(m => m.ValueText == "static");
  32.  
  33. static readonly string prototypeAttributeName = typeof(FactoryPrototypeMarkerAttribute<,,>).GetTruncatedTypeName().Replace("Attribute", string.Empty);
  34. static readonly string realizationAttributeName = typeof(FactoryRealizationAttribute).GetTruncatedTypeName().Replace("Attribute", string.Empty);
  35. static readonly string handleAttributeName = typeof(FactoryHandlerAttribute<>).GetTruncatedTypeName().Replace("Attribute", string.Empty);
  36.  
  37. INamedTypeSymbol GetSymbolTypeFromAttribute(AttributeSyntax attribute, SemanticModel semanticModel)
  38. {
  39. var attrTypeInfo = semanticModel.GetTypeInfo(attribute);
  40. return ((INamedTypeSymbol)attrTypeInfo.Type!);
  41. }
  42.  
  43. bool isGeneric;
  44. ICollection<string>? classTypes;
  45.  
  46. public void Execute(GeneratorExecutionContext context)
  47. {
  48. var compilation = context.Compilation;
  49.  
  50. foreach (var syntaxTree in compilation.SyntaxTrees)
  51. {
  52. var semanticModel = compilation.GetSemanticModel(syntaxTree);
  53.  
  54. var isGenericClass = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
  55. .FirstOrDefault()?.TypeParameterList?.Parameters.Count > 0;
  56.  
  57. if (isGenericClass)
  58. {
  59. classTypes = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
  60. .FirstOrDefault()
  61. .TypeParameterList?.Parameters.Select(e => e.Identifier.Text)
  62. .ToList();
  63. }
  64.  
  65. var targetTypes = syntaxTree.GetRoot().DescendantNodes()
  66. .OfType<ClassDeclarationSyntax>()
  67. .Where(x => x.ContainsClassAttribute(realizationAttributeName))
  68. .Select(e => new
  69. {
  70. Target = e,
  71. FactoryName = semanticModel.GetConstantValue(e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(realizationAttributeName)))
  72. .Select(f => f?.ArgumentList.Arguments.FirstOrDefault().Expression)
  73. .FirstOrDefault()).Value.ToString()
  74. })
  75. //.Select(x => semanticModel.GetDeclaredSymbol(x))
  76. //.OfType<ITypeSymbol>()
  77. .ToImmutableList();
  78.  
  79. var factoryPrototypes = syntaxTree.GetRoot().DescendantNodes()
  80. .Where(x => x is ClassDeclarationSyntax)
  81. .Cast<ClassDeclarationSyntax>()
  82. .Where(GetStaticClasses)
  83. .Where(e => e.ContainsClassAttribute(prototypeAttributeName))
  84. .Select(e => new
  85. {
  86. Prototype = e,
  87. FactoryName = semanticModel.GetConstantValue(e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(prototypeAttributeName)))
  88. .Select(f => f?.ArgumentList.Arguments.FirstOrDefault().Expression)
  89. .FirstOrDefault()).Value.ToString(),
  90.  
  91. GenericTypes = e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(prototypeAttributeName)))
  92. .Select(f => GetSymbolTypeFromAttribute(f, semanticModel))
  93. .FirstOrDefault().TypeArguments
  94. //.Select(e => e.OriginalDefinition.Name.ToString())
  95. .ToList(),
  96. })
  97. .ToImmutableList();
  98.  
  99. var pairs = targetTypes.Join(factoryPrototypes,
  100. e => e.FactoryName,
  101. f => f.FactoryName,
  102. (target, prototype) => new Combination(target.Target, prototype.Prototype, prototype.GenericTypes, target.FactoryName))
  103. .ToList();
  104.  
  105.  
  106. foreach (var pair in pairs)
  107. {
  108. var genericAddon = isGeneric ? $@"<{string.Join(",", classTypes)}>" : "";
  109. var targetSymbol = semanticModel.GetDeclaredSymbol(pair.Target);
  110.  
  111. string source = GenerateFactory(pair, semanticModel, genericAddon);
  112. context.AddSource($"{targetSymbol.Name}.Factory.{pair.FactoryName}.cs", SourceText.From(source, Encoding.UTF8));
  113. }
  114.  
  115. }
  116. }
  117.  
  118. string GenerateFactory(Combination combination, SemanticModel semanticModel, string genericAddon)
  119. {
  120. var targetSymbol = semanticModel.GetDeclaredSymbol(combination.Target);
  121. var prototypeSymbol = semanticModel.GetDeclaredSymbol(combination.Prototype);
  122.  
  123. var generatedResult = $@"
  124. using System;
  125. using System.Collections.Generic;
  126. using System.Linq;
  127. using System.Reflection;
  128. using System.Runtime.CompilerServices;
  129. using System.Text;
  130. using System.Threading.Tasks;
  131.  
  132. using {prototypeSymbol.ContainingNamespace};
  133.  
  134. namespace {targetSymbol.ContainingNamespace}
  135. {{
  136. {PublishFactoryPrototype(combination, semanticModel)}
  137.  
  138. public partial class {targetSymbol.Name}{genericAddon}
  139. {{
  140. public {combination.FactoryName} {combination.FactoryName} {{get; protected set;}} = {combination.FactoryName}.Instance;
  141. }}
  142. }}";
  143.  
  144. return generatedResult;
  145. }
  146.  
  147. string PublishFactoryPrototype(Combination combination, SemanticModel semanticModel)
  148. {
  149. var prototypeMethods = combination.Prototype.Members.Select(e => e as MethodDeclarationSyntax)
  150. .Where(e => e != null)
  151. .Where(e => e.AttributeLists.SelectMany(f => f.Attributes).Where(s => s.Name.ToString().Contains(handleAttributeName)).Any())
  152. .Select(e => new
  153. {
  154. Method = e,
  155. ParamValue = e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(handleAttributeName)))
  156. .Select(f => f?.ArgumentList.Arguments.FirstOrDefault()
  157. .Expression
  158. //.NormalizeWhitespace()
  159. .ToString()).FirstOrDefault(),
  160. })
  161. .ToList();
  162.  
  163. var TKey = combination.GenericTypes.ElementAt(0).OriginalDefinition.Name.ToString();
  164. var TRequest = combination.GenericTypes.ElementAt(1).OriginalDefinition.Name.ToString();
  165. var TResponse = combination.GenericTypes.ElementAt(2).OriginalDefinition.Name.ToString();
  166.  
  167. var dictionaryInitializer = new StringBuilder();
  168.  
  169. foreach(var prototypeMethod in prototypeMethods)
  170. {
  171. dictionaryInitializer.AppendLine($@"methods.Add({prototypeMethod.ParamValue}, {combination.Prototype.Identifier}.{prototypeMethod.Method.Identifier});");
  172. }
  173.  
  174. var initializer = $@"private void Initialize()
  175. {{
  176. Console.WriteLine(""xyu"");
  177. {dictionaryInitializer}
  178. }}";
  179.  
  180. var handler = $@"public async Task<{TResponse}> Handle({TKey} key, {TRequest} request)
  181. {{
  182. if (methods.TryGetValue(key, out var method))
  183. {{
  184. return await method(request);
  185. }}
  186.  
  187. throw new NotImplementedException();
  188. }}";
  189.  
  190. var classBuilder = $@"public sealed class {combination.FactoryName}
  191. {{
  192. Dictionary<{TKey}, Func<{TRequest},Task<{TResponse}>>> methods = new();
  193.  
  194. static readonly Lazy<{combination.FactoryName}> lazy = new(() => new());
  195.  
  196. public static {combination.FactoryName} Instance {{ get => lazy.Value; }}
  197.  
  198. private {combination.FactoryName}()
  199. {{
  200. Initialize();
  201. }}
  202.  
  203. {initializer}
  204.  
  205. {handler}
  206. }}";
  207.  
  208. return classBuilder.ToString();
  209. }
  210. public void Initialize(GeneratorInitializationContext context)
  211. {
  212. }
  213. }
  214. }
Advertisement
Add Comment
Please, Sign In to add comment