diff --git a/README.md b/README.md index 9ef1248..66da656 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ namespace Example IGeneratorEnvironment generatorEnvironment, ILogger logger) : base("ExampleSourceGenerator", generatorPlatform, logger) - { + {$$ } @@ -108,46 +108,6 @@ This will popup the following window and you have to select your visual studio i ![AutoAttach](./img/DebuggerAttach.gif) -## Polyfills - -Source generators are written targeting `netstandard2.0` which means you don't have access to some features of the language. By using this library you will get the following. - -### `init` Properties: -```cs -// init gives a compiler error without the polyfill -public string MyProperty { get; init; } -``` -These can be disabled by setting the `false`. The default value is `true`. - -### Required Members -A way of specifying that a property or field is required to be set during object initialization, forcing the instance creator to provide an initial value for the member in an object initializer at the creation site. - -```cs -public class MyClass -{ - public required string Name { get; init; } -} -``` -These can be disabled by setting the `false`. The default value is `true`. - -### Nullable Attributes -These attributes enable the compiler to provide warnings when you may dereference a null value, throwing a [System.NullReferenceException](https://learn.microsoft.com/en-us/dotnet/api/system.nullreferenceexception). -| Attribute | Category | Meaning | -|-----------|----------|---------| -| `AllowNull` | Precondition | A non-nullable parameter, field, or property may be null. -| `DisallowNull` | Precondition | A nullable parameter, field, or property should never be null. -| `MaybeNull` | Postcondition | A non-nullable parameter, field, property, or return value may be null. -| `NotNull` | Postcondition | A nullable parameter, field, property, or return value will never be null. -| `MaybeNullWhen` | Conditional postcondition | A non-nullable argument may be null when the method returns the specified bool value. -| `NotNullWhen` | Conditional postcondition | A nullable argument won't be null when the method returns the specified bool value. -| `NotNullIfNotNull` | Conditional postcondition | A return value, property, or argument isn't null if the argument for the specified parameter isn't null. -| `MemberNotNull` | Method and property helper methods | The listed member won't be null when the method returns. -| `MemberNotNullWhen` | Method and property helper methods | The listed member won't be null when the method returns the specified bool value. -| `DoesNotReturn` | Unreachable code | A method or property never returns. In other words, it always throws an exception. -| `DoesNotReturnIf` | Unreachable code | This method or property never returns if the associated bool parameter has the specified value. - -These can be disabled by setting the `false`. The default value is `true`. - ## Helpers Included in the project is a series of helper classes to help you while working on your generator. @@ -162,14 +122,6 @@ Included in the project is a series of helper classes to help you while working # How it works -## Script Injector -It all starts with `ScriptInjector.cs`. This is a source generator loops over the resources within the current assembly and finds all `*.cs` files whos name starts with `SGF.Script::` and copies them into the target assembly. The image below shows -some of the scripts that are copied. - -![ScriptInjector](./img/ScriptInjector.jpg) - -If you would like a script to be copied over you can add it as a `ItemGroup` element called `SGF_EmbeddedScript` which will be embedded in your assembly and prefixed with the correct name. - ## Assembly Resolver When your source generator runs it needs to find it's dependencies and this is often what fails. When you want to provide external packages you will have to write your own resolve that is able to locate the required assemblies. So instead we have the `ScriptInjector` inject an implemention for you. This assembly resolver will loop over all the resources in the current assembly and look for all resources that start with `SGF.Assembly::`. If the assembly being requested to be loaded exists in the resources, it's extracted and loaded into the current appdomain. diff --git a/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleApp.SourceGenerator.csproj b/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleApp.SourceGenerator.csproj index db95b59..b59e3d0 100644 --- a/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleApp.SourceGenerator.csproj +++ b/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleApp.SourceGenerator.csproj @@ -1,6 +1,7 @@  + 11 netstandard2.0 true AnyCPU;x64 diff --git a/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleAppSourceGenerator.cs b/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleAppSourceGenerator.cs index 493dabf..7e573ec 100644 --- a/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleAppSourceGenerator.cs +++ b/src/Sandbox/ConsoleApp.SourceGenerator/ConsoleAppSourceGenerator.cs @@ -1,8 +1,10 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; using Newtonsoft.Json; using SGF; using SGF.Diagnostics; using System; +using System.Text; namespace ConsoleApp.SourceGenerator { @@ -15,7 +17,7 @@ public class Payload public string? Version { get; set; } } - public ConsoleAppSourceGenerator(IGeneratorEnvironment generatorEnvironment, ILogger logger) : base("ConsoleAppSourceGenerator", generatorEnvironment, logger) + public ConsoleAppSourceGenerator() : base("ConsoleAppSourceGenerator") { } @@ -33,9 +35,31 @@ public override void OnInitialize(SgfInitializationContext context) Logger.Information("This generator references Newtonsoft.Json and it can just be referenced without any other boilerplate"); Logger.Information(JsonConvert.SerializeObject(payload)); Logger.Information("Having the log makes working with generators much simpler!"); + + context.RegisterPostInitializationOutput(AddSource); + } + + private void AddSource(IncrementalGeneratorPostInitializationContext context) + { + SourceText sourceText = SourceText.From(""" + namespace Examples + { + public class Person + { + public string Name { get; } + + public Person(string name) + { + Name = name; + } + } + } + """, Encoding.UTF8); + + context.AddSource("Person.g.cs", sourceText); } - protected override void OnException(Exception exception) + public override void OnException(Exception exception) { base.OnException(exception); diff --git a/src/Sandbox/ConsoleApp/Program.cs b/src/Sandbox/ConsoleApp/Program.cs index 3da11fd..67e713c 100644 --- a/src/Sandbox/ConsoleApp/Program.cs +++ b/src/Sandbox/ConsoleApp/Program.cs @@ -1,7 +1,10 @@ -public static class Program +using Examples; + +public static class Program { public static void Main(string[] arguments) { + Person person = new Person(""); //Testing(1); } diff --git a/src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs b/src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs index 48220bc..96f47ff 100644 --- a/src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs +++ b/src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs @@ -2,6 +2,11 @@ using System.Diagnostics; using SGF.Diagnostics; using Microsoft.CodeAnalysis; +using SGF.Environments; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Linq; +using SGF.Diagnostics.Sinks; namespace SGF { @@ -12,31 +17,46 @@ namespace SGF /// public abstract class IncrementalGenerator : IDisposable { - private readonly IGeneratorEnvironment m_developmentPlatform; + private readonly static IGeneratorEnvironment s_environment; /// /// Gets the name of the source generator /// - public string Name { get; } + public string Name { get; private set; } /// /// Gets the log that can allow you to output information to your /// IDE of choice /// - public ILogger Logger { get; } + public ILogger Logger { get; private set; } // Set with reflection, don't change + static IncrementalGenerator() + { + s_environment = CreateEnvironment(); + } + /// - /// Initializes a new instance of the incremental generator with an optional name + /// Initializes a new instance of the incremental generator. Note both + /// and will be provided by the framework. /// - protected IncrementalGenerator( - string? name, - IGeneratorEnvironment developmentPlatform, - ILogger logger) + protected IncrementalGenerator(string? name) { - m_developmentPlatform = developmentPlatform; Name = name ?? GetType().Name; - Logger = logger; + Logger = new Logger(Name); + if(s_environment != null) + { + foreach(ILogSink sink in s_environment.GetLogSinks()) + { + Logger.AddSink(sink); + } + } +#pragma warning disable RS1035 // Do not use APIs banned for analyzers + if (Environment.UserInteractive) + { + Logger.AddSink(); + } +#pragma warning restore RS1035 // Do not use APIs banned for analyzers } /// @@ -57,7 +77,7 @@ protected virtual void Dipose() protected void AttachDebugger() { Process process = Process.GetCurrentProcess(); - m_developmentPlatform.AttachDebugger(process.Id); + s_environment.AttachDebugger(process.Id); } /// @@ -65,7 +85,7 @@ protected void AttachDebugger() /// to handle the exception. /// /// The exception that was thrown - protected virtual void OnException(Exception exception) + public virtual void OnException(Exception exception) { Logger.Error(exception, $"Unhandled exception was throw while running the generator {Name}"); } @@ -81,6 +101,48 @@ private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) } } + /// + /// Gets an instance of a development platform to be used to log and debug info + /// + /// + private static IGeneratorEnvironment CreateEnvironment() + { + string? platformAssembly = null; + IGeneratorEnvironment? platform = null; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Windows Development Platform + platformAssembly = "SourceGenerator.Foundations.Windows"; + } + else + { + // Generic Development Platform + platformAssembly = "SourceGenerator.Foundations.Contracts"; + } + + if (!string.IsNullOrWhiteSpace(platformAssembly)) + { + AssemblyName assemblyName = new(platformAssembly); + Assembly? assembly = null; + try + { + assembly = Assembly.Load(platformAssembly); + Type? platformType = assembly? + .GetTypes() + .Where(typeof(IGeneratorEnvironment).IsAssignableFrom) + .FirstOrDefault(); + if (platformType != null) + { + platform = Activator.CreateInstance(platformType) as IGeneratorEnvironment; + } + } + catch + { } + } + + return platform ?? new GenericDevelopmentEnvironment(); + } + /// void IDisposable.Dispose() { diff --git a/src/SourceGenerator.Foundations.Shared/Core/Configuration/ResourceConfiguration.cs b/src/SourceGenerator.Foundations.Shared/Configuration/ResourceConfiguration.cs similarity index 100% rename from src/SourceGenerator.Foundations.Shared/Core/Configuration/ResourceConfiguration.cs rename to src/SourceGenerator.Foundations.Shared/Configuration/ResourceConfiguration.cs diff --git a/src/SourceGenerator.Foundations.Shared/Core/Reflection/AssemblyResolver.cs b/src/SourceGenerator.Foundations.Shared/Core/Reflection/AssemblyResolver.cs deleted file mode 100644 index 3cb8cc2..0000000 --- a/src/SourceGenerator.Foundations.Shared/Core/Reflection/AssemblyResolver.cs +++ /dev/null @@ -1,162 +0,0 @@ -#nullable enable -using SGF.Configuration; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace SGF.Reflection -{ - internal static class AssemblyResolver - { - private static readonly List s_assembliesWithResources; - private static readonly Dictionary s_loadedAssemblies; - - static AssemblyResolver() - { - s_assembliesWithResources = new List(); - s_loadedAssemblies = new Dictionary(new AssemblyNameComparer()); - } - - internal static void Initialize() - { - // The assembly resolvers get added to multiple source generators - // so what we do here is only allow the first one defined to allow - // itself to be a resolver. Since this could lead to cases where two resolvers - // exists and provide two different instances of the same assembly. - const string RESOLVER_ATTACHED_KEY = "SGF_ASSEMBLY_RESOLVER_IS_ATTACHED"; - AppDomain currentDomain = AppDomain.CurrentDomain; - object? rawValue = currentDomain.GetData(RESOLVER_ATTACHED_KEY); - - if (rawValue == null || (rawValue is bool isAttached && !isAttached)) - { - currentDomain.SetData(RESOLVER_ATTACHED_KEY, true); - currentDomain.AssemblyLoad += OnAssemblyLoaded; - currentDomain.AssemblyResolve += ResolveMissingAssembly; - - foreach (Assembly assembly in currentDomain.GetAssemblies()) - { - AddAssembly(assembly); - } - } - } - - /// - /// Raised whenever our app domain loads a new assembly - /// - /// THe thing that raised the event - /// The parameters - private static void OnAssemblyLoaded(object sender, AssemblyLoadEventArgs args) - { - AddAssembly(args.LoadedAssembly); - } - - /// - /// Adds an assembly to the veriuos collections used to keep track of loaded items - /// - private static void AddAssembly(Assembly assembly) - { - AssemblyName assemblyName = assembly.GetName(); - - if (s_loadedAssemblies.ContainsKey(assemblyName)) - { - return; - } - s_loadedAssemblies.Add(assemblyName, assembly); - - if (assembly.IsDynamic) return; - - string[] resources = assembly.GetManifestResourceNames() - .Where(r => r.StartsWith(ResourceConfiguration.AssemblyResourcePrefix)) - .ToArray(); - - if (resources.Length == 0) return; - - foreach (string resource in resources) - { - Console.WriteLine($"Extracting: {resource}"); - if (TryExtractingAssembly(assembly, resource, out Assembly? loadedAssembly)) - { - AddAssembly(loadedAssembly!); - } - } - } - - /// - /// Attempts to resolve any assembly by looking for dependencies that are embedded directly - /// in this dll. - /// - private static Assembly? ResolveMissingAssembly(object sender, ResolveEventArgs args) - { - AssemblyName assemblyName = new(args.Name); - - if (s_loadedAssemblies.TryGetValue(assemblyName, out Assembly assembly)) - { - return assembly; - } - - foreach (Assembly loadedAssembly in s_assembliesWithResources) - { - string resourceName = $"{ResourceConfiguration.AssemblyResourcePrefix}{assemblyName.Name}.dll"; - if (TryExtractingAssembly(loadedAssembly, resourceName, out Assembly? extractedAssembly)) - { - AddAssembly(extractedAssembly!); - return extractedAssembly!; - }; - } - - return null; - } - - - /// - /// Attempts to load an assembly that is contained within aonther assembly as a resource - /// - /// The assembly that should contain the resource - /// The expected name of the reosurce - /// The assembly if it was loaded - /// True if the assembly could be loaded otherwise false - private static bool TryExtractingAssembly(Assembly assembly, string resourceName, out Assembly? loadedAssembly) - { - loadedAssembly = null; - if (TryGetResourceBytes(assembly, resourceName, out byte[]? assemblyBytes)) - { -#pragma warning disable RS1035 // Do not use APIs banned for analyzers - loadedAssembly = TryGetResourceBytes(assembly, Path.ChangeExtension(resourceName, ".pdb"), out byte[]? symbolBytes) - ? Assembly.Load(assemblyBytes, symbolBytes) - : Assembly.Load(assemblyBytes); -#pragma warning restore RS1035 // Do not use APIs banned for analyzers - return true; - } - return false; - } - - /// - /// Attempts to read bytes from a resource and returns back if it's successful or not - /// - /// The assembly to pull the resource from - /// The name of the resource - /// The bytes[] if the resource could be found - /// True if the resource was found otherwise false - private static bool TryGetResourceBytes(Assembly assembly, string resourceName, out byte[]? bytes) - { - bytes = null; - ManifestResourceInfo resourceInfo = assembly.GetManifestResourceInfo(resourceName); - if (resourceInfo == null) - { - return false; - } - - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - { - bytes = new byte[stream.Length]; - _ = stream.Read(bytes, 0, bytes.Length); - } - - return true; - } - } -} \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Core/IncrementalGenerator.cs b/src/SourceGenerator.Foundations.Shared/IncrementalGenerator.cs similarity index 100% rename from src/SourceGenerator.Foundations.Shared/Core/IncrementalGenerator.cs rename to src/SourceGenerator.Foundations.Shared/IncrementalGenerator.cs diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/InitProperties/IsExternalInit.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/InitProperties/IsExternalInit.poly.cs deleted file mode 100644 index ecca3de..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/InitProperties/IsExternalInit.poly.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace System.Runtime.CompilerServices -{ - using System.ComponentModel; - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit - { - } -} \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/AllowNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/AllowNullAttribute.poly.cs deleted file mode 100644 index d8f4def..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/AllowNullAttribute.poly.cs +++ /dev/null @@ -1,23 +0,0 @@ -#nullable enable -#pragma warning disable -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that is allowed as an input even if the - /// corresponding type disallows it. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class AllowNullAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public AllowNullAttribute() { } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DisallowNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DisallowNullAttribute.poly.cs deleted file mode 100644 index 68e9bbe..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DisallowNullAttribute.poly.cs +++ /dev/null @@ -1,24 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that is disallowed as an input even if the - /// corresponding type allows it. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class DisallowNullAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public DisallowNullAttribute() { } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnAttribute.poly.cs deleted file mode 100644 index 8707979..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnAttribute.poly.cs +++ /dev/null @@ -1,22 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that a method that will never return under any circumstance. - /// - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class DoesNotReturnAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public DoesNotReturnAttribute() { } - } -} -#pragma warning restore -#nullable restore diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnIfAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnIfAttribute.poly.cs deleted file mode 100644 index 6f6237a..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/DoesNotReturnIfAttribute.poly.cs +++ /dev/null @@ -1,37 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that the method will not return if the associated - /// parameter is passed the specified value. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// - /// Gets the condition parameter value. Code after the method is considered unreachable - /// by diagnostics if the argument to the associated parameter matches this value. - /// - public bool ParameterValue { get; } - - /// - /// Initializes a new instance of the - /// class with the specified parameter value. - /// - /// - /// The condition parameter value. Code after the method is considered - /// unreachable by diagnostics if the argument to the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) - { - } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullAttribute.poly.cs deleted file mode 100644 index 998b214..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullAttribute.poly.cs +++ /dev/null @@ -1,28 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that an output may be even if the - /// corresponding type disallows it. - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Parameter | - AttributeTargets.Property | AttributeTargets.ReturnValue, - Inherited = false - )] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class MaybeNullAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public MaybeNullAttribute() { } - } -} - -#pragma warning restore -#nullable restore diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullWhenAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullWhenAttribute.poly.cs deleted file mode 100644 index 47b355d..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MaybeNullWhenAttribute.poly.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that when a method returns , - /// the parameter may be even if the corresponding type disallows it. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// - /// Gets the return value condition. - /// If the method returns this value, the associated parameter may be . - /// - public bool ReturnValue { get; } - - /// - /// Initializes the attribute with the specified return value condition. - /// - /// - /// The return value condition. - /// If the method returns this value, the associated parameter may be . - /// - public MaybeNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - } -} -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullAttribute.poly.cs deleted file mode 100644 index a1abf48..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullAttribute.poly.cs +++ /dev/null @@ -1,46 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that the method or property will ensure that the listed field and property members have - /// not- values. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class MemberNotNullAttribute : Attribute - { - /// - /// Gets field or property member names. - /// - public string[] Members { get; } - - /// - /// Initializes the attribute with a field or property member. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) - { - Members = new[] { member }; - } - - /// - /// Initializes the attribute with the list of field and property members. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) - { - Members = members; - } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullWhenAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullWhenAttribute.poly.cs deleted file mode 100644 index e1d1f76..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/MemberNotNullWhenAttribute.poly.cs +++ /dev/null @@ -1,62 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that the method or property will ensure that the listed field and property members have - /// non- values when returning with the specified return value condition. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// - /// Gets the return value condition. - /// - public bool ReturnValue { get; } - - /// - /// Gets field or property member names. - /// - public string[] Members { get; } - - /// - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// - /// The return value condition. If the method returns this value, - /// the associated parameter will not be . - /// - /// - /// The field or property member that is promised to be not-. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// - /// Initializes the attribute with the specified return value condition and list - /// of field and property members. - /// - /// - /// The return value condition. If the method returns this value, - /// the associated parameter will not be . - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullAttribute.poly.cs deleted file mode 100644 index a7d7812..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullAttribute.poly.cs +++ /dev/null @@ -1,28 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that an output is not even if the - /// corresponding type allows it. - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Parameter | - AttributeTargets.Property | AttributeTargets.ReturnValue, - Inherited = false - )] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class NotNullAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public NotNullAttribute() { } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullIfNotNullAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullIfNotNullAttribute.poly.cs deleted file mode 100644 index ce844e2..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullIfNotNullAttribute.poly.cs +++ /dev/null @@ -1,43 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that the output will be non- if the - /// named parameter is non-. - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, - AllowMultiple = true, - Inherited = false - )] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// - /// Gets the associated parameter name. - /// The output will be non- if the argument to the - /// parameter specified is non-. - /// - public string ParameterName { get; } - - /// - /// Initializes the attribute with the associated parameter name. - /// - /// - /// The associated parameter name. - /// The output will be non- if the argument to the - /// parameter specified is non-. - /// - public NotNullIfNotNullAttribute(string parameterName) - { - ParameterName = parameterName; - } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullWhenAttribute.poly.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullWhenAttribute.poly.cs deleted file mode 100644 index 8c6cf82..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/Nullable/NotNullWhenAttribute.poly.cs +++ /dev/null @@ -1,37 +0,0 @@ -#nullable enable -#pragma warning disable - -namespace System.Diagnostics.CodeAnalysis -{ - using global::System; - - /// - /// Specifies that when a method returns , - /// the parameter will not be even if the corresponding type allows it. - /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal sealed class NotNullWhenAttribute : Attribute - { - /// - /// Gets the return value condition. - /// If the method returns this value, the associated parameter will not be . - /// - public bool ReturnValue { get; } - - /// - /// Initializes the attribute with the specified return value condition. - /// - /// - /// The return value condition. - /// If the method returns this value, the associated parameter will not be . - /// - public NotNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - } -} - -#pragma warning restore -#nullable restore \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Polyfills/RequiredMembers/RequiredMemberAttribute.cs b/src/SourceGenerator.Foundations.Shared/Polyfills/RequiredMembers/RequiredMemberAttribute.cs deleted file mode 100644 index 1fd40a8..0000000 --- a/src/SourceGenerator.Foundations.Shared/Polyfills/RequiredMembers/RequiredMemberAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.CompilerServices -{ - /// - /// Specifies that a type has required members or that a member is required. - /// - [global::System.AttributeUsage( - global::System.AttributeTargets.Class | - global::System.AttributeTargets.Struct | - global::System.AttributeTargets.Field | - global::System.AttributeTargets.Property, - AllowMultiple = false, - Inherited = false)] - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - internal sealed class RequiredMemberAttribute : global::System.Attribute - { - } -} \ No newline at end of file diff --git a/src/SourceGenerator.Foundations.Shared/Core/Reflection/AssemblyNameComparer.cs b/src/SourceGenerator.Foundations.Shared/Reflection/AssemblyNameComparer.cs similarity index 100% rename from src/SourceGenerator.Foundations.Shared/Core/Reflection/AssemblyNameComparer.cs rename to src/SourceGenerator.Foundations.Shared/Reflection/AssemblyNameComparer.cs diff --git a/src/SourceGenerator.Foundations.Shared/Core/SgfInitializationContext.cs b/src/SourceGenerator.Foundations.Shared/SgfInitializationContext.cs similarity index 100% rename from src/SourceGenerator.Foundations.Shared/Core/SgfInitializationContext.cs rename to src/SourceGenerator.Foundations.Shared/SgfInitializationContext.cs diff --git a/src/SourceGenerator.Foundations.Shared/Core/SgfSourceProductionContext.cs b/src/SourceGenerator.Foundations.Shared/SgfSourceProductionContext.cs similarity index 100% rename from src/SourceGenerator.Foundations.Shared/Core/SgfSourceProductionContext.cs rename to src/SourceGenerator.Foundations.Shared/SgfSourceProductionContext.cs diff --git a/src/SourceGenerator.Foundations.sln b/src/SourceGenerator.Foundations.sln index 1f73b58..7c4d601 100644 --- a/src/SourceGenerator.Foundations.sln +++ b/src/SourceGenerator.Foundations.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGenerator.Foundations EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{F0124C8F-B8FB-4832-9D65-D0F13E4308AE}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SourceGenerator.Foundations.Shared", "SourceGenerator.Foundations.Shared\SourceGenerator.Foundations.Shared.shproj", "{8AF3630C-2BF5-4854-A45D-0074C2787964}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -97,4 +99,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EDB10920-970A-43F9-A2B3-7F1270DD477B} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + SourceGenerator.Foundations.Shared\SourceGenerator.Foundations.Shared.projitems*{8af3630c-2bf5-4854-a45d-0074c2787964}*SharedItemsImports = 13 + EndGlobalSection EndGlobal diff --git a/src/SourceGenerator.Foundations/Analyzer/SourceGeneratorAnalyzer.cs b/src/SourceGenerator.Foundations/Analyzer/SourceGeneratorAnalyzer.cs index 30d9b49..9118f4b 100644 --- a/src/SourceGenerator.Foundations/Analyzer/SourceGeneratorAnalyzer.cs +++ b/src/SourceGenerator.Foundations/Analyzer/SourceGeneratorAnalyzer.cs @@ -1,4 +1,6 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System; using System.Collections.Immutable; @@ -31,6 +33,13 @@ public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(CheckForAttribute, SyntaxKind.ClassDeclaration); + } + + private void CheckForAttribute(SyntaxNodeAnalysisContext context) + { + + } } } diff --git a/src/SourceGenerator.Foundations/SourceGenerator.Foundations.csproj b/src/SourceGenerator.Foundations/SourceGenerator.Foundations.csproj index 788be2b..0601463 100644 --- a/src/SourceGenerator.Foundations/SourceGenerator.Foundations.csproj +++ b/src/SourceGenerator.Foundations/SourceGenerator.Foundations.csproj @@ -20,6 +20,7 @@ + diff --git a/src/SourceGenerator.Foundations/Templates/AssemblyResolver.cs b/src/SourceGenerator.Foundations/Templates/AssemblyResolver.cs index c44f53f..427dfe7 100644 --- a/src/SourceGenerator.Foundations/Templates/AssemblyResolver.cs +++ b/src/SourceGenerator.Foundations/Templates/AssemblyResolver.cs @@ -41,155 +41,7 @@ private static string GetName(AssemblyName assemblyName) } } - internal static class AssemblyResolver - { - private static readonly List s_assembliesWithResources; - private static readonly Dictionary s_loadedAssemblies; - - static AssemblyResolver() - { - s_assembliesWithResources = new List(); - s_loadedAssemblies = new Dictionary(new AssemblyNameComparer()); - } - - internal static void Initialize() - { - // The assembly resolvers get added to multiple source generators - // so what we do here is only allow the first one defined to allow - // itself to be a resolver. Since this could lead to cases where two resolvers - // exists and provide two different instances of the same assembly. - const string RESOLVER_ATTACHED_KEY = "SGF_ASSEMBLY_RESOLVER_IS_ATTACHED"; - AppDomain currentDomain = AppDomain.CurrentDomain; - object? rawValue = currentDomain.GetData(RESOLVER_ATTACHED_KEY); - - if (rawValue == null || (rawValue is bool isAttached && !isAttached)) - { - currentDomain.SetData(RESOLVER_ATTACHED_KEY, true); - currentDomain.AssemblyLoad += OnAssemblyLoaded; - currentDomain.AssemblyResolve += ResolveMissingAssembly; - - foreach (Assembly assembly in currentDomain.GetAssemblies()) - { - AddAssembly(assembly); - } - } - } - - /// - /// Raised whenever our app domain loads a new assembly - /// - /// THe thing that raised the event - /// The parameters - private static void OnAssemblyLoaded(object sender, AssemblyLoadEventArgs args) - { - AddAssembly(args.LoadedAssembly); - } - - /// - /// Adds an assembly to the veriuos collections used to keep track of loaded items - /// - private static void AddAssembly(Assembly assembly) - { - AssemblyName assemblyName = assembly.GetName(); - - if (s_loadedAssemblies.ContainsKey(assemblyName)) - { - return; - } - s_loadedAssemblies.Add(assemblyName, assembly); - - if (assembly.IsDynamic) return; - - string[] resources = assembly.GetManifestResourceNames() - .Where(r => r.StartsWith("SGF.Assembly::")) - .ToArray(); - - if (resources.Length == 0) return; - - foreach (string resource in resources) - { - Console.WriteLine($"Extracting {resource} assembly from {assemblyName.Name}'s resources."); - if (TryExtractingAssembly(assembly, resource, out Assembly? loadedAssembly)) - { - AddAssembly(loadedAssembly!); - } - } - } - - /// - /// Attempts to resolve any assembly by looking for dependencies that are embedded directly - /// in this dll. - /// - private static Assembly? ResolveMissingAssembly(object sender, ResolveEventArgs args) - { - AssemblyName assemblyName = new(args.Name); - - if (s_loadedAssemblies.TryGetValue(assemblyName, out Assembly assembly)) - { - return assembly; - } - - foreach (Assembly loadedAssembly in s_assembliesWithResources) - { - string resourceName = $"SGF.Assembly::{assemblyName.Name}.dll"; - if (TryExtractingAssembly(loadedAssembly, resourceName, out Assembly? extractedAssembly)) - { - AddAssembly(extractedAssembly!); - return extractedAssembly!; - }; - } - - return null; - } - - - /// - /// Attempts to load an assembly that is contained within aonther assembly as a resource - /// - /// The assembly that should contain the resource - /// The expected name of the reosurce - /// The assembly if it was loaded - /// True if the assembly could be loaded otherwise false - private static bool TryExtractingAssembly(Assembly assembly, string resourceName, out Assembly? loadedAssembly) - { - loadedAssembly = null; - if (TryGetResourceBytes(assembly, resourceName, out byte[]? assemblyBytes)) - { -#pragma warning disable RS1035 // Do not use APIs banned for analyzers - loadedAssembly = TryGetResourceBytes(assembly, Path.ChangeExtension(resourceName, ".pdb"), out byte[]? symbolBytes) - ? Assembly.Load(assemblyBytes, symbolBytes) - : Assembly.Load(assemblyBytes); -#pragma warning restore RS1035 // Do not use APIs banned for analyzers - return true; - } - return false; - } - - /// - /// Attempts to read bytes from a resource and returns back if it's successful or not - /// - /// The assembly to pull the resource from - /// The name of the resource - /// The bytes[] if the resource could be found - /// True if the resource was found otherwise false - private static bool TryGetResourceBytes(Assembly assembly, string resourceName, out byte[]? bytes) - { - bytes = null; - ManifestResourceInfo resourceInfo = assembly.GetManifestResourceInfo(resourceName); - if (resourceInfo == null) - { - return false; - } - - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - { - bytes = new byte[stream.Length]; - _ = stream.Read(bytes, 0, bytes.Length); - } - - return true; - } - } + } """, Encoding.UTF8); } \ No newline at end of file diff --git a/src/SourceGenerator.Foundations/Templates/SourceGeneratorHoistBase.cs b/src/SourceGenerator.Foundations/Templates/SourceGeneratorHoistBase.cs index 9f33f03..edbec74 100644 --- a/src/SourceGenerator.Foundations/Templates/SourceGeneratorHoistBase.cs +++ b/src/SourceGenerator.Foundations/Templates/SourceGeneratorHoistBase.cs @@ -9,6 +9,7 @@ public static class SourceGeneratorHoistBase public static SourceText RenderTemplate(string @namespace) => SourceText.From($$""" #nullable enable using System; +using System.IO; using System.Linq; using System.Text; using System.Reflection; @@ -27,105 +28,148 @@ namespace {{@namespace}} /// internal abstract class SourceGeneratorHoist { - protected static readonly AppDomain s_currentDomain; + private static readonly List s_assembliesWithResources; + private static readonly Dictionary s_loadedAssemblies; - protected readonly ILogger m_logger; - // Needs to be an 'object' otherwise the assemblies will be attempted go be loaded before our assembly resolver - protected readonly object m_environment; + static SourceGeneratorHoist() + { + s_assembliesWithResources = new List(); + s_loadedAssemblies = new Dictionary(new AssemblyNameComparer()); + + // The assembly resolvers get added to multiple source generators + // so what we do here is only allow the first one defined to allow + // itself to be a resolver. Since this could lead to cases where two resolvers + // exists and provide two different instances of the same assembly. + const string RESOLVER_ATTACHED_KEY = "SGF_ASSEMBLY_RESOLVER_IS_ATTACHED"; + AppDomain currentDomain = AppDomain.CurrentDomain; + object? rawValue = currentDomain.GetData(RESOLVER_ATTACHED_KEY); + + if (rawValue == null || (rawValue is bool isAttached && !isAttached)) + { + currentDomain.SetData(RESOLVER_ATTACHED_KEY, true); + currentDomain.AssemblyLoad += OnAssemblyLoaded; + currentDomain.AssemblyResolve += ResolveMissingAssembly; + + foreach (Assembly assembly in currentDomain.GetAssemblies()) + { + AddAssembly(assembly); + } + } + } /// - /// Gets the name of the source generator for logging purposes + /// Raised whenever our app domain loads a new assembly /// - public string Name { get; } - - static SourceGeneratorHoist() + /// THe thing that raised the event + /// The parameters + private static void OnAssemblyLoaded(object sender, AssemblyLoadEventArgs args) { - AssemblyResolver.Initialize(); - s_currentDomain = AppDomain.CurrentDomain; + AddAssembly(args.LoadedAssembly); } - protected SourceGeneratorHoist(string name) + /// + /// Adds an assembly to the veriuos collections used to keep track of loaded items + /// + private static void AddAssembly(Assembly assembly) { - Name = name; - IGeneratorEnvironment environment = GetEnvironment(); - m_environment = environment; - s_currentDomain.ProcessExit += OnProcessExit; + AssemblyName assemblyName = assembly.GetName(); - m_logger = new Logger(Name); - if(environment != null) + if (s_loadedAssemblies.ContainsKey(assemblyName)) { - foreach(ILogSink logSink in environment.GetLogSinks()) - { - m_logger.AddSink(logSink); - } + return; } + s_loadedAssemblies.Add(assemblyName, assembly); + + if (assembly.IsDynamic) return; + + string[] resources = assembly.GetManifestResourceNames() + .Where(r => r.StartsWith("SGF.Assembly::")) + .ToArray(); + + if (resources.Length == 0) return; - if (Environment.UserInteractive) + foreach (string resource in resources) { - m_logger.AddSink(); + Console.WriteLine($"Extracting {resource} assembly from {assemblyName.Name}'s resources."); + if (TryExtractingAssembly(assembly, resource, out Assembly? loadedAssembly)) + { + AddAssembly(loadedAssembly!); + } } } - protected virtual void Dispose() - { - s_currentDomain.ProcessExit -= OnProcessExit; - } - /// - /// Raised when the process is closing, giving us a chance to cleanup any resources + /// Attempts to resolve any assembly by looking for dependencies that are embedded directly + /// in this dll. /// - private void OnProcessExit(object sender, EventArgs e) + private static Assembly? ResolveMissingAssembly(object sender, ResolveEventArgs args) { - try + AssemblyName assemblyName = new(args.Name); + + if (s_loadedAssemblies.TryGetValue(assemblyName, out Assembly assembly)) { - Dispose(); + return assembly; } - catch (Exception ex) + + foreach (Assembly loadedAssembly in s_assembliesWithResources) { - m_logger.Error(ex, $"Exception thrown while disposing '{Name}'"); + string resourceName = $"SGF.Assembly::{assemblyName.Name}.dll"; + if (TryExtractingAssembly(loadedAssembly, resourceName, out Assembly? extractedAssembly)) + { + AddAssembly(extractedAssembly!); + return extractedAssembly!; + }; } + + return null; } + /// - /// Gets an instance of a development platform to be used to log and debug info + /// Attempts to load an assembly that is contained within aonther assembly as a resource /// - /// - private static IGeneratorEnvironment GetEnvironment() + /// The assembly that should contain the resource + /// The expected name of the reosurce + /// The assembly if it was loaded + /// True if the assembly could be loaded otherwise false + private static bool TryExtractingAssembly(Assembly assembly, string resourceName, out Assembly? loadedAssembly) { - string? platformAssembly = null; - IGeneratorEnvironment? platform = null; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + loadedAssembly = null; + if (TryGetResourceBytes(assembly, resourceName, out byte[]? assemblyBytes)) { - // Windows Development Platform - platformAssembly = "SourceGenerator.Foundations.Windows"; +#pragma warning disable RS1035 // Do not use APIs banned for analyzers + loadedAssembly = TryGetResourceBytes(assembly, Path.ChangeExtension(resourceName, ".pdb"), out byte[]? symbolBytes) + ? Assembly.Load(assemblyBytes, symbolBytes) + : Assembly.Load(assemblyBytes); +#pragma warning restore RS1035 // Do not use APIs banned for analyzers + return true; } - else + return false; + } + + /// + /// Attempts to read bytes from a resource and returns back if it's successful or not + /// + /// The assembly to pull the resource from + /// The name of the resource + /// The bytes[] if the resource could be found + /// True if the resource was found otherwise false + private static bool TryGetResourceBytes(Assembly assembly, string resourceName, out byte[]? bytes) + { + bytes = null; + ManifestResourceInfo resourceInfo = assembly.GetManifestResourceInfo(resourceName); + if (resourceInfo == null) { - // Generic Development Platform - platformAssembly = "SourceGenerator.Foundations.Contracts"; + return false; } - if (!string.IsNullOrWhiteSpace(platformAssembly)) + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { - AssemblyName assemblyName = new(platformAssembly); - Assembly? assembly = null; - try - { - assembly = Assembly.Load(platformAssembly); - Type? platformType = assembly? - .GetTypes() - .Where(typeof(IGeneratorEnvironment).IsAssignableFrom) - .FirstOrDefault(); - if (platformType != null) - { - platform = Activator.CreateInstance(platformType) as IGeneratorEnvironment; - } - } - catch - { } + bytes = new byte[stream.Length]; + _ = stream.Read(bytes, 0, bytes.Length); } - return platform ?? new GenericDevelopmentEnvironment(); + return true; } } } diff --git a/src/SourceGenerator.Foundations/Templates/SourceGeneratorHostImpl.cs b/src/SourceGenerator.Foundations/Templates/SourceGeneratorHostImpl.cs index bd723d1..d889bdf 100644 --- a/src/SourceGenerator.Foundations/Templates/SourceGeneratorHostImpl.cs +++ b/src/SourceGenerator.Foundations/Templates/SourceGeneratorHostImpl.cs @@ -26,7 +26,7 @@ internal class {{dataModel.ClassName}}Hoist : SourceGeneratorHoist, IIncremental // Has to be untyped otherwise it will try to resolve at startup private object? m_generator; - public {{dataModel.ClassName}}Hoist() : base("{{dataModel.ClassName}}") + public {{dataModel.ClassName}}Hoist() : base() { m_generator = null; } @@ -37,11 +37,7 @@ internal class {{dataModel.ClassName}}Hoist : SourceGeneratorHoist, IIncremental public void Initialize(IncrementalGeneratorInitializationContext context) { // The expected arguments types for the generator being created - Type[] typeArguments = new Type[] - { - typeof(IGeneratorEnvironment), - typeof(ILogger), - }; + Type[] typeArguments = new Type[] { }; BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; Type generatorType = typeof(global::{{dataModel.QualifedName}}); @@ -49,34 +45,29 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if(constructor == null) { - string argumentString = string.Join(", ", typeArguments.Select(t => t.Name)); - m_logger.Error($"Unable to create instance of {generatorType.FullName} as no constructor could be found that takes {argumentString}. The generator will not be run"); return; } - object[] constructorArguments = new object[] - { - m_environment, - m_logger - }; + object[] constructorArguments = new object[]{}; IncrementalGenerator generator = (global::{{dataModel.QualifedName}})constructor.Invoke(constructorArguments); + ILogger logger = generator.Logger; + m_generator = generator; try { - SgfInitializationContext sgfContext = new(context, m_logger); + SgfInitializationContext sgfContext = new(context, logger); generator.OnInitialize(sgfContext); } catch (Exception exception) { - m_logger.Error(exception, $"Error! An unhandle exception was thrown while initializing the source generator '{Name}'."); + logger.Error(exception, $"Error! An unhandle exception was thrown while initializing the source generator '{{dataModel.QualifedName}}'."); } } - protected override void Dispose() + public void Dispose() { - base.Dispose(); if(m_generator is IDisposable disposable) { disposable.Dispose();