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 4.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Reflection;
  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. var receiver = (SyntaxReceiver)context.SyntaxContextReceiver!;
  17. var converters = new List<string>();
  18. foreach (var @class in receiver.Classes)
  19. {
  20. var semanticModel = context.Compilation.GetSemanticModel(
  21. @class.SyntaxTree);
  22. if (semanticModel.GetDeclaredSymbol(@class) is
  23. not INamedTypeSymbol classSymbol)
  24. throw new InvalidOperationException(
  25. "Could not find named type symbol for " +
  26. $"{@class.Identifier}");
  27. context.AddSource(
  28. $"Converters.{classSymbol.Name}",
  29. GenerateConverter(classSymbol));
  30. converters.Add($"{classSymbol.Name}Converter");
  31. }
  32. context.AddSource("SerializerOptions.Complete",
  33. GenerateSerializerOptionsSourceCode(converters));
  34. }
  35. public void Initialize(GeneratorInitializationContext context)
  36. {
  37. context.RegisterForPostInitialization(PostInitialize);
  38. context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
  39. }
  40. public static void PostInitialize(
  41. GeneratorPostInitializationContext context)
  42. => context.AddSource("SerializerOptions.Template",
  43. GenerateSerializerOptionsTemplateSourceCode());
  44. internal class SyntaxReceiver : ISyntaxContextReceiver
  45. {
  46. public List<ClassDeclarationSyntax> Classes { get; } = new();
  47. private readonly Dictionary<string, INamedTypeSymbol> _interestingAttributes
  48. = new();
  49. public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
  50. {
  51. _ = GetOrAddAttribute(_interestingAttributes,
  52. context.SemanticModel,
  53. "Discord.Net.Serialization.DiscriminatedUnionAttribute");
  54. _ = GetOrAddAttribute(_interestingAttributes,
  55. context.SemanticModel,
  56. "Discord.Net.Serialization.DiscriminatedUnionMemberAttribute");
  57. if (context.Node is ClassDeclarationSyntax classDecl
  58. && classDecl.AttributeLists is
  59. SyntaxList<AttributeListSyntax> attrList
  60. && attrList.Any(
  61. list => list.Attributes
  62. .Any(a => IsInterestingAttribute(a,
  63. context.SemanticModel,
  64. _interestingAttributes.Values))))
  65. {
  66. Classes.Add(classDecl);
  67. }
  68. }
  69. private static INamedTypeSymbol GetOrAddAttribute(
  70. Dictionary<string, INamedTypeSymbol> cache,
  71. SemanticModel model, string name)
  72. {
  73. if (!cache.TryGetValue(name, out var type))
  74. {
  75. type = model.Compilation.GetTypeByMetadataName(name);
  76. Debug.Assert(type != null);
  77. cache.Add(name, type!);
  78. }
  79. return type!;
  80. }
  81. private static bool IsInterestingAttribute(
  82. AttributeSyntax attribute, SemanticModel model,
  83. IEnumerable<INamedTypeSymbol> interestingAttributes)
  84. {
  85. var typeInfo = model.GetTypeInfo(attribute.Name);
  86. return interestingAttributes.Any(
  87. x => SymbolEqualityComparer.Default
  88. .Equals(typeInfo.Type, x));
  89. }
  90. }
  91. }
  92. }

No Description