Skip to content

Generator Infrastructure

Utility types for common incremental source generator tasks: cacheable diagnostics, partial type nesting, and embedded attribute registration.

See also: Generators Overview | PipelineModel | EquatableArray | EmitWriter


DiagnosticInfo

A pipeline-safe representation of a Diagnostic that does not root any syntax trees or symbols. Safe to store in [PipelineModel] models.

Why

Roslyn's Diagnostic holds a Location, which holds a SyntaxTree reference. Storing diagnostics in pipeline models defeats incremental caching. DiagnosticInfo extracts only primitive data, then reconstructs the Diagnostic in the output step.

Factory Methods

Method Description
Create(DiagnosticDescriptor, Location, params string[]) Create from a descriptor and source location
Create(DiagnosticDescriptor, params string[]) Create with no location

Reconstruction

Method Description
ToDiagnostic() Reconstruct a Diagnostic for reporting

Example

// In the transform step — capture diagnostic data
var info = DiagnosticInfo.Create(
    MyDiagnostics.MissingAttribute,
    symbol.Locations[0],
    symbol.Name);

// In the output step — reconstruct and report
context.ReportDiagnostic(info.ToDiagnostic());

Properties

Property Type Description
Descriptor DiagnosticDescriptor The diagnostic descriptor
Location LocationInfo? The location info, or null
MessageArgs EquatableArray<string> Format arguments

LocationInfo

A pipeline-safe representation of a source code Location that does not root any syntax trees.

Factory Methods

Method Description
From(Location) Create from a Roslyn Location. Returns null for non-source locations.

Reconstruction

Method Description
ToLocation() Reconstruct a Roslyn Location

Properties

Property Type Description
FilePath string The source file path
TextSpan TextSpan The text span within the file
LineSpan LinePositionSpan The line/column span

PartialTypeHierarchy

Reconstructs the namespace and parent type nesting for partial type declarations. Use this when a source generator emits code that extends a user-declared partial type.

Handles:

  • File-scoped namespace declarations
  • Arbitrary nesting depth (class inside struct inside class, etc.)
  • Generic parent types with type parameters and constraints
  • All type kinds: class, struct, record, record struct, interface

Methods

Method Description
Write(EmitWriter, INamedTypeSymbol, Action<EmitWriter>) Write namespace + nesting, invoke callback for body content
Write(EmitWriter, INamedTypeSymbol, string) Write namespace + nesting around a pre-built string body

Example

using var writer = new EmitWriter();
writer.Comment("<auto-generated/>");
writer.NullableEnable();
writer.WriteLine();

PartialTypeHierarchy.Write(writer, targetTypeSymbol, inner =>
{
    inner.WriteLine("public string GeneratedProperty => \"hello\";");
});

context.AddSource($"{typeName}.g.cs", writer.ToString());

For a type MyApp.Models.Outer.Inner, this produces:

// <auto-generated/>
#nullable enable

namespace MyApp.Models;

partial class Outer
{
    partial class Inner
    {
        public string GeneratedProperty => "hello";
    }
}

EmbeddedAttribute

Helper for registering embedded marker attributes in incremental source generators. Embedded attributes are compiled into the consumer's assembly with [Conditional("DEEPSTAGING_GENERATOR")] to prevent runtime dependency on the generator package.

Methods

Method Description
Register(IncrementalGeneratorInitializationContext, EmbeddedAttributeOptions) Register an attribute via RegisterPostInitializationOutput
BuildSource(EmbeddedAttributeOptions) Build the attribute source text without registering (useful for testing)

EmbeddedAttributeOptions

Property Type Default Description
Namespace string required Namespace for the attribute
Name string required Attribute name (Attribute suffix added if missing)
Targets AttributeTargets required Valid attribute targets
AllowMultiple bool false Allow multiple applications to the same target
Properties IReadOnlyList<(string Type, string Name)>? null Optional auto-properties on the attribute

Example

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    EmbeddedAttribute.Register(context, new EmbeddedAttributeOptions
    {
        Namespace = "MyGenerator",
        Name = "AutoNotify",
        Targets = AttributeTargets.Field,
        Properties = [("string?", "PropertyName")]
    });

    // Generator pipeline uses the registered attribute...
}

This generates an internal sealed class AutoNotifyAttribute with [Conditional] and [AttributeUsage] decorations, plus a PropertyName auto-property.