Skip to content

Commit

Permalink
Fixed nullability, added a nuspec, allow for custom context class nam…
Browse files Browse the repository at this point in the history
…espace/name
  • Loading branch information
EdwardCooke committed Jan 28, 2023
1 parent 5edec9d commit 8ffa46e
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 52 deletions.
7 changes: 7 additions & 0 deletions YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class ClassSyntaxReceiver : ISyntaxContextReceiver
{
public List<string> Log { get; } = new();
public Dictionary<string, ClassObject> Classes { get; } = new Dictionary<string, ClassObject>();
public INamedTypeSymbol? YamlStaticContextType { get; set; }

public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
Expand All @@ -39,6 +40,12 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax)!;
if (classSymbol.GetAttributes().Any())
{
var attributes = classSymbol.GetAttributes();
if (attributes.Any(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlStaticContextAttribute"))
{
YamlStaticContextType = classSymbol;
}

if (classSymbol.GetAttributes().Any(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlSerializableAttribute"))
{
ClassObject classObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"public class {classObject.SanitizedClassName}_{classObject.GuidSuffix} : YamlDotNet.Serialization.IObjectAccessor");
Write($"class {classObject.SanitizedClassName}_{classObject.GuidSuffix} : YamlDotNet.Serialization.IObjectAccessor");
Write("{"); Indent();

Write("public void Set(string propertyName, object target, object value)");
Write("{"); Indent();
if (classObject.FieldSymbols.Count > 0 || classObject.PropertySymbols.Count > 0)
{
Write($"var v = ({classObject.FullName})target;");
Write($"var v = ({classObject.FullName.Replace("?", string.Empty)})target;");
Write("switch (propertyName)");
Write("{"); Indent();
foreach (var field in classObject.FieldSymbols)
Expand All @@ -68,7 +68,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)

Write("public object Read(string propertyName, object target)");
Write("{"); Indent();
Write($"var v = ({classObject.FullName})target;");
Write($"var v = ({classObject.FullName.Replace("?", string.Empty)})target;");
if (classObject.FieldSymbols.Count > 0 || classObject.PropertySymbols.Count > 0)
{
Write("switch (propertyName)");
Expand All @@ -91,7 +91,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)

private string GetSetter(string name, ITypeSymbol type)
{
return $"case \"{name}\": v.{name} = ({type.GetFullName()})value; return;";
return $"case \"{name}\": v.{name} = ({type.GetFullName().Replace("?", string.Empty)})value; return;";
}

private string GetReader(string name)
Expand Down
2 changes: 1 addition & 1 deletion YamlDotNet.Analyzers.StaticGenerator/StaticContextFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public StaticContextFile(Action<string, bool> write, Action indent, Action unind

public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
{
Write("public partial class StaticContext : YamlDotNet.Serialization.StaticContext");
Write($"public partial class {classSyntaxReceiver.YamlStaticContextType?.Name ?? "StaticContext"} : YamlDotNet.Serialization.StaticContext");
Write("{"); Indent();
Write("public YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory ObjectFactory { get; } = new StaticObjectFactory();");
Write("public StaticTypeInspector TypeInspector { get; } = new StaticTypeInspector();");
Expand Down
24 changes: 12 additions & 12 deletions YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ public StaticObjectFactoryFile(Action<string, bool> write, Action indent, Action

public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
{
Write("public class StaticObjectFactory : YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory");
Write($"class StaticObjectFactory : YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory");
Write("{"); Indent();

Write("public override object Create(Type type)");
Write("{"); Indent();
foreach (var o in classSyntaxReceiver.Classes.Where(c => !c.Value.IsArray))
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return new {classObject.ModuleSymbol.GetFullName()}();");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return new {classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}();");
}
Write($"throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
UnIndent(); Write("}");
Expand All @@ -51,7 +51,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes.Where(c => c.Value.IsArray))
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return new {classObject.ModuleSymbol.GetFullName(false)}[count];");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return new {classObject.ModuleSymbol.GetFullName(false).Replace("?", string.Empty)}[count];");
}
Write($"throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
UnIndent(); Write("}");
Expand All @@ -61,7 +61,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return {classObject.IsDictionary.ToString().ToLower()};");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return {classObject.IsDictionary.ToString().ToLower()};");
}
Write("return false;");
UnIndent(); Write("}");
Expand All @@ -71,7 +71,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return {classObject.IsArray.ToString().ToLower()};");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return {classObject.IsArray.ToString().ToLower()};");
}
Write("return false;");
UnIndent(); Write("}");
Expand All @@ -81,7 +81,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return {classObject.IsList.ToString().ToLower()};");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return {classObject.IsList.ToString().ToLower()};");
}
Write("return false;");
UnIndent(); Write("}");
Expand All @@ -101,9 +101,9 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)

if (type.IsGenericType)
{
keyType = type.TypeArguments[0].GetFullName();
keyType = type.TypeArguments[0].GetFullName().Replace("?", string.Empty);
}
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return typeof({keyType});");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return typeof({keyType});");
}
Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
UnIndent(); Write("}");
Expand All @@ -122,20 +122,20 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
if (classObject.IsDictionary)
{
//we're a dictionary
valueType = ((INamedTypeSymbol)classObject.ModuleSymbol).TypeArguments[1].GetFullName();
valueType = ((INamedTypeSymbol)classObject.ModuleSymbol).TypeArguments[1].GetFullName().Replace("?", string.Empty);
}
else if (classObject.IsList)
{
//we're a list
valueType = ((INamedTypeSymbol)classObject.ModuleSymbol).TypeArguments[0].GetFullName();
valueType = ((INamedTypeSymbol)classObject.ModuleSymbol).TypeArguments[0].GetFullName().Replace("?", string.Empty);
}
else
{
//we're an array
valueType = ((IArrayTypeSymbol)classObject.ModuleSymbol).ElementType.GetFullName();
valueType = ((IArrayTypeSymbol)classObject.ModuleSymbol).ElementType.GetFullName().Replace("?", string.Empty);
}

Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()})) return typeof({valueType});");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return typeof({valueType});");
}
Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
UnIndent(); Write("}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public StaticPropertyDescriptorFile(Action<string, bool> write, Action indent, A

public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
{
Write("public class StaticPropertyDescriptor : YamlDotNet.Serialization.IPropertyDescriptor");
Write("class StaticPropertyDescriptor : YamlDotNet.Serialization.IPropertyDescriptor");
Write("{"); Indent();
Write("private YamlDotNet.Serialization.IObjectAccessor _accessor;");
Write("private readonly Attribute[] _attributes;");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()}))");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}))");
Write("{"); Indent();
Write($"var accessor = new {classObject.SanitizedClassName}_{classObject.GuidSuffix}();");
Write("return new YamlDotNet.Serialization.IPropertyDescriptor[]");
Expand All @@ -70,7 +70,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName()}))");
Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}))");
Write("{"); Indent();
Write($"var accessor = new {classObject.SanitizedClassName}_{classObject.GuidSuffix}();");
foreach (var field in classObject.FieldSymbols)
Expand All @@ -96,7 +96,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)

private void WritePropertyDescriptor(string name, ITypeSymbol type, bool isReadonly, ImmutableArray<AttributeData> attributes, char finalChar)
{
Write($"new StaticPropertyDescriptor(accessor, \"{name}\", {(!isReadonly).ToString().ToLower()}, typeof({type.GetFullName().TrimEnd('?')}), new Attribute[] {{");
Write($"new StaticPropertyDescriptor(accessor, \"{name}\", {(!isReadonly).ToString().ToLower()}, typeof({type.GetFullName().Replace("?", string.Empty)}), new Attribute[] {{");
foreach (var attribute in attributes)
{
switch (attribute.AttributeClass?.ToDisplayString())
Expand Down
42 changes: 21 additions & 21 deletions YamlDotNet.Analyzers.StaticGenerator/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ public static string GetFullName(this ITypeSymbol symbol, bool includeArrayDimen
return symbol.Name;
}

public static string GetNamespace(this ISymbol symbol)
{
var parts = new Stack<string>();
var currentNamespace = symbol.ContainingNamespace;

while (currentNamespace != null)
{
if (!string.IsNullOrEmpty(currentNamespace.Name))
{
parts.Push(currentNamespace.Name);
currentNamespace = currentNamespace.ContainingNamespace;
}
else
{
break;
}
}

return string.Join(".", parts);
}

static string GetNullable(this ITypeSymbol symbol)
{
if (symbol.IsValueType || symbol.NullableAnnotation != NullableAnnotation.Annotated)
Expand Down Expand Up @@ -97,26 +118,5 @@ static string GetGenericTypes(IReadOnlyList<ITypeSymbol> typeArguments)

return string.Empty;
}

static string GetNamespace(this ISymbol symbol)
{
var parts = new Stack<string>();
var currentNamespace = symbol.ContainingNamespace;

while (currentNamespace != null)
{
if (!string.IsNullOrEmpty(currentNamespace.Name))
{
parts.Push(currentNamespace.Name);
currentNamespace = currentNamespace.ContainingNamespace;
}
else
{
break;
}
}

return string.Join(".", parts);
}
}
}
16 changes: 15 additions & 1 deletion YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public void Execute(GeneratorExecutionContext context)
return;
}

if (receiver.Classes.Count == 0)
{
return;
}

_context = context;

foreach (var log in receiver.Log)
Expand Down Expand Up @@ -73,9 +78,18 @@ private string GenerateSource(ClassSyntaxReceiver classSyntaxReceiver)
}
});

write("#pragma warning disable CS8767 // Nullability of reference types", true);
write("#pragma warning disable CS8767 // Nullability of reference types", true);
write("#pragma warning disable CS8603 // Possible null reference return", true);
write("#pragma warning disable CS8604 // Possible null reference argument", true);
write("#pragma warning disable CS8766 // Nullability of reference types", true);

write("using System;", true);
write("using System.Collections.Generic;", true);
write("namespace YamlDotNet.Static", true);

var namespaceName = classSyntaxReceiver.YamlStaticContextType?.ContainingNamespace.ContainingNamespace;

write($"namespace {classSyntaxReceiver.YamlStaticContextType?.GetNamespace() ?? "YamlDotNet.Static"}", true);
write("{", true); indent();

new StaticContextFile(write, indent, unindent, _context).Write(classSyntaxReceiver);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- dotnet pack -c release -o /packages -p:NuspecFile=YamlDotNet.Analyzers.StaticGenerator.nuspec -->
<package>
<metadata minClientVersion="2.8">
<id>YamlDotNet.Analyzers.StaticGenerator</id>
<version>0.0.1</version>
<authors>YamlDotNet Contributors</authors>
<description>Roslyn Code Generator that will generate a static context for use with YamlDotNet to support ahead-of-time and library trimming.</description>
<summary>Static context generator for YamlDotNet.</summary>
<language>en-US</language>
<license type="file">LICENSE.txt</license>
<projectUrl>https://github.com/aaubry/YamlDotNet/wiki</projectUrl>
<iconUrl>http://aaubry.net/images/yamldotnet.png</iconUrl>
<icon>images/yamldotnet.png</icon>
<repository type="git" url="https://github.com/aaubry/YamlDotNet.git" />
<tags>yaml parser development library serialization</tags>
</metadata>
<files>
<file src="bin/Release/netstandard2.0/YamlDotNet.Analyzers.StaticGenerator.dll" target="analyzers/dotnet/cs/netstandard2.0" />

<file src="../LICENSE.txt" target="" />
<file src="../yamldotnet.png" target="images/" />
</files>
</package>
16 changes: 8 additions & 8 deletions YamlDotNet.Core7AoTCompileTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

var input = new StringReader(yaml);

var aotContext = new YamlDotNet.Static.StaticContext();
var aotContext = new YamlDotNet.Core7AoTCompileTest.StaticContext();
var deserializer = new StaticDeserializerBuilder(aotContext)
.Build();

Expand Down Expand Up @@ -123,13 +123,13 @@
[YamlSerializable]
public class MyArray
{
public int[] myArray { get; set; }
public int[]? myArray { get; set; }
}

[YamlSerializable]
public class Inner
{
public string Text { get; set; }
public string? Text { get; set; }
}

[YamlSerializable]
Expand All @@ -151,15 +151,15 @@ public class PrimitiveTypes
public sbyte MySByte { get; set; }
public float MySingle { get; set; }
[YamlMember(ScalarStyle = ScalarStyle.DoubleQuoted)]
public string MyString { get; set; }
public string MyString { get; set; } = string.Empty;
public string? MyNullableString { get; set; }
public ushort MyUInt16 { get; set; }
public uint MyUInt32 { get; set; }
public ulong MyUInt64 { get; set; }
public Inner Inner { get; set; }
public MyArray MyArray { get; set; }
public Dictionary<string, string> MyDictionary { get; set; }
public List<string> MyList { get; set; }
public Inner? Inner { get; set; }
public MyArray? MyArray { get; set; }
public Dictionary<string, string>? MyDictionary { get; set; }
public List<string>? MyList { get; set; }
}

public enum MyTestEnum
Expand Down
5 changes: 4 additions & 1 deletion YamlDotNet.Core7AoTCompileTest/StaticAoTContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

namespace YamlDotNet.Static
using YamlDotNet.Serialization;

namespace YamlDotNet.Core7AoTCompileTest
{
// The rest of this partial class gets generated at build time
[YamlStaticContext]
public partial class StaticContext : YamlDotNet.Serialization.StaticContext
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFramework>net70</TargetFramework>
<PublishAot>true</PublishAot>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<Nullable>enable</Nullable>
</PropertyGroup>

<Import Project="../build/common.props" />
Expand Down

0 comments on commit 8ffa46e

Please sign in to comment.