diff --git a/YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs b/YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs index fe654dd0..8a527c58 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs @@ -44,69 +44,86 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context) if (attributes.Any(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlStaticContextAttribute")) { YamlStaticContextType = classSymbol; + + var types = + attributes.Where(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlSerializableAttribute" + && attribute.ConstructorArguments.Any(argument => argument.Type?.ToDisplayString() == "System.Type")) + .Select(attribute => attribute.ConstructorArguments.First().Value) + .ToArray(); + + foreach (var type in types.OfType()) + { + AddSerializableClass(type); + } } - if (classSymbol.GetAttributes().Any(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlSerializableAttribute")) + if (classSymbol.GetAttributes().Any(attribute => attribute.AttributeClass?.ToDisplayString() == "YamlDotNet.Serialization.YamlSerializableAttribute" + && attribute.ConstructorArguments.Length == 0)) { - ClassObject classObject; - var className = SanitizeName(classSymbol.GetFullName()); - if (Classes.ContainsKey(className)) + AddSerializableClass(classSymbol); + } + } + } + } + + private void AddSerializableClass(INamedTypeSymbol? classSymbol) + { + ClassObject classObject; + var className = SanitizeName(classSymbol!.GetFullName()); + if (Classes.ContainsKey(className)) + { + classObject = Classes[className]; + } + else + { + classObject = new ClassObject(className, classSymbol!); + Classes[className] = classObject; + } + while (classSymbol != null) + { + var members = classSymbol.GetMembers(); + foreach (var member in members) + { + if (member.IsStatic || + (member.DeclaredAccessibility != Accessibility.Public && + member.DeclaredAccessibility != Accessibility.Internal) || + member.GetAttributes().Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.YamlIgnoreAttribute")) + { + continue; + } + + if (member is IPropertySymbol propertySymbol) + { + classObject.PropertySymbols.Add(propertySymbol); + CheckForSupportedGeneric(propertySymbol.Type); + } + else if (member is IFieldSymbol fieldSymbol) + { + classObject.FieldSymbols.Add(fieldSymbol); + CheckForSupportedGeneric(fieldSymbol.Type); + } + else if (member is IMethodSymbol methodSymbol) + { + var methodAttributes = methodSymbol.GetAttributes(); + if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute")) { - classObject = Classes[className]; + classObject.OnDeserializedMethods.Add(methodSymbol); } - else + if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute")) { - classObject = new ClassObject(className, classSymbol); - Classes[className] = classObject; + classObject.OnDeserializingMethods.Add(methodSymbol); } - while (classSymbol != null) + if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializedAttribute")) { - var members = classSymbol.GetMembers(); - foreach (var member in members) - { - if (member.IsStatic || - (member.DeclaredAccessibility != Accessibility.Public && - member.DeclaredAccessibility != Accessibility.Internal) || - member.GetAttributes().Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.YamlIgnoreAttribute")) - { - continue; - } - - if (member is IPropertySymbol propertySymbol) - { - classObject.PropertySymbols.Add(propertySymbol); - CheckForSupportedGeneric(propertySymbol.Type); - } - else if (member is IFieldSymbol fieldSymbol) - { - classObject.FieldSymbols.Add(fieldSymbol); - CheckForSupportedGeneric(fieldSymbol.Type); - } - else if (member is IMethodSymbol methodSymbol) - { - var methodAttributes = methodSymbol.GetAttributes(); - if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute")) - { - classObject.OnDeserializedMethods.Add(methodSymbol); - } - if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute")) - { - classObject.OnDeserializingMethods.Add(methodSymbol); - } - if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializedAttribute")) - { - classObject.OnSerializedMethods.Add(methodSymbol); - } - if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializingAttribute")) - { - classObject.OnSerializingMethods.Add(methodSymbol); - } - } - } - classSymbol = classSymbol.BaseType; + classObject.OnSerializedMethods.Add(methodSymbol); + } + if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializingAttribute")) + { + classObject.OnSerializingMethods.Add(methodSymbol); } } } + classSymbol = classSymbol.BaseType; } } diff --git a/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs b/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs index 0a95a796..954cf4b0 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs @@ -21,7 +21,6 @@ using System; using System.Text; -using System.Xml; using Microsoft.CodeAnalysis; namespace YamlDotNet.Analyzers.StaticGenerator diff --git a/YamlDotNet.Core7AoTCompileTest.Model/ExternalModel.cs b/YamlDotNet.Core7AoTCompileTest.Model/ExternalModel.cs new file mode 100644 index 00000000..a882325d --- /dev/null +++ b/YamlDotNet.Core7AoTCompileTest.Model/ExternalModel.cs @@ -0,0 +1,6 @@ +namespace YamlDotNet.Core7AoTCompileTest.Model; + +public class ExternalModel +{ + public string? Text { get; set; } +} diff --git a/YamlDotNet.Core7AoTCompileTest.Model/YamlDotNet.Core7AoTCompileTest.Model.csproj b/YamlDotNet.Core7AoTCompileTest.Model/YamlDotNet.Core7AoTCompileTest.Model.csproj new file mode 100644 index 00000000..2dfd6d2c --- /dev/null +++ b/YamlDotNet.Core7AoTCompileTest.Model/YamlDotNet.Core7AoTCompileTest.Model.csproj @@ -0,0 +1,11 @@ + + + + net70 + enable + enable + + + + + diff --git a/YamlDotNet.Core7AoTCompileTest/Program.cs b/YamlDotNet.Core7AoTCompileTest/Program.cs index 87d5de1d..9e8a4123 100644 --- a/YamlDotNet.Core7AoTCompileTest/Program.cs +++ b/YamlDotNet.Core7AoTCompileTest/Program.cs @@ -24,12 +24,14 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using YamlDotNet.Core; +using YamlDotNet.Core7AoTCompileTest.Model; using YamlDotNet.Serialization; using YamlDotNet.Serialization.Callbacks; -string yaml = $@"MyBool: true +string yaml = string.Create(CultureInfo.InvariantCulture, $@"MyBool: true hi: 1 MyChar: h MyDateTime: {DateTime.Now} @@ -65,7 +67,9 @@ Inherited: Inherited: hello NotInherited: world -"; +External: + Text: hello +"); var input = new StringReader(yaml); @@ -91,6 +95,7 @@ Console.WriteLine("MyUInt64: <{0}>", x.MyUInt64); Console.WriteLine("Inner == null: <{0}>", x.Inner == null); Console.WriteLine("Inner.Text: <{0}>", x.Inner?.Text); +Console.WriteLine("External.Text: <{0}>", x.External?.Text); foreach (var inner in x.InnerArray) { Console.WriteLine("InnerArray.Text: <{0}>", inner.Text); @@ -188,6 +193,7 @@ public class PrimitiveTypes public Dictionary? MyDictionary { get; set; } public List? MyList { get; set; } public Inherited Inherited { get; set; } + public ExternalModel External { get; set; } } public class InheritedBase diff --git a/YamlDotNet.Core7AoTCompileTest/StaticAoTContext.cs b/YamlDotNet.Core7AoTCompileTest/StaticAoTContext.cs index b455ce0c..f661c96a 100644 --- a/YamlDotNet.Core7AoTCompileTest/StaticAoTContext.cs +++ b/YamlDotNet.Core7AoTCompileTest/StaticAoTContext.cs @@ -19,12 +19,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +using YamlDotNet.Core7AoTCompileTest.Model; using YamlDotNet.Serialization; namespace YamlDotNet.Core7AoTCompileTest { // The rest of this partial class gets generated at build time [YamlStaticContext] + [YamlSerializable(typeof(ExternalModel))] public partial class StaticContext : YamlDotNet.Serialization.StaticContext { } diff --git a/YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj b/YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj index dbd640dc..5867c74b 100644 --- a/YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj +++ b/YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj @@ -25,6 +25,7 @@ + diff --git a/YamlDotNet.sln b/YamlDotNet.sln index 2951c871..85ca3975 100644 --- a/YamlDotNet.sln +++ b/YamlDotNet.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet.Core7AoTCompileT EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "YamlDotNet.Samples.Fsharp", "YamlDotNet.Samples.Fsharp\YamlDotNet.Samples.Fsharp.fsproj", "{C047392D-6B20-47CD-9FE6-D0FA326FD262}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet.Core7AoTCompileTest.Model", "YamlDotNet.Core7AoTCompileTest.Model\YamlDotNet.Core7AoTCompileTest.Model.csproj", "{BFE15564-7C2C-47DA-8302-9BCB39B6864B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +71,10 @@ Global {C047392D-6B20-47CD-9FE6-D0FA326FD262}.Debug|Any CPU.Build.0 = Debug|Any CPU {C047392D-6B20-47CD-9FE6-D0FA326FD262}.Release|Any CPU.ActiveCfg = Release|Any CPU {C047392D-6B20-47CD-9FE6-D0FA326FD262}.Release|Any CPU.Build.0 = Release|Any CPU + {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/YamlDotNet/Serialization/YamlSerializable.cs b/YamlDotNet/Serialization/YamlSerializable.cs index e6198017..49fc5dcb 100644 --- a/YamlDotNet/Serialization/YamlSerializable.cs +++ b/YamlDotNet/Serialization/YamlSerializable.cs @@ -24,10 +24,25 @@ namespace YamlDotNet.Serialization { /// - /// Put this attribute on classes that you want the static analyzer to detect and use. + /// Put this attribute either on serializable types or on the that you want + /// the static analyzer to detect and use. /// - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class YamlSerializableAttribute : Attribute { + /// + /// Use this constructor if the attribute is placed on a serializable class. + /// + public YamlSerializableAttribute() + { + } + + /// + /// Use this constructor if the attribute is placed on the . + /// + /// The type for which to include static code generation. + public YamlSerializableAttribute(Type serializableType) + { + } } }