Skip to content

Code written by WriteSourceFile has nullable disabled: every string? it emits raises CS8669, and non-annotated members are null-oblivious to consumers #55

Closed

Description

@matt-edmondson

What's wrong

GeneratorBase.WriteFileHeader (SourceGeneratorToolkit/GeneratorBase.cs, around lines 133–147) writes the copyright lines, then // <auto-generated />, then a blank line. WriteSourceFile (around lines 158–164) then calls codeBlocker.AddSourceFile(...) immediately.

Nothing on this path writes a #nullable directive. CodeBlocker.AddSourceFile emits the namespace, usings and types, but not #nullable enable; CodeBlocker has a separate WriteNullableEnable() for that.

Roslyn treats any file carrying an <auto-generated marker as generated code. Generated code runs with the nullable context disabled unless the file opts in with a directive, whatever <Nullable>enable</Nullable> the consuming project sets. And because WriteSourceFile writes the header and the body in one call, a caller can't put the directive between them.

Failure scenario

A generator built on the toolkit writes a class through WriteSourceFile with public string? Description; and public string Name = "";. The output is:

// Copyright (c) 2023-2026 ktsu-dev contributors
// <auto-generated />

namespace Generated;

public class Thing
{
	public string? Description;
	public string Name = "";
}

That output is compiled with NullableContextOptions.Enable alongside a user file containing t.Name = null; int n = t.Description.Length;. The compiler reports:

Thing.g.cs(8,15): warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
User.cs(1,75): warning CS8602: Dereference of a possibly null reference.

t.Name = null produces no CS8625, because Name is nullable-oblivious.

Consequences:

Suggested fix

Have WriteFileHeader write #nullable enable after the // <auto-generated /> marker, so WriteSourceFile inherits it. Alternatively, add a parameter or overload that opts in. Enabled-by-default suits the ktsu convention of nullable-enabled projects.

Acceptance criteria

  • The output of WriteSourceFile and WriteFileHeader contains #nullable enable after the generated-file marker, or does so when the new option is chosen.
  • A test compiles WriteSourceFile output that contains a string? member under nullable enable and asserts there is no CS8669.
  • A test asserts that assigning null to a non-annotated generated string member raises CS8625.

Activity

  1. matt-edmondson commented on Oct 29, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

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