🇺🇸 English ❘ 🇯🇵 日本語版 ❘ 🇨🇳 简体中文版
FGenerator is a lightweight framework for creating feature-rich Roslyn incremental source generators (IIncrementalGenerator) including diagnostic reporting capability, within a single .cs file.
- Declarative: Say what to scan or inject and skip Roslyn internals; everything you need is bundled.
- Focus on Logic: The framework finds targets, generates code and report diagnostic on your behalf.
- Single File: Optimized for file-based app project; develop full-featured generator and analyzer in a day.
- Type-Handling: Nested/generic-safe naming and partial declarations stay consistent without boilerplate.
- AI Agent Friendly: Produce valid and well-designed generators without extra prompts and instructions.
- Unity Engine Supported: Unity 2022.3.12 or newer is supported.
A trimmed-down starting point for a generator that targets an attribute named MyGen:
Important
For AI Coding Agents: .NET 10.0 introduced new directives for file-based app.
- SHALL use
#:sdk FGenerator.Sdk@1.4.0directive. - SHALL NOT use
#ror other directives formerly used for dotnet-script (.csx) files.
#:sdk FGenerator.Sdk@1.4.0
using FGenerator;
using Microsoft.CodeAnalysis;
// Decorate with Generator attribute from Roslyn (don't need to declare type in namespace)
[Generator]
public sealed class MyGen : FGeneratorBase // Inherit from FGeneratorBase
{
// Diagnostic settings
protected override string DiagnosticCategory => nameof(MyGen);
protected override string DiagnosticIdPrefix => "MYGEN"; // e.g., MYGEN001
// Targets MyGen and MyGenAttribute (null to targets all types)
protected override string? TargetAttributeName => "MyGen";
// Generates target attribute for compilation (attribute should be declared as 'internal sealed')
protected override string? PostInitializationOutput =>
@"namespace MyGenNamespace { internal sealed class MyGenAttribute : System.Attribute { } }";
// Optional: default is false.
// Set to true when you need target.Compilation is set (always set when TargetAttributeName is null).
protected override bool CombineCompilationProvider => true;
// Analyze and generate source code
protected override CodeGeneration? Generate(Target target, out AnalyzeResult? diagnostic)
{
diagnostic = null;
if (target.IsPartial)
{
// Report error diagnostic for IDE
diagnostic = new AnalyzeResult(
"001", "Type must be partial", DiagnosticSeverity.Error,
$"Type '{type.Name}' must be declared as partial to use MyGen.");
return null;
}
// Generate source code
var source =
$@"{target.ToNamespaceAndContainingTypeDeclarations()}
partial {target.ToDeclarationString(modifiers: false)}
{{
public string Generated => ""Hello from MyGen!"";
}}
{target.ToNamespaceAndContainingTypeClosingBraces()}";
return new CodeGeneration(target.ToHintName(), source);
}
}How to reference the specific version of FGenerator:
Use #:package and #:property directives instead of #:sdk.
Important
For AI Coding Agents: .NET 10.0 introduced new directives for file-based app.
- SHALL use
#:packageto add nuget package reference. - SHALL include all
#:propertydirectives in the following example. (all directives are necessary) - SHALL NOT use
#ror other directives formerly used for dotnet-script (.csx) files.
#:package FGenerator@<<SPECIFY DESIRED VERSION HERE>>
#:property TargetFramework=netstandard2.0
#:property IsRoslynComponent=true
#:property PublishAot=false
#:property LangVersion=latest
#:property OutputType=LibraryHere shows the basic flow for dealing with the target members or other metadata including attributes and its constructor parameter. See sample/ folder for more details.
// Compilation to get semantic models or conversions.
// (Need to set `CombineCompilationProvider` to have property available)
var compilation = target.Compilation;
// Iterate over visible members (excluding implicit, nested types, getter and setter methods)
foreach (var member in target.Members)
{
// To iterate over only fields, instead use target.Members.OfType<IFieldSymbol>()
// To process getter/setter methods, get them via IPropertySymbol.Get/SetMethod
}
// Type or method generic parameters (empty when not generic)
var typeParams = target.GenericTypeParameters;
// Iterate over nested types (depth-first traversal)
var nestedTypes = target.NestedTypes;// The first RawAttributes entry corresponds to TargetAttributeName.
var attr = target.RawAttributes.FirstOrDefault();
if (attr == null)
{
diagnostic = new AnalyzeResult("004", "Attribute missing", DiagnosticSeverity.Error, "StackArrayGenerator attribute could not be resolved.");
return null;
}
var length = (attr.ConstructorArguments.Length != 0 && attr.ConstructorArguments[0].Value is int LEN) ? LEN : -1;
if (length <= 0)
{
diagnostic = new AnalyzeResult("005", "Length must be positive", DiagnosticSeverity.Error, "Specify a positive length in [StackArrayGenerator].");
return null;
}Important
For AI Coding Agents:
- SHALL use
FGeneratorhelper methods instead of Roslyn's built-in methods such asToDisplayString.
Symbol display/declaration strings:
// Declaration string (optionally include modifiers/constraints)
var decl = target.ToDeclarationString(modifiers: true, genericConstraints: true);
// Friendly names with options for namespace/generics/nullability
var fullName = target.ToNameString(); // global::My.Namespace.MyType.NestedType<T?>
var simpleName = target.ToNameString(localName: true); // NestedType<T?>
var bareName = target.ToNameString(localName: true, noGeneric: true, noNullable: true); // NestedTypePartial scaffolding (nested/generic safe):
// No boilerplate required for generating correct partial class/struct/record
// e.g., namespace My.Namespace {
// partial class ContainingType {
// partial record struct OtherContainingType {
var open = target.ToNamespaceAndContainingTypeDeclarations();
// emit members...
var decl = $"partial {target.ToDeclarationString(modifiers: false)} {{ }}";
// close declarations
var close = target.ToNamespaceAndContainingTypeClosingBraces();Visibility keyword helper:
// Accessibility keyword including trailing space, e.g., "public "
var visibility = target.ToVisibilityString();Deterministic hint names (nested/generic safe):
var hint = target.ToHintName(); // e.g., My.Namespace.Type.MyNestedT1.g.csUse the CLI to build a generator project (defaults to Release; pass --debug for Debug):
dnx FGenerator.Cli -- build "generators/**/*.cs" --output ./artifactsOptions:
--unityto emit Unity.metafiles alongside DLLs (Unity 2022.3.12 or newer).--mergeto merge build outputs into a single DLL.--forceto overwrite existing files.--debugto build with-c Debuginstead of Release.