public void Execute(SourceGeneratorContext context) { try { var registrationSymbols = RegistrationSymbols.FromCompilation(context.Compilation); var containerClasses = context.LocateContainerSymbols(registrationSymbols.ContainerSymbol); var generator = new ContainerClassContentGenerator(context, registrationSymbols); foreach (var containerClass in containerClasses) { var hintName = $"Generated.{containerClass.FullyQualifiedName}"; var content = generator.GenerateClassString(containerClass); WriteOutDebugFile(hintName, content, context); context.AddSource(hintName, SourceText.From(content, Encoding.UTF8)); } } catch (DiagnosticException ex) { context.ReportDiagnostic(ex.Diagnostic); } catch (Exception ex) { var descriptor = new DiagnosticDescriptor(DiagnosticConstants.UnknownExceptionId, "Unexpected error", $"Unknown error during generation: {ex.GetType()} {ex.Message}", DiagnosticConstants.Category, DiagnosticSeverity.Error, true); context.ReportDiagnostic(Diagnostic.Create(descriptor, Location.None)); } }
private void Report_MultiplesFormatStringAttributesApplied(INamedTypeSymbol type) { // TODO Reportar mejor la localizacion _context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor( nameof(FormatToStringAttribute), nameof(FormatToStringAttribute), $"Multiples attributes form No properties found in '{type.ContainingNamespace}.{type.Name}' type to fill ToString() method.", $"{nameof(AutoToStringAttribute)}", DiagnosticSeverity.Warning, true), type.Locations.FirstOrDefault() ?? Location.None )); }
public void Execute(SourceGeneratorContext context) { foreach (AdditionalText bfFile in context.AdditionalFiles.Where(x => x.Path.EndsWith(".bf"))) { try { (IEnumerable <BFOp> operations, BFTranspilerOptions options) = BFParser.Parse(bfFile); if (!operations.Any()) { continue; } BFTranspiler btf = new BFTranspiler(options); string csFile = btf.Transpile(operations); context.AddSource(options.ClassName + ".cs", SourceText.From(csFile, Encoding.UTF8)); } catch (Exception e) { context.ReportDiagnostic( Diagnostic.Create( new DiagnosticDescriptor("P0T4T0", "BFError", "Error when transpiling BF: {0}", "BF.Transpile", DiagnosticSeverity.Error, true), Location.Create(Path.GetFileName(bfFile.Path), new TextSpan(), new LinePositionSpan()), e.Message)); } } }
public void Execute(SourceGeneratorContext context) { // retreive the populated receiver if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) { return; } try { // 简单测试aop 生成 Action <StringBuilder, IMethodSymbol> beforeCall = (sb, method) => { }; Action <StringBuilder, IMethodSymbol> afterCall = (sb, method) => { sb.Append("r++;"); }; // 获取生成结果 var code = receiver.SyntaxNodes .Select(i => context.Compilation.GetSemanticModel(i.SyntaxTree).GetDeclaredSymbol(i) as INamedTypeSymbol) .Where(i => i != null && !i.IsStatic) .Select(i => ProxyCodeGenerator.GenerateProxyCode(i, beforeCall, afterCall)) .First(); context.AddSource("code.cs", SourceText.From(code, Encoding.UTF8)); } catch (Exception ex) { // 失败汇报 context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("n001", ex.ToString(), ex.ToString(), "AOP.Generate", DiagnosticSeverity.Warning, true), Location.Create("code.cs", TextSpan.FromBounds(0, 0), new LinePositionSpan()))); } }
public void Execute(SourceGeneratorContext context) { //_ = Debugger.Launch(); // uncomment to debug this source generator // create a folder to serialize the generated source for debugging var generatedPath = default(string?); if (serializeSource) { // place it inside obj so that source is not added to the project generatedPath = Path.Combine("Generated", nameof(OverloadsGenerator)); // delete source generated by previous build if (Directory.Exists(generatedPath)) { Directory.Delete(generatedPath, true); } _ = Directory.CreateDirectory(generatedPath); } try { var collectedExtensionMethods = CollectExtensionMethods(context); GenerateSource(context, collectedExtensionMethods, generatedPath); } catch (Exception ex) { context.ReportDiagnostic(Diagnostic.Create(UnhandledExceptionError, Location.None, ex.Message)); } }
// return true if we should continue private bool ReportDiagnostics(IEnumerable <Diagnostic> diagnostics) { bool hasError = false; foreach (var result in diagnostics) { hasError |= result.Severity == DiagnosticSeverity.Error; _generatorContext.ReportDiagnostic(result); } return(hasError); }
// TODO : Remove when possible to peek into generated code private void WriteOutDebugFile(string hintName, string content, SourceGeneratorContext context) { #if DEBUG try { var tempFile = $"{System.IO.Path.GetTempPath()}{hintName}.cs"; System.IO.File.WriteAllText(tempFile, content); context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("GDBG", "Write out debug file", tempFile, DiagnosticConstants.Category, DiagnosticSeverity.Warning, true), Location.None)); } catch { throw; } #endif }
public void Execute(SourceGeneratorContext context) { // retreive the populated receiver if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) { return; } var compilation = context.Compilation; INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("AutomaticInterfaceAttribute.GenerateAutomaticInterfaceAttribute"); List <INamedTypeSymbol> classSymbols = new List <INamedTypeSymbol>(); foreach (ClassDeclarationSyntax cls in receiver.CandidateClasses) { var model = compilation.GetSemanticModel(cls.SyntaxTree); var classSymbol = model.GetDeclaredSymbol(cls); if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass.Name == attributeSymbol.Name)) // todo, weird that ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default) always returns null - see https://github.com/dotnet/roslyn/issues/30248 maybe? { classSymbols.Add(classSymbol); } } foreach (var classSymbol in classSymbols) { var sourceBuilder = new StringBuilder(); var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); var interfaceName = $"I{classSymbol.Name}"; sourceBuilder.Append($@" using System; namespace {namespaceName} {{ public interface {interfaceName} {{ "); addMembersToInterface(classSymbol, sourceBuilder); sourceBuilder.Append(@" } }"); File.WriteAllText(@"C:\dev\net_automatic_interface\AutomaticInterface\bla.cs", sourceBuilder.ToString()); var descriptor = new DiagnosticDescriptor(nameof(AutomaticInterface), "Result", $"Finished compilation for {interfaceName}", "Compilation", DiagnosticSeverity.Warning, isEnabledByDefault: true); context.ReportDiagnostic(Diagnostic.Create(descriptor, null)); // inject the created source into the users compilation context.AddSource(nameof(AutomaticInterfaceGenerator), SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); } }
private static void ReportDiagnostic(SourceGeneratorContext context, string id, string title, string messageFormat, DiagnosticSeverity defaultSeverity, params object[] messageArgs) { var diagnosticDescriptor = new DiagnosticDescriptor( id: id, title: title, messageFormat: messageFormat, category: "mocks", defaultSeverity: defaultSeverity, isEnabledByDefault: true); var diagnostic = Diagnostic.Create(diagnosticDescriptor, Location.None, messageArgs); context.ReportDiagnostic(diagnostic); }
public void Execute(SourceGeneratorContext context) { try { if (!(context.SyntaxReceiver is CandidateSyntaxReceiver receiver)) { return; } _genFactory.RunGeneration(context.Compilation, receiver.DeclaredTypes, generator => { generator.OnReportedDiagnostic += (_, diagnostic) => context.ReportDiagnostic(diagnostic); generator.OnGeneratedSource += (_, source) => context.AddSourceWithDebug(source); generator.GenerateFiles(); }); } catch (Exception e) { context.WriteException(e); } }
public void Execute(SourceGeneratorContext context) { try { CodeGenerator generator = new CodeGenerator(GltfSchema); generator.ParseSchemas(); generator.ExpandSchemaReferences(); generator.EvaluateInheritance(); generator.PostProcessSchema(); var generatedFiles = generator.CSharpCodeGen(); foreach (var file in generatedFiles) { context.AddSource(file.Key, SourceText.From(file.Value, Encoding.UTF8)); } } catch (Exception e) { context.ReportDiagnostic(Diagnostic.Create("GLTF1", "Compiler", e.Message, DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 1)); } }
public void Execute(SourceGeneratorContext context) { try { ExecuteInternal(context); } catch (Exception e) { //This is temporary till https://github.com/dotnet/roslyn/issues/46084 is fixed context.ReportDiagnostic(Diagnostic.Create( new DiagnosticDescriptor( "SI0000", "An exception was thrown by the SvgGenerator generator", "An exception was thrown by the SvgGenerator generator: '{0}'", "SvgGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true), Location.None, e.ToString())); } }
public void Execute(SourceGeneratorContext context) { if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) { return; } var file = RandomUtils.NewCSFileName(); try { var source = CreateGenerateSymbolSource(receiver.SyntaxNodes, context); var notations = CreateNotationGenerators() .Select(i => i.GenerateNotations(source)) .Combine(); context.AddSource(file, CreateSourceText(notations)); } catch (Exception ex) { context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("n001", ex.ToString(), ex.ToString(), "Norns.AOT.Generate", DiagnosticSeverity.Warning, true), Location.Create(file, TextSpan.FromBounds(0, 0), new LinePositionSpan()))); } }
private void WriteSerializerClasses(SourceGeneratorContext context, List <INamedTypeSymbol> bitStructClasses, Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo> classesSizeInfo, INamedTypeSymbol bitStructAttributeSymbol, INamedTypeSymbol bitArrayAttributeSymbol) { foreach (INamedTypeSymbol classSymbol in bitStructClasses) { ClassSerializeSizeInfo classSizeInfo = classesSizeInfo[classSymbol]; string classFullName = SourceGenUtils.GetTypeFullName(classSymbol); string serializerClassName = CreateSerializerName(classSymbol); BitStructAttribute bitStructAttribute = SourceGenUtils.GetAttribute <BitStructAttribute>(classSymbol, bitStructAttributeSymbol); var sourceBuilder = new StringBuilder(); sourceBuilder.Append($@" namespace {classSymbol.ContainingNamespace} {{ "); ITypeSymbol[] classContainingTypes = SourceGenUtils.GetContainingTypesList(classSymbol); foreach (ITypeSymbol containingType in classContainingTypes) { switch (containingType.TypeKind) { case TypeKind.Class: sourceBuilder.Append($@" partial class {containingType.Name} {{ "); break; case TypeKind.Struct: sourceBuilder.Append($@" partial struct {containingType.Name} {{ "); break; default: context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Only expecting struct or class containing types. Have {containingType.TypeKind}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); return; } } sourceBuilder.Append($@" {SourceGenUtils.GetAccessibilityString(classSymbol.DeclaredAccessibility)} static class {serializerClassName} {{ "); var sizeFuncBuilder = new StringBuilder(); var serializeFuncBuilder = new StringBuilder(); var deserializeFuncBuilder = new StringBuilder(); if (classSizeInfo.Type == ClassSerializeSizeType.Const) { sizeFuncBuilder.Append($@" public const int Size = {classSizeInfo.ConstSize}; "); } sizeFuncBuilder.Append($@" public static int CalculateSize({classFullName} value) {{ int result = {classSizeInfo.ConstSize}; "); serializeFuncBuilder.Append($@" public static global::System.Span<byte> Serialize(global::System.Span<byte> output, {classFullName} value) {{ "); deserializeFuncBuilder.Append($@" public static global::System.ReadOnlySpan<byte> Deserialize(global::System.ReadOnlySpan<byte> input, out {classFullName} value) {{ value = new {classFullName}(); "); foreach (IFieldSymbol classFieldMember in GetClassFieldMembers(classSymbol)) { if (classFieldMember.Type.IsIntegerType() || classFieldMember.Type.TypeKind == TypeKind.Enum) { INamedTypeSymbol fieldType = (INamedTypeSymbol)classFieldMember.Type; IntegerOrEnumTypeInfo fieldTypeInfo = GetIntegerOrEnumTypeInfo(fieldType, bitStructAttribute.Endianess); serializeFuncBuilder.Append($@" if (!{fieldTypeInfo.SerializeFuncName}(output, {fieldTypeInfo.SerializeTypeCast}value.{classFieldMember.Name})) {{ throw new global::System.Exception(string.Format(""Not enough space to serialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} output = output.Slice({fieldTypeInfo.TypeSize}); "); deserializeFuncBuilder.Append($@" {{ if (!{fieldTypeInfo.DeserializeFuncName}(input, out var fieldValue)) {{ throw new global::System.Exception(string.Format(""Not enough data to deserialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} value.{classFieldMember.Name} = {fieldTypeInfo.DeserializeTypeCast}fieldValue; input = input.Slice({fieldTypeInfo.TypeSize}); }} "); } else if (classFieldMember.Type.TypeKind == TypeKind.Class || classFieldMember.Type.TypeKind == TypeKind.Struct) { if (!SourceGenUtils.HasAttribute(classFieldMember.Type, bitStructAttributeSymbol)) { // Type requires BitStruct attribute. return; } INamedTypeSymbol fieldType = (INamedTypeSymbol)classFieldMember.Type; ClassSerializeSizeInfo fieldTypeSizeInfo = classesSizeInfo[fieldType]; string serializerClassFullName = CreateSerializerFullName(fieldType); if (fieldTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic) { sizeFuncBuilder.Append($@" result += {serializerClassFullName}.CalculateSize(value.{classFieldMember.Name}); "); } serializeFuncBuilder.Append($@" output = {serializerClassFullName}.Serialize(output, value.{classFieldMember.Name}); "); deserializeFuncBuilder.Append($@" {{ input = {serializerClassFullName}.Deserialize(input, out var fieldValue); value.{classFieldMember.Name} = fieldValue; }} "); } else if (classFieldMember.Type.TypeKind == TypeKind.Array) { BitArrayAttribute bitArrayAttribute = SourceGenUtils.GetAttribute <BitArrayAttribute>(classFieldMember, bitArrayAttributeSymbol); if (bitArrayAttribute == null) { // Type requires BitArray attribute. return; } IArrayTypeSymbol arrayType = (IArrayTypeSymbol)classFieldMember.Type; string elementTypeFullName = SourceGenUtils.GetTypeFullName(arrayType.ElementType); ClassSerializeSizeInfo elementTypeSizeInfo; string calculateElementSize; string serializeItem; string deserializeItem; if (arrayType.ElementType.IsIntegerType() || arrayType.ElementType.TypeKind == TypeKind.Enum) { IntegerOrEnumTypeInfo elementTypeInfo = GetIntegerOrEnumTypeInfo((INamedTypeSymbol)arrayType.ElementType, bitStructAttribute.Endianess); elementTypeSizeInfo = new ClassSerializeSizeInfo() { Type = ClassSerializeSizeType.Const, ConstSize = elementTypeInfo.TypeSize, }; calculateElementSize = $"result += {elementTypeInfo.TypeSize};"; serializeItem = $@" if (!{elementTypeInfo.SerializeFuncName}(output, {elementTypeInfo.SerializeTypeCast}item)) {{ throw new global::System.Exception(string.Format(""Not enough space to serialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} output = output.Slice({elementTypeInfo.TypeSize}); "; deserializeItem = $@" if (!{elementTypeInfo.DeserializeFuncName}(input, out var tmp)) {{ throw new global::System.Exception(string.Format(""Not enough data to deserialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} var item = {elementTypeInfo.DeserializeTypeCast}tmp; input = input.Slice({elementTypeInfo.TypeSize}); "; } else if (arrayType.ElementType.TypeKind == TypeKind.Class || arrayType.ElementType.TypeKind == TypeKind.Struct) { INamedTypeSymbol elementType = (INamedTypeSymbol)arrayType.ElementType; elementTypeSizeInfo = classesSizeInfo[elementType]; string elementSerializerClassFullName = CreateSerializerFullName(elementType); if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Const) { calculateElementSize = $@"result += {elementSerializerClassFullName}.Size;"; } else { calculateElementSize = $@"result += {elementSerializerClassFullName}.CalculateSize(item);"; } serializeItem = $@"output = {elementSerializerClassFullName}.Serialize(output, item);"; deserializeItem = $@"input = {elementSerializerClassFullName}.Deserialize(input, out var item);"; } else { // Can't serialize type. return; } switch (bitArrayAttribute.SizeType) { case BitArraySizeType.Const: if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic) { sizeFuncBuilder.Append($@" {{ var array = value.{classFieldMember.Name}; int collectionCount = array?.Length ?? 0; if (collectionCount > {bitArrayAttribute.ConstSize}) {{ throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} if (array != null) {{ foreach (var item in array) {{ {calculateElementSize} }} }} int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount; if (backfillCount > 0) {{ {elementTypeFullName} item = default; for (int i = 0; i != backfillCount; ++i) {{ {calculateElementSize} }} }} }} "); } serializeFuncBuilder.Append($@" {{ var array = value.{classFieldMember.Name}; int collectionCount = array?.Length ?? 0; if (collectionCount > {bitArrayAttribute.ConstSize}) {{ throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}"")); }} if (array != null) {{ foreach (var item in array) {{ {serializeItem} }} }} int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount; if (backfillCount > 0) {{ {elementTypeFullName} item = default; for (int i = 0; i != backfillCount; ++i) {{ {serializeItem} }} }} }} "); deserializeFuncBuilder.Append($@" {{ var array = new {elementTypeFullName}[{bitArrayAttribute.ConstSize}]; for (int i = 0; i != {bitArrayAttribute.ConstSize}; ++i) {{ {deserializeItem} array[i] = item; }} value.{classFieldMember.Name} = array; }} "); break; case BitArraySizeType.EndFill: sizeFuncBuilder.Append($@" {{ var array = value.{classFieldMember.Name}; if (array != null) {{ foreach (var item in array) {{ {calculateElementSize} }} }} }} "); serializeFuncBuilder.Append($@" {{ var array = value.{classFieldMember.Name}; if (array != null) {{ foreach (var item in array) {{ {serializeItem} }} }} }} "); deserializeFuncBuilder.Append($@" var list = new global::System.Collections.Generic.List<{elementTypeFullName}>(); while (!input.IsEmpty) {{ {deserializeItem} list.Add(item); }} value.{classFieldMember.Name} = list.ToArray(); "); break; default: // Unknown BitArraySizeType. return; } } else { // Can't serialize type. return; } } sizeFuncBuilder.Append($@" return result; }} "); serializeFuncBuilder.Append($@" return output; }} "); deserializeFuncBuilder.Append($@" return input; }} "); sourceBuilder.Append(sizeFuncBuilder.ToString()); sourceBuilder.Append(serializeFuncBuilder.ToString()); sourceBuilder.Append(deserializeFuncBuilder.ToString()); for (int i = 0; i != classContainingTypes.Length + 1; ++i) { sourceBuilder.Append($@" }} "); } sourceBuilder.Append($@" }} "); string sourceCode = sourceBuilder.ToString(); context.AddSource($"{serializerClassName}.cs", SourceText.From(sourceCode, Encoding.UTF8)); } }
// Figures out which classes have a constant serialization size and which have a serialization size that can change // depending on the object's value. private Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo> GenerateClassSizeInfo(SourceGeneratorContext context, IReadOnlyList <INamedTypeSymbol> bitStructClasses, INamedTypeSymbol bitStructAttributeSymbol, INamedTypeSymbol bitArrayAttributeSymbol) { // Fill in the results object with default values, to avoid needing to dynamically insert values into the dictionary. var classesSizeInfo = new Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo>(); foreach (INamedTypeSymbol classSymbol in bitStructClasses) { classesSizeInfo.Add(classSymbol, new ClassSerializeSizeInfo() { Type = ClassSerializeSizeType.Dynamic, ConstSize = 0, }); } // A class has a constant size iff. all its fields are one of the following types: // a. Integer type. // b. Enum type. // c. Classes that have a constant size. // d. Arrays with a constant length and an element type of a, b or c. // // Because of c and d, this produces a tree(/graph) where constant-size-ness must be propogated from // the leaf nodes up the tree. // // The naive version of this algorithm would be to try to flow the constant-size-ness through the graph // directly. (This would probably be done using a dynamic programming pattern.) However, this wouldn't // handle recursive data structures and could get stuck in an infinite loop. // // So, instead this algorithm borrows techniques from the type inference algorithm. Specifically, it // begins by assuming all types have a dynamic size. Then it iterates through the list of types and // looks for types it can update to have a known constant size. The algorithm keeps looping through // the list of types until there are no more changes. for (bool changed = true; changed; changed = false) { foreach (INamedTypeSymbol classSymbol in bitStructClasses) { // Assume the class's size is constant until a member variable is found with a dynamic size. ClassSerializeSizeInfo classSizeInfo = new ClassSerializeSizeInfo() { Type = ClassSerializeSizeType.Const, ConstSize = 0, }; if (classesSizeInfo[classSymbol].Type == ClassSerializeSizeType.Const) { // Type's serialization size is already known to be constant. So, no point checking again. continue; } // Iterate through the class's member variables. foreach (IFieldSymbol classFieldMember in GetClassFieldMembers(classSymbol)) { // Integers and enums have a constant size. if (classFieldMember.Type.IsIntegerType() || classFieldMember.Type.TypeKind == TypeKind.Enum) { INamedTypeSymbol fieldType = (INamedTypeSymbol)classFieldMember.Type; IntegerOrEnumTypeInfo fieldTypeInfo = GetIntegerOrEnumTypeInfo(fieldType, BitEndianess.LittleEndian); classSizeInfo.ConstSize += fieldTypeInfo.TypeSize; } else if (classFieldMember.Type.TypeKind == TypeKind.Class || classFieldMember.Type.TypeKind == TypeKind.Struct) { INamedTypeSymbol fieldType = classFieldMember.Type as INamedTypeSymbol; if (!classesSizeInfo.ContainsKey(fieldType)) { // Classes/structs must have the BitStruct attribute. // This ensures that the code is specific about which endianess is required for a class because this it not inherited. context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Type {classFieldMember.Type.Name} must have a BitStruct attribute.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); continue; } ClassSerializeSizeInfo fieldTypeSizeInfo = classesSizeInfo[fieldType]; if (fieldTypeSizeInfo.Type == ClassSerializeSizeType.Const) { // Type has a constant size. classSizeInfo.ConstSize += fieldTypeSizeInfo.ConstSize; } else { // Type has a dynamic size (or is not yet known to have a constant size). classSizeInfo.Type = ClassSerializeSizeType.Dynamic; } } else if (classFieldMember.Type.TypeKind == TypeKind.Array) { IArrayTypeSymbol arrayType = (IArrayTypeSymbol)classFieldMember.Type; BitArrayAttribute bitArrayAttribute = SourceGenUtils.GetAttribute <BitArrayAttribute>(classFieldMember, bitArrayAttributeSymbol); if (bitArrayAttribute == null) { // Arrays must be the BitArray attribute, as this specifies how the array's length is handled. context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Field {classFieldMember.Name} of type {classSymbol.Name} must have a BitArray attribute.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); continue; } ClassSerializeSizeInfo elementTypeSizeInfo; if (arrayType.ElementType.IsIntegerType() || arrayType.ElementType.TypeKind == TypeKind.Enum) { IntegerOrEnumTypeInfo elementTypeInfo = GetIntegerOrEnumTypeInfo((INamedTypeSymbol)arrayType.ElementType, BitEndianess.LittleEndian); elementTypeSizeInfo = new ClassSerializeSizeInfo() { Type = ClassSerializeSizeType.Const, ConstSize = elementTypeInfo.TypeSize, }; } else if (arrayType.ElementType.TypeKind == TypeKind.Class || arrayType.ElementType.TypeKind == TypeKind.Struct) { INamedTypeSymbol elementType = (INamedTypeSymbol)arrayType.ElementType; elementTypeSizeInfo = classesSizeInfo[elementType]; } else { // Unsupported type. // Note: Arrays of arrays aren't supported, as the BitArray attribute is required for the inner arrays. // Though this can be handled by wrapping the inner arrays in a struct. context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Can't serialize type of {arrayType.ElementType.Name}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); continue; } // A constant length array of a constant size element type has a constant serialization length. // Otherwise, it has a dynamic serialization length. switch (bitArrayAttribute.SizeType) { case BitArraySizeType.Const: if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic) { classSizeInfo.Type = ClassSerializeSizeType.Dynamic; } else { classSizeInfo.ConstSize += elementTypeSizeInfo.ConstSize * bitArrayAttribute.ConstSize; } break; case BitArraySizeType.EndFill: classSizeInfo.Type = ClassSerializeSizeType.Dynamic; break; default: // Unsupported type. context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Unknown BitArraySizeType value of {bitArrayAttribute.SizeType}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); continue; } } else { // Unsupported type. context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Can't serialize type of {classSymbol.Name}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan()))); continue; } } // Check if the class's size info has changed. if (classSizeInfo != classesSizeInfo[classSymbol]) { classesSizeInfo[classSymbol] = classSizeInfo; changed = true; } } } return(classesSizeInfo); }
public void Execute(SourceGeneratorContext generatorContext) { var ct = generatorContext.CancellationToken; var compilation = generatorContext.Compilation; var jensonSerializeAttribute = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonSerializeAttribute"); var jensonPropertyAttribute = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonPropertyAttribute"); var jensonTypeDiscriminatorAttribute = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonTypeDiscriminatorAttribute"); var jsonPropertyNameAttribute = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonPropertyNameAttribute"); var jsonIgnoreAttribute = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonIgnoreAttribute"); if (jensonSerializeAttribute is null) { throw new Exception("Missing JensonSerializeAttribute in compilation."); } if (jensonPropertyAttribute is null) { throw new Exception("Missing JensonPropertyAttribute in compilation."); } if (jensonTypeDiscriminatorAttribute is null) { throw new Exception("Missing JensonTypeDiscriminatorAttribute in compilation."); } if (jsonPropertyNameAttribute is null) { throw new Exception("Missing JsonPropertyName in compilation."); } if (jsonIgnoreAttribute is null) { throw new Exception("Missing JsonIgnore in compilation."); } var context = new JensonContext( generatorContext, jensonSerializeAttribute, jensonPropertyAttribute, jensonTypeDiscriminatorAttribute, jsonPropertyNameAttribute, jsonIgnoreAttribute); var rootTypes = compilation.GlobalNamespace.GetAllTypes(ct) .Where(t => t.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, jensonSerializeAttribute))); ct.ThrowIfCancellationRequested(); var sb = new StringBuilder(); foreach (var t in rootTypes) { var typeDeclarations = t.DeclaringSyntaxReferences.Select(sr => { return((TypeDeclarationSyntax)sr.GetSyntax()); }); var isPartial = typeDeclarations.Any(td => td.Modifiers.Any(SyntaxKind.PartialKeyword)); if (!isPartial) { foreach (var td in typeDeclarations) { var diagnostic = Diagnostic.Create(MissingPartialModifier, td.GetLocation(), new object[] { t.Name }); generatorContext.ReportDiagnostic(diagnostic); } } var w = new SourceWriter(); GenerateTypeSerializer(context, w, t); generatorContext.AddSource($"{t.Name}.jenson", SourceText.From(w.ToString(), Encoding.UTF8)); // for debugging sb.Append(w.ToString()); sb.AppendLine(); sb.AppendLine(); } // for debugging File.WriteAllText("C:\\Users\\jesse\\Desktop\\converter.cs", sb.ToString()); }