Skip to content

Generated factories rely on the consumer's ImplicitUsings for System / System.Threading types #97

Description

@keithdv

Summary

Generated factory files reference four BCL types unqualified — Task, CancellationToken, IServiceProvider, InvalidOperationException — but emit no using System;, using System.Threading;, or using System.Threading.Tasks;. They resolve today only because the consuming project has ImplicitUsings enabled. A consumer that does not (an older-style project file, or one that sets <ImplicitUsings>disable</ImplicitUsings>) gets CS0246 inside *.g.cs files it cannot edit.

Mechanism

The factory header is fixed at FactoryGenerator.Types.cs:171-173, with two more usings appended for the class leg (ClassFactoryRenderer.cs:42,48):

using Neatoo.RemoteFactory;
using Neatoo.RemoteFactory.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection.Extensions;

No using System* is emitted anywhere in the generator (grep -rn '"using System' src/Generator returns nothing), while the renderers emit those four types by bare name:

Type Namespace Emitted at
IServiceProvider System ClassFactoryRenderer.cs:172,250,264
InvalidOperationException System ClassFactoryRenderer.cs:398 (the IsServerRuntime guard)
CancellationToken System.Threading ClassFactoryRenderer.cs:1241,1278,1285,1292 and every interface member
Task, Task.FromResult System.Threading.Tasks ClassFactoryRenderer.cs:598,602,1005,1009,1124,1220

Consumer types are not affected: generated code sits in the consumer's own namespace, so IOrder and friends resolve without a using.

Reproduction

Observed 2026-09-08 during EXRM-004, in the generator's own unit-test harness rather than a consumer build. DiagnosticTestHelper.RunGenerator (src/Tests/RemoteFactory.UnitTests/TestContainers/DiagnosticTestHelper.cs:71-88) builds a raw CSharpCompilation from the fixture source with no implicit usings — the same position as a consumer with the feature off. Adding an output-compilation assertion to two NF0105 tests surfaced it immediately:

TestNamespace.StaticFactoryTargetFactory.g.cs(24,26): error CS0246: The type or namespace name 'IServiceProvider' could not be found
TestNamespace.StaticFactoryTargetFactory.g.cs(43,64): error CS0246: The type or namespace name 'CancellationToken' could not be found
TestNamespace.StaticFactoryTargetFactory.g.cs(57,27): error CS0246: The type or namespace name 'InvalidOperationException' could not be found

The errors disappeared once the fixture source declared using System;, using System.Threading;, using System.Threading.Tasks; itself — which is why AssemblyAttributeEmissionTests already carries the note "The usings are required, not decorative" on its fixtures (AssemblyAttributeEmissionTests.cs:311-318).

Not observed in a real consumer build. Every project in this repository, and every .NET SDK template since .NET 6, enables ImplicitUsings, so the gap has never surfaced outside the test harness. That is what makes this latent rather than urgent — but it is a compile error in unfixable generated code when it does surface, and the diagnosis (CS0246 in a .g.cs file) points the user at the wrong place.

Suggested fix

Either would close it:

  1. Emit the three usings in the factory header alongside the existing five. Smallest change; consistent with how the header already works.
  2. Global-qualify the BCL tokens (global::System.Threading.Tasks.Task<…>, etc.). More verbose output, but it matches the convention the relay-handler leg already follows — FactoryGenerator.RelayHandler.cs qualifies every emitted type token, pinned by AssemblyAttributeEmissionTests.RelayHandler_EveryEmittedTypeToken_IsGlobalQualified (:993-1013), precisely so the generated body does not depend on the file's usings.

Option 2 is the stronger guarantee and the one the codebase has already reached for once; option 1 is the smaller diff. Either wants a regression test that compiles generated output from a fixture with no usings beyond Neatoo.RemoteFactory.

Context

Surfaced by EXRM-004 (PR #96) while hardening two NF0105 tests against silent generator failures; recorded there as a dismissed finding (pre-existing, serving none of that todo's criteria) and filed here instead.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions