You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

SerializationSourceGenerator.cs 6.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Threading;
  7. using Microsoft.CodeAnalysis;
  8. using Microsoft.CodeAnalysis.CSharp.Syntax;
  9. namespace Discord.Net.SourceGenerators.Serialization
  10. {
  11. [Generator]
  12. public partial class SerializationSourceGenerator : ISourceGenerator
  13. {
  14. public void Execute(GeneratorExecutionContext context)
  15. {
  16. if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(
  17. "build_property.DiscordNet_SerializationGenerator_OptionsTypeNamespace",
  18. out var serializerOptionsNamespace))
  19. throw new InvalidOperationException(
  20. "Missing output namespace. Set DiscordNet_SerializationGenerator_OptionsTypeNamespace in your project file.");
  21. bool searchThroughReferencedAssemblies =
  22. context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(
  23. "build_property.DiscordNet_SerializationGenerator_SearchThroughReferencedAssemblies",
  24. out var _);
  25. var generateSerializerAttribute = context.Compilation
  26. .GetTypeByMetadataName(
  27. "Discord.Net.Serialization.GenerateSerializerAttribute");
  28. var discriminatedUnionSymbol = context.Compilation
  29. .GetTypeByMetadataName(
  30. "Discord.Net.Serialization.DiscriminatedUnionAttribute");
  31. var discriminatedUnionMemberSymbol = context.Compilation
  32. .GetTypeByMetadataName(
  33. "Discord.Net.Serialization.DiscriminatedUnionMemberAttribute");
  34. Debug.Assert(generateSerializerAttribute != null);
  35. Debug.Assert(discriminatedUnionSymbol != null);
  36. Debug.Assert(discriminatedUnionMemberSymbol != null);
  37. Debug.Assert(context.SyntaxContextReceiver != null);
  38. var receiver = (SyntaxReceiver)context.SyntaxContextReceiver!;
  39. var symbolsToBuild = receiver.GetSerializedTypes(
  40. context.Compilation);
  41. if (searchThroughReferencedAssemblies)
  42. {
  43. var visitor = new VisibleTypeVisitor(context.CancellationToken);
  44. foreach (var module in context.Compilation.Assembly.Modules)
  45. foreach (var reference in module.ReferencedAssemblySymbols)
  46. visitor.Visit(reference);
  47. symbolsToBuild = symbolsToBuild
  48. .Concat(visitor.GetVisibleTypes());
  49. }
  50. var types = SerializedTypeUtils.BuildTypeTrees(
  51. generateSerializerAttribute: generateSerializerAttribute!,
  52. discriminatedUnionSymbol: discriminatedUnionSymbol!,
  53. discriminatedUnionMemberSymbol: discriminatedUnionMemberSymbol!,
  54. symbolsToBuild: symbolsToBuild);
  55. foreach (var type in types)
  56. {
  57. context.AddSource($"Converters.{type.ConverterTypeName}",
  58. type.GenerateSourceCode(serializerOptionsNamespace));
  59. if (type is DiscriminatedUnionSerializedType duDeclaration)
  60. foreach (var member in duDeclaration.Members)
  61. context.AddSource(
  62. $"Converters.{type.ConverterTypeName}.{member.ConverterTypeName}",
  63. member.GenerateSourceCode(serializerOptionsNamespace));
  64. }
  65. context.AddSource("SerializerOptions",
  66. GenerateSerializerOptionsSourceCode(
  67. serializerOptionsNamespace, types));
  68. }
  69. public void Initialize(GeneratorInitializationContext context)
  70. => context.RegisterForSyntaxNotifications(
  71. () => new SyntaxReceiver());
  72. private class SyntaxReceiver : ISyntaxContextReceiver
  73. {
  74. private readonly List<SyntaxNode> _classes;
  75. public SyntaxReceiver()
  76. {
  77. _classes = new();
  78. }
  79. public IEnumerable<INamedTypeSymbol> GetSerializedTypes(
  80. Compilation compilation)
  81. {
  82. foreach (var @class in _classes)
  83. {
  84. var semanticModel = compilation.GetSemanticModel(
  85. @class.SyntaxTree);
  86. if (semanticModel.GetDeclaredSymbol(@class) is
  87. INamedTypeSymbol classSymbol)
  88. yield return classSymbol;
  89. }
  90. }
  91. private INamedTypeSymbol? _generateSerializerAttributeSymbol;
  92. public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
  93. {
  94. _generateSerializerAttributeSymbol ??=
  95. context.SemanticModel.Compilation.GetTypeByMetadataName(
  96. "Discord.Net.Serialization.GenerateSerializerAttribute");
  97. Debug.Assert(_generateSerializerAttributeSymbol != null);
  98. if (context.Node is ClassDeclarationSyntax classDeclaration
  99. && classDeclaration.AttributeLists is
  100. SyntaxList<AttributeListSyntax> classAttributeLists
  101. && classAttributeLists.Any(
  102. list => list.Attributes.Any(
  103. n => IsAttribute(n, context.SemanticModel,
  104. _generateSerializerAttributeSymbol!))))
  105. {
  106. _classes.Add(classDeclaration);
  107. }
  108. else if (context.Node is RecordDeclarationSyntax recordDeclaration
  109. && recordDeclaration.AttributeLists is
  110. SyntaxList<AttributeListSyntax> recordAttributeLists
  111. && recordAttributeLists.Any(
  112. list => list.Attributes.Any(
  113. n => IsAttribute(n, context.SemanticModel,
  114. _generateSerializerAttributeSymbol!))))
  115. {
  116. _classes.Add(recordDeclaration);
  117. }
  118. static bool IsAttribute(AttributeSyntax attribute,
  119. SemanticModel model, INamedTypeSymbol expected)
  120. {
  121. var typeInfo = model.GetTypeInfo(attribute.Name);
  122. return SymbolEqualityComparer.Default.Equals(
  123. typeInfo.Type, expected);
  124. }
  125. }
  126. }
  127. }
  128. }

No Description