Move MethodBuilder and RecordingGeneratorsFactory to Generators#45
Conversation
…o Generators Co-authored-by: dex3r <3155725+dex3r@users.noreply.github.com>
Why do they need to be added as resources? Can't they just be compiled into Generators and thats it? @copilot |
They need to be injected as source into the ephemeral execution compilation rather than just compiled into Generators as-is, because of a .NET
Injecting the source code of |
There was a problem hiding this comment.
Pull request overview
This PR relocates concrete “recording/execution” helper implementations out of Abstractions into the Generators project, and updates the execution-compilation path to inject these helpers as embedded source so they load in the correct AssemblyLoadContext when running as an analyzer.
Changes:
- Moved
MethodBuilderandRecordingGeneratorsFactoryintoMattSourceGenHelpers.Generatorsand added explicitusingdirectives for source-injection scenarios. - Embedded both
.csfiles as resources inMattSourceGenHelpers.Generators.csprojand injected them as extra syntax trees into the ephemeral execution compilation. - Updated runtime type resolution and constants to locate
RecordingGeneratorsFactoryin the compiled execution assembly / Generators namespace.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| MattSourceGenHelpers.Generators/RecordingGeneratorsFactory.cs | Moves recording types into Generators namespace and adds explicit usings for injection compilation. |
| MattSourceGenHelpers.Generators/MethodBuilder.cs | Moves MethodBuilder into Generators namespace and adds explicit using for injected compilation. |
| MattSourceGenHelpers.Generators/MattSourceGenHelpers.Generators.csproj | Embeds MethodBuilder.cs and RecordingGeneratorsFactory.cs as manifest resources. |
| MattSourceGenHelpers.Generators/GeneratesMethodExecutionRuntime.cs | Injects embedded helper sources into execution compilation and resolves factory type from execution assembly. |
| MattSourceGenHelpers.Generators/Consts.cs | Adds Generators namespace/assembly constants and updates recording factory full name. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Type? generatorStaticType = abstractionsAssembly.GetType(Consts.GenerateTypeFullName); | ||
| Type? recordingFactoryType = abstractionsAssembly.GetType(Consts.RecordingGeneratorsFactoryTypeFullName); | ||
| Type? recordingFactoryType = assembly.GetType(Consts.RecordingGeneratorsFactoryTypeFullName); | ||
| if (generatorStaticType == null || recordingFactoryType == null) | ||
| { | ||
| return (null, $"Could not find {Consts.GenerateTypeFullName} or {Consts.RecordingGeneratorsFactoryTypeFullName} types in Abstractions assembly"); | ||
| return (null, $"Could not find {Consts.GenerateTypeFullName} or {Consts.RecordingGeneratorsFactoryTypeFullName} types in compiled assembly"); | ||
| } |
There was a problem hiding this comment.
The failure message here is misleading: GenerateTypeFullName is looked up from abstractionsAssembly, while RecordingGeneratorsFactoryTypeFullName is looked up from the compiled execution assembly. Consider splitting the null checks and reporting which type was missing from which assembly (or include both assembly names) to make troubleshooting accurate.
| string dummySource = BuildDummyImplementation(allPartialMethods); | ||
| string methodBuilderSource = ReadEmbeddedResource($"{Consts.GeneratorsAssemblyName}.MethodBuilder.cs"); | ||
| string recordingFactorySource = ReadEmbeddedResource($"{Consts.GeneratorsAssemblyName}.RecordingGeneratorsFactory.cs"); |
There was a problem hiding this comment.
BuildExecutionCompilation reads the embedded .cs resources on every call, which can add noticeable overhead in analyzer execution (multiple invocations per compilation). Consider caching the resource text (or even the parsed SyntaxTrees) in static fields so this work is done once per analyzer load.
| { | ||
| using Stream? stream = typeof(GeneratesMethodExecutionRuntime).Assembly.GetManifestResourceStream(resourceName); | ||
| if (stream == null) | ||
| throw new InvalidOperationException($"Embedded resource '{resourceName}' not found in {Consts.GeneratorsAssemblyName} assembly"); |
There was a problem hiding this comment.
Throwing InvalidOperationException when the embedded resource is missing will crash the analyzer/generator (often surfacing as a hard failure in the IDE/build). Prefer returning a structured error (consistent with the other Execute* methods) and/or reporting a diagnostic so the generator fails gracefully instead of throwing.
| throw new InvalidOperationException($"Embedded resource '{resourceName}' not found in {Consts.GeneratorsAssemblyName} assembly"); | |
| { | |
| // Fail gracefully when the embedded resource cannot be found instead of crashing the generator. | |
| return string.Empty; | |
| } |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Abstractionscontained concrete implementation classes (MethodBuilder,RecordingGeneratorsFactory,SwitchBodyRecord, and relatedRecording*types) that belong inGenerators.Changes
MethodBuilder.csandRecordingGeneratorsFactory.csfromAbstractions→Generators, namespace updated toMattSourceGenHelpers.Generatorsusingdirectives added to both files — required because they are injected as raw source text into execution compilations (which don't inherit the project's implicit usings)Generators.csprojso they can be read and injected at runtimeBuildExecutionCompilationnow injectsMethodBuilder.csandRecordingGeneratorsFactory.csas extra syntax trees into the ephemeral execution compilation, making the recording types available in the correctAssemblyLoadContextwithout relying onAssembly.Location(which is empty when loaded as a Roslyn analyzer)ExecuteFluentGeneratorMethodnow resolvesRecordingGeneratorsFactoryfrom the execution assembly instead of from the Abstractions assemblyConstsgainsGeneratorsNamespace/GeneratorsAssemblyNameconstants;RecordingGeneratorsFactoryTypeFullNameupdated accordinglyOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.