Purview.SourceGeneratorFramework 1.0.0-prerelease.25

This is a prerelease version of Purview.SourceGeneratorFramework.
dotnet add package Purview.SourceGeneratorFramework --version 1.0.0-prerelease.25
                    
NuGet\Install-Package Purview.SourceGeneratorFramework -Version 1.0.0-prerelease.25
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Purview.SourceGeneratorFramework" Version="1.0.0-prerelease.25" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Purview.SourceGeneratorFramework" Version="1.0.0-prerelease.25" />
                    
Directory.Packages.props
<PackageReference Include="Purview.SourceGeneratorFramework" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Purview.SourceGeneratorFramework --version 1.0.0-prerelease.25
                    
#r "nuget: Purview.SourceGeneratorFramework, 1.0.0-prerelease.25"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Purview.SourceGeneratorFramework@1.0.0-prerelease.25
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Purview.SourceGeneratorFramework&version=1.0.0-prerelease.25&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Purview.SourceGeneratorFramework&version=1.0.0-prerelease.25&prerelease
                    
Install as a Cake Tool

Purview.SourceGeneratorFramework

Referencing a generator project

Roslyn must receive both a source-generator assembly and its framework runtime dependency as analyzer inputs. Use an analyzer project reference:

<ProjectReference
	Include="..\MyGenerator\MyGenerator.csproj"
	PrivateAssets="all"
	OutputItemType="Analyzer"
	ReferenceOutputAssembly="false"
/>

The Purview SDK automatically invokes GetSourceGeneratorAnalyzerFiles, which returns both the generator and its framework dependency without adding either file to the consuming application's runtime references. Specifying Targets="GetSourceGeneratorAnalyzerFiles" explicitly remains supported but is not required.

Referencing a generator from its test project

A test project can need the source-generator project in two different roles at the same time:

  • as an analyzer, so the generator runs against the test project and its generated attributes and other types can be used directly by test source files; and
  • as a normal assembly reference, so the test code can name and instantiate the generator type through Purview.SourceGeneratorFramework.Testing.

Add two project references with deliberately different metadata:

<ItemGroup>
  
  <ProjectReference
    Include="..\MyGenerator\MyGenerator.csproj"
    PrivateAssets="all"
    OutputItemType="Analyzer"
    ReferenceOutputAssembly="false"
  />

  
  <ProjectReference
    Include="..\MyGenerator\MyGenerator.csproj"
    PrivateAssets="all"
    ReferenceOutputAssembly="true"
  />
</ItemGroup>

Do not put OutputItemType="Analyzer" on the normal reference. The Purview SDK automatically uses GetSourceGeneratorAnalyzerFiles for the analyzer reference and supplies the generator's runtime dependencies to Roslyn.

Because the second reference is a normal assembly reference, the generator's Roslyn dependencies also become visible to the test compilation. For a multi-target test project, build the generator against the oldest Roslyn version that supports its API usage and is compatible with the oldest test target. This framework supports Roslyn 4.13; prefer IncrementalGeneratorInitializationContext.RegisterEmbeddedAttribute(...) over Roslyn 4.14's IncrementalGeneratorPostInitializationContext.AddEmbeddedAttributeDefinition() when the tests must also target .NET 8. Do not centrally pin System.Collections.Immutable to a newer runtime version merely to make the generator load.

Core helpers, models, and MSBuild integration for writing incremental C# source generators with Roslyn.

Installation

dotnet add package Purview.SourceGeneratorFramework

What's included

  • CodeWriter — allocation-conscious helper for building generated C# source files with indentation, namespaces, type declarations, comments, and XML documentation.
  • IncrementalPipeline — extension methods for composing IncrementalValueProvider<T> and IncrementalValuesProvider<T> pipelines, including attribute-based discovery, generation context creation, and disable-property checks.
  • GenerationContext — a base execution-services context that carries the Roslyn Compilation, immutable generator settings, optional logging, and a factory for independently owned CodeWriter instances.
  • GeneratorResult<T> — a value-or-diagnostics result type for incremental source generator transforms.
  • TypeValueObject, TargetSymbolDescriptor, EquatableArray<T>, DiagnosticInfo — reusable models for generator inputs and outputs.
  • SymbolResolver, TypeHelpers, EmbeddedResources — helper classes for common symbol and resource tasks.
  • AttributeDataModelGenerator — bundled source generator that emits readonly record struct attribute parser models from [GenerateAttributeDataModel] declarations, eliminating repetitive FromAttributeData boilerplate. Supports manual mapping, auto-discovery, nested models, and inheritance matching.
  • SourceGeneratorFramework.Analyzers — Roslyn analyzers that enforce incremental source generator best practices (prefer ForAttributeWithMetadataName, use IIncrementalGenerator, avoid RegisterImplementationSourceOutput, etc.).
  • MSBuild .props / .targets — automatically adds global using directives for the main namespaces and supports packaging source generators that reference this framework.

Usage

Reference the package from a Roslyn source generator project:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsRoslynComponent>true</IsRoslynComponent>
    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Purview.SourceGeneratorFramework" />
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
  </ItemGroup>
</Project>

Implement IIncrementalGenerator and use the framework helpers to build a pipeline:

using Microsoft.CodeAnalysis;
using Purview.SourceGeneratorFramework.Helpers;
using Purview.SourceGeneratorFramework.Models;

[Generator]
public sealed class MyGenerator : IIncrementalGenerator
{
    static readonly TypeValueObject AttributeType = new("MyAttribute", "MyNamespace");

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var contextProvider = IncrementalPipeline.DefaultGenerationContextValueProvider(
            context,
            nameof(MyGenerator),
            "1.0.0"
        );

        var targets = IncrementalPipeline.ForAttributeWithMetadataName(
            context,
            AttributeType,
            static (ctx, ct) => ctx.TargetSymbol.Name
        );

        context.RegisterSourceOutput(
            targets.CombineWithContext(contextProvider),
            static (spc, pair) =>
            {
                var (name, generationContext) = pair;
                var writer = generationContext.CreateCodeWriter();
                writer.WriteAutoGeneratedHeader();
                writer.WriteFileScopedNamespace("MyNamespace");
                using (
					writer.WriteClassScope(
                        new TypeDeclarationOptions(name)
                        {
                            Accessibility = TypeDeclarationAccessibility.Public,
                            IsStatic = true,
                        }
                    )
                )
                {
                    writer.WriteLine("// generated content");
                }

                spc.AddSource($"{name}.g.cs", writer.ToString());
            }
        );
    }
}

See SourceGeneratorFramework.ExampleGenerator for a complete reference implementation.

Generated attributes and determinism

CodeWriter automatically stamps generated declarations with [GeneratedCode], [CompilerGenerated], and [ExcludeFromCodeCoverage] (where applicable) using the generator identity supplied to its constructor. The header written by WriteAutoGeneratedHeader() is deterministic and does not include a timestamp, so the same inputs always produce the same source.

Thin source-output registration

For per-target pipelines that return GeneratorResult<T>, use IncrementalPipeline.RegisterSourceOutput to combine targets with the generation context, report diagnostics, and run the generator callback only for successful results:

var targets = IncrementalPipeline.ForAttributeWithMetadataName(
    context,
    AttributeType,
    static (ctx, ct) =>
    {
        var symbol = ctx.TargetSymbol;
        return symbol is null
            ? GeneratorResult<string>.Empty
            : GeneratorResult<string>.Ok(symbol.Name);
    }
);

var contextProvider = IncrementalPipeline.DefaultGenerationContextValueProvider(
    context,
    nameof(MyGenerator),
    "1.0.0"
);

IncrementalPipeline.RegisterSourceOutput(
    context,
    targets,
    contextProvider,
    static (spc, name, generationContext) =>
    {
        var writer = generationContext.CreateCodeWriter();
        writer.WriteLine($"// generated {name}");
        spc.AddSource($"{name}.g.cs", writer.ToString());
    }
);

Keep CodeWriter out of incremental contexts

Treat GenerationContext values as cached incremental-pipeline state and each CodeWriter as mutable, output-scoped execution state. Create the writer inside the registered source-output callback, after the incremental cache boundary:

IncrementalPipeline.RegisterSourceOutput(
    context,
    targets,
    contextProvider,
    static (spc, target, generationContext) =>
    {
        var writer = generationContext.CreateCodeWriter();
        EmitTarget(generationContext, writer, target);
        spc.AddSource($"{target.Name}.g.cs", writer.ToString());
    }
);

This separation is intentional:

  • Roslyn caches the complete value published by an incremental provider. It does not provide a way to exclude one property of that value from caching.
  • CodeWriter is mutable. Caching one can retain previously written source when the context is reused for another output or generator run.
  • Source-output callbacks may process independent targets concurrently. Sharing a writer can mix their output and introduce data races.
  • A fresh writer gives each generated source independent scope tracking and deterministic ownership.

These rules also apply to custom contexts: never add or assign a CodeWriter property or field on a class derived from GenerationContext. A custom context is still produced by an incremental provider and cached as one complete value. Store only compilation-derived services and immutable configuration there, and call CreateCodeWriter() in the output callback.

When emitter methods need both logging/context services and writing, either pass the context and output-scoped writer separately, or compose them into a short-lived output wrapper created inside the callback. Such a wrapper must never be returned from an incremental provider:

public sealed class GenerationOutputContext<TContext> : ISourceGenLogger
    where TContext : GenerationContext
{
    public GenerationOutputContext(TContext generation)
    {
        Generation = generation;
        Writer = generation.CreateCodeWriter();
    }

    public TContext Generation { get; }
    public CodeWriter Writer { get; }

    public void Log(
        SourceGenLogLevel level,
        int indentation,
        string message,
        params object[] args) =>
        Generation.Log(level, indentation, message, args);
}

The wrapper reduces emitter parameter noise without extending the writer's lifetime into Roslyn's incremental cache.

Attribute model generation

The package includes AttributeDataModelGenerator, which generates readonly record struct parser models for .NET attributes. Instead of hand-writing FromAttributeData methods for every attribute you inspect, declare a readonly partial record struct with [GenerateAttributeDataModel] and let the generator fill in the Empty sentinel, FromAttributeData overloads, and property extraction logic.

using Microsoft.CodeAnalysis;
using Purview.SourceGeneratorFramework.Testing.Generators;
using System.ComponentModel.DataAnnotations;

namespace MySourceGenerator.Models;

[GenerateAttributeDataModel(typeof(ValidationAttribute), MatchByInheritance = true)]
public readonly partial record struct ValidationAttributeData(
    [AttributeProperty] string? ErrorMessage,
    [AttributeProperty] string? ErrorMessageResourceName,
    [AttributeProperty] ITypeSymbol? ErrorMessageResourceType
);

[GenerateAttributeDataModel(typeof(RequiredAttribute))]
public readonly partial record struct RequiredAttributeData(
    [AttributeProperty] bool AllowEmptyStrings,
    [AttributeProperty(Source = AttributePropertySource.NestedModel)] ValidationAttributeData ValidationAttribute
);

Supported mapping sources:

  • NamedArgument — reads a named attribute property
  • ConstructorIndex — reads a constructor argument by position
  • ConstructorName — reads a constructor argument by parameter name
  • NestedModel — populates a nested [GenerateAttributeDataModel] type

You can also target an attribute by fully-qualified name, which is useful when the attribute type is not available in the generator project (e.g., LengthAttribute in .NET 8+ or a self-generated attribute):

[GenerateAttributeDataModel("System.ComponentModel.DataAnnotations.RequiredAttribute")]
public readonly partial record struct RequiredAttributeData(
    [AttributeProperty] bool AllowEmptyStrings
);

Enable auto-discovery with [GenerateAttributeDataModel(typeof(MyAttribute), AutoDiscover = true)] to generate properties for every constructor parameter and public named property. Auto-discovery requires the Type overload. Override defaults with [AttributeProperty(DefaultValue = ...)] or rely on inferred defaults from optional constructor parameters.

See SourceGeneratorFramework.Testing.Generators for full documentation and additional examples.

Generic type identities and references

TypeIdentity distinguishes an open generic definition from a constructed generic type:

var openDictionary = new TypeIdentity(typeof(Dictionary<,>));

var stringToIntDictionary = openDictionary.MakeGeneric(
    new TypeIdentity(typeof(string)),
    new TypeIdentity(typeof(int))
);

TypeReference openReference = openDictionary.AsTypeReference();
TypeReference typedReference = stringToIntDictionary.AsTypeReference();

Use the open identity when any construction is acceptable. Its Matches(ITypeSymbol) method—and TypeHelpers.Is/IsDerivedFromExpectedBase—matches symbols such as Dictionary<string, int> and Dictionary<Guid, Widget> by generic definition and arity. Use the constructed identity when the arguments matter.

Structural equality remains exact: an open identity is not equal to a constructed identity, and Dictionary<string, int> is not equal to Dictionary<string, long>. Symbol matching is deliberately asymmetric: an open expected identity can match a constructed symbol.

String arguments to MakeGeneric are literal type names, not wildcards:

// Describes ResourceKitBase<TResource>, where the argument is literally named TResource.
var resourceKitBase = new TypeIdentity("ResourceKitBase", "Example");
var parameterized = resourceKitBase.MakeGeneric("TResource");

// Describes the open List<> definition and matches any List<T> construction.
var openList = new TypeIdentity(typeof(List<>));

For contract-aware comparisons, a constructed expected argument may be an interface or base type. TypeHelpers.Is and the symbol overload of IsDerivedFromExpectedBase accept an actual generic argument that implements or inherits from that expected contract. The syntax-only overload cannot inspect semantic relationships; it compares only the declared base type name.

TypeReference adds use-site composition—nullable annotations, arrays, pointers, generic parameters and nested constructions—around a TypeIdentity. It preserves the identity's generic matching behavior, but its modifiers must also match.

Structured member declarations

Methods, properties, fields, and constructors use immutable value-type declaration options. The descriptor itself does not allocate an object; strings and ImmutableArray values are references owned by the caller.

using (writer.WriteMethodScope(
    new MethodDeclarationOptions(
        "CreateAsync",
        new TypeReferenceOptions("Task").MakeGeneric(new TypeReferenceOptions("Result"))
    )
    {
        Accessibility = TypeDeclarationAccessibility.Public,
        IsStatic = true,
        IsAsync = true,
        Parameters =
        [
            new("request", new TypeReferenceOptions("Request")),
            new("cancellationToken", new TypeReferenceOptions("CancellationToken")),
        ],
    }))
{
    writer.WriteLine("return await ExecuteAsync(request, cancellationToken);");
}

writer.WriteProperty(
    new PropertyDeclarationOptions("Name", new TypeReferenceOptions("string"))
    {
        Accessibility = TypeDeclarationAccessibility.Public,
        HasSetter = true,
        IsInitOnly = true,
        Initializer = "string.Empty",
    }
);

writer.WriteField(
    new FieldDeclarationOptions("Instance", "Service")
    {
        Accessibility = TypeDeclarationAccessibility.Private,
        IsStatic = true,
        IsReadOnly = true,
        Initializer = "new()",
    }
);

WriteMethod folds long parameter lists automatically. WriteProperty supports automatic accessors, expression bodies, and callback-generated getter/setter bodies. Structured methods and constructors return a disposable body scope; callback overloads are available when a complete member should be written in one call.

TypeDeclarationOptions.Kind supports classes, structs, record classes, record structs, interfaces, enums, and delegates. The matching WriteInterface, WriteEnum, and WriteDelegate helpers set the kind automatically. Interface inheritance is supplied through Interfaces, enums can specify EnumUnderlyingType, and delegates use DelegateReturnType and DelegateParameters. Generic delegate and interface constraints use the existing GenericTypes model.

Attributes and parameters are structured as well; raw declaration fragments are not accepted:

new MethodDeclarationOptions("TryGet", "bool")
{
    Accessibility = TypeDeclarationAccessibility.Public,
    Attributes = [new("Obsolete")],
    ReturnAttributes = [new("NotNull")],
    Parameters =
    [
        new("value", "string?")
        {
            Modifier = ParameterModifier.Out,
            Attributes =
            [
                new("NotNullWhen")
                {
                    Arguments = [new("true")],
                },
            ],
        },
    ],
};

Every type, method, constructor, property, and field declaration exposes Attributes. Methods also expose ReturnAttributes; parameters expose their own Attributes. AttributeArgumentOptions supports positional arguments, constructor-named arguments using Name, and property assignments using Name with IsPropertyAssignment = true.

All declaration type positions use TypeReferenceOptions. Nullability is therefore composed rather than embedded in a type string:

var widget = new TypeReferenceOptions("Widget").Nullable();
var result = new TypeReferenceOptions("global::System.Collections.Generic.Dictionary")
    .MakeGeneric(new TypeReferenceOptions("string"), widget)
    .MakeArray()
    .Nullable();

new ParameterDeclarationOptions(
    "items",
    new TypeReferenceOptions("global::System.Collections.Generic.List")
        .MakeGeneric(widget)
)
{
    IsNullable = true,
    DefaultValue = "null",
};

For parameters, IsNullable = true is a convenience equivalent to calling .Nullable() on the parameter's TypeReferenceOptions. If both are used, only one nullable annotation is emitted.

TypeReferenceOptions supports nullable annotations, nested constructed generics, open generic arity, multidimensional and jagged arrays, pointers, and construction from Type, Roslyn ITypeSymbol, or TypeValueObject. Arbitrary expressions such as default values and initializers remain strings because they are expressions rather than type syntax.

Set TypeDeclarationOptions.IsAbstract for abstract classes or record classes. It takes precedence over the default IsSealed = true, so callers do not need to disable sealing explicitly. Abstract static classes and abstract non-class declarations are rejected.

Roslyn accessibility values can be converted in both directions:

TypeDeclarationAccessibility? declarationAccessibility =
    symbol.DeclaredAccessibility.ToTypeDeclarationAccessibility();

Accessibility roslynAccessibility =
    TypeDeclarationAccessibility.ProtectedInternal.ToRoslynAccessibility();

Both conversions are non-throwing. Roslyn NotApplicable maps to null; declaration File and unknown future values map to Roslyn NotApplicable, because Roslyn models file-local types separately from Accessibility.

Member spacing is tracked automatically at each declaration level:

  • Consecutive fields are grouped without a blank line.
  • A field followed by any other member has one blank line between them.
  • Methods, constructors, properties, and nested types are separated from the following member by one blank line.
  • An existing blank line is retained without adding another one.

Body-bearing declarations are registered when their returned scope is disposed. This means the next member is formatted correctly only after the preceding method, constructor, or type has been closed. If XML documentation or attributes were written after the previous member, the separator is inserted before that trivia so it remains attached to the declaration it documents.

Detecting undisposed CodeWriter scopes

CodeWriter can detect block or indentation scopes that have not been disposed before generated source is materialized. This validation is intended for development and automated tests and is disabled by default.

Enable it in the project consuming the source generator:

<PropertyGroup>
  <PurviewSourceGeneratorFrameworkValidateCodeWriterScopes>true</PurviewSourceGeneratorFrameworkValidateCodeWriterScopes>
</PropertyGroup>

The default generation-context provider reads the property automatically:

var contextProvider =
    IncrementalPipeline.DefaultGenerationContextValueProvider(
        context,
        nameof(MyGenerator),
        "1.0.0"
    );

Create a fresh writer through the generation context so it inherits the setting:

var writer = generationContext.CreateCodeWriter();

CreateCodeWriter() returns a new, independently owned instance on every call. The writer is not stored on GenerationContext; keep it scoped to the source-output operation that owns the generated source. CodeWriter.ThrowOnUnclosedScopes is read-only, and its configuration is supplied through the writer constructor by the context factory.

When validation is enabled, calling ToString() or implicitly converting a writer to Roslyn SourceText throws a CodeWriterScopeValidationException if OpenScopeCount is not zero. The dedicated exception allows generator error handlers to rethrow this framework invariant failure instead of reducing it to a generic generator diagnostic:

Cannot create generated source while 1 disposable scope(s) remain open. Dispose every scope before calling ToString().

Open scope #1: block — public sealed class Example
   at MyGenerator.Generate(...)

The exception's OpenScopes collection exposes the scope kind, block header, and opening stack trace programmatically. Stack traces are captured only when validation is enabled, avoiding this diagnostic allocation during normal generator execution.

Both BlockScope and IndentScope are tracked. Prefer using or callback-based blocks so scopes are always closed:

writer.WriteBlock(
    "if (value is null)",
    body => body.WriteLine("return;")
);

Custom generation contexts

Scope validation is applied to every context returned by GenerationContextValueProvider. Custom contexts do not need to accept or read the build property themselves:

public sealed class MyGenerationContext : GenerationContext
{
    public MyGenerationContext(
        Compilation compilation,
        GenerationSettings settings,
        ISourceGenLogger? logger)
        : base(compilation, settings, logger)
    {
    }
}

Do not add a CodeWriter to MyGenerationContext. Custom contexts have the same incremental-cache lifetime as the default context, so a writer stored on one can be reused across independent outputs.

Use the ordinary context-provider overload. The framework combines the compiler-visible property with the compilation and supplies the resulting immutable settings to the custom context factory:

var contextProvider = IncrementalPipeline.GenerationContextValueProvider(
    context,
    nameof(MyGenerator),
    "1.0.0",
    factory: static (compilation, settings, logger, cancellationToken) =>
    {
        cancellationToken.ThrowIfCancellationRequested();
        return new MyGenerationContext(compilation, settings, logger);
    },
    disablePropertyName: "MyGenerator_Disable"
);

The provider resolves scope validation, generator disabling, and test logging from analyzer-config properties before invoking the factory. The supplied logger is created internally only when logging is enabled and a sink is registered for that run.

Generators embedded in another package

If the generator assembly is embedded in a different NuGet package, the outer package must make the property compiler-visible to its consumers. Build assets from Purview.SourceGeneratorFramework are not automatically copied into the outer package.

Include this in a .props file imported by the outer package:

<Project>
  <PropertyGroup>
    <PurviewSourceGeneratorFrameworkValidateCodeWriterScopes
      Condition="'$(PurviewSourceGeneratorFrameworkValidateCodeWriterScopes)' == ''"
    >false</PurviewSourceGeneratorFrameworkValidateCodeWriterScopes>
  </PropertyGroup>

  <ItemGroup>
    <CompilerVisibleProperty Include="PurviewSourceGeneratorFrameworkValidateCodeWriterScopes">
      <Description>Throws when generated source is materialized while CodeWriter scopes remain undisposed.</Description>
    </CompilerVisibleProperty>
  </ItemGroup>
</Project>

Pack that file using the outer package's ID so NuGet imports it automatically:

<None
  Include="Sdk\Sdk.props"
  Pack="true"
  PackagePath="buildTransitive\$(PackageId).props"
  Visible="false"
/>

Framework-based generator tests enable scope validation by default. Disable it for a test only when partial source materialization is intentional:

new SourceGeneratorTestOptions
{
    ValidateCodeWriterScopes = false,
};

Disabling a generator at build time

Pass the generator's compiler-visible disable property to the context provider. Its resolved value is included in GenerationSettings automatically:

<PropertyGroup>
  <MyGenerator_Disable>true</MyGenerator_Disable>
</PropertyGroup>
var contextProvider = IncrementalPipeline.DefaultGenerationContextValueProvider(
    context,
    nameof(MyGenerator),
    "1.0.0",
    disablePropertyName: "MyGenerator_Disable"
);

// In the output stage:
if (generationContext.Settings.IsSourceGeneratorDisabled)
    return;

IsDisabledValueProvider remains available when expensive upstream transforms must be filtered before they are combined with the generation context.

Test logging

Framework logging is disabled in ordinary compiler runs. The testing integration enables it by registering an isolated sink and supplying a per-run session ID through analyzer config. Context providers create the internal logger automatically; generators do not implement a logging interface and no logging-support source is generated.

The sink registry stores callbacks only. It never buffers log entries. If logging is disabled, the session ID is missing, or no matching sink is registered, the provider supplies no logger and log calls are discarded without storing entries. Test sinks own any entries they choose to capture and are removed when the test run completes.

Analyzers

The SourceGeneratorFramework.Analyzers package ships Roslyn diagnostics that help generator authors follow the incremental-source-generator rules that the framework itself observes:

Rule Summary
PSGFR11 Prefer SyntaxProvider.ForAttributeWithMetadataName over CreateSyntaxProvider for attribute-based detection.
PSGFR12 Use IIncrementalGenerator / RegisterSourceOutput instead of ISourceGenerator.
PSGFR14 Avoid RegisterImplementationSourceOutput unless implementation-only output is required.

Add a reference to the analyzers assembly in your generator project to enable these diagnostics at build time.

License

This project is licensed under the MIT license.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Purview.SourceGeneratorFramework:

Package Downloads
Purview.SourceGeneratorFramework.Testing

Purview SourceGeneratorFramework libraries for building and testing incremental C# source generators.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-prerelease.25 40 8/27/2026
1.0.0-prerelease.24 64 8/19/2026
1.0.0-prerelease.23 64 8/19/2026
1.0.0-prerelease.22 67 8/19/2026
1.0.0-prerelease.21 65 8/18/2026
1.0.0-prerelease.20 70 8/18/2026
1.0.0-prerelease.19 71 8/17/2026
1.0.0-prerelease.18 72 8/16/2026
1.0.0-prerelease.17 64 8/14/2026
1.0.0-prerelease.16 64 8/13/2026
1.0.0-prerelease.15 65 8/13/2026
1.0.0-prerelease.14 60 8/13/2026
1.0.0-prerelease.13 66 8/13/2026
1.0.0-prerelease.12 68 8/12/2026
1.0.0-prerelease.11 65 8/12/2026
1.0.0-prerelease.10 60 8/12/2026
1.0.0-prerelease.9 75 8/11/2026
1.0.0-prerelease.7 68 8/9/2026
1.0.0-prerelease.6 65 8/8/2026
1.0.0-prerelease.5 62 8/7/2026
Loading failed