Historical note: This page describes the earlier GraphQL/source-generation architecture. The current Foundgine direction is documented in Direction and Current Status. Historical implementation is under
archive/.
Home → Documentation → Source Generators → Diagnostics
| Id | Mirrors (old runtime behavior) | Severity |
|---|---|---|
| CBMAP001 | NodeBuilder “WARNING: … is type-incompatible with …” |
Warning |
| CBMAP002 | NodeBuilder “WARNING: … has no matching property…” |
Warning |
| CBMAP003 | NodeBuilder.BuildEntityChildren ambiguous-navigation exception |
Error |
| CBMAP004 | (new) navigation-shaped property with no resolvable FK by convention | Error |
| CBMAP005 | (new) unsupported BuildMap() statement shape |
Error |
CBMAP003 replaces a runtime InvalidOperationException for ambiguous navigations with a
build-time error — resolve it with a ModelToEntity alias entry, or the
[EntityForeignKey] escape hatch. See
Mapping Generator → Ambiguous navigation handling
for the full pattern.
The Diagnostics subsystem is responsible for identifying architectural, modeling, and configuration issues during compilation rather than execution. Instead of allowing invalid applications to fail at runtime, Foundgine reports deterministic compiler diagnostics with actionable guidance, enabling developers to correct problems before the application is ever executed.
Diagnostics are part of the framework.
They are not an afterthought.
Diagnostics follow one rule:
Every preventable runtime error should become a compile-time diagnostic.
Compilation is the best opportunity to improve developer experience.
Without diagnostics:
Compile
↓
Run
↓
Exception
↓
Debug
With diagnostics:
Compile
↓
Diagnostic
↓
Fix
↓
Run
Failures move left.
Source Code
↓
Parser
↓
Validation
↓
Diagnostics
↓
Generation
Invalid models never reach code generation.
The diagnostics subsystem is responsible for:
Diagnostics never modify generated output.
Every diagnostic follows the same lifecycle.
Source
↓
Validation
↓
Diagnostic
↓
IDE
↓
Developer
Generation continues whenever possible.
Diagnostics should be grouped by concern.
Examples:
Architecture
Metadata
Relationships
Planning
Providers
Generation
Performance
Each category should have a distinct identifier range.
Diagnostic identifiers should remain stable.
Example:
CB1000
Architecture
CB2000
Metadata
CB3000
Relationships
CB4000
Planning
CB5000
Providers
CB9000
Internal Generator
Stable identifiers improve documentation and troubleshooting.
Diagnostics should clearly communicate severity.
Info
↓
Warning
↓
Error
Errors prevent generation.
Warnings allow generation.
Errors indicate invalid applications.
Examples:
Applications should not compile with structural errors.
Warnings indicate questionable designs.
Examples:
Warnings educate developers.
Information diagnostics improve visibility.
Examples:
Informational diagnostics should never block compilation.
Diagnostics may originate from multiple stages.
Syntax
↓
Semantic
↓
Model
↓
Metadata
↓
Planning
Each stage validates only its own responsibilities.
Examples include:
Syntax diagnostics occur before semantic analysis.
Examples:
Semantic analysis resolves compiler symbols.
Model validation includes:
Internal models should always be valid after this stage.
Metadata validation includes:
Runtime assumes metadata correctness.
Planning validation includes:
Invalid plans should never be generated.
Providers may report compatibility issues.
Examples:
JSON not supported
Recursive CTE unavailable
Unsupported UPSERT strategy
Provider diagnostics should remain compile-time whenever possible.
Analyzers should remain independent from generation.
Recommended structure:
Syntax Analyzer
Semantic Analyzer
Architecture Analyzer
Performance Analyzer
Provider Analyzer
Each analyzer owns one responsibility.
Many diagnostics should provide automatic fixes.
Examples:
Missing Attribute
↓
Add Attribute
Duplicate Identifier
↓
Generate New Identifier
Code fixes significantly improve developer experience.
Messages should answer three questions:
Avoid vague diagnostics.
CB2004
Duplicate entity identifier.
The entity 'Customer' shares an identifier with
'Supplier'.
Assign unique identifiers or allow automatic
allocation.
The fix should be obvious.
Diagnostics should appear at the most relevant location.
Prefer:
Entity Declaration
Instead of:
Generated Code
Developers should never debug generated files.
Incremental generators should invalidate only affected diagnostics.
Changing:
Customer.cs
should not recompute diagnostics for unrelated entities.
Future analyzers may detect:
Performance guidance belongs in the IDE.
Architectural analyzers may validate:
This helps preserve long-term architecture.
Diagnostics should be snapshot tested.
Input
↓
Diagnostics
↓
Snapshot
Changes become immediately visible during review.
Every diagnostic should have documentation.
Example:
CB3007
Relationship Cycle
Description
Example
Resolution
Related Diagnostics
Documentation should remain versioned.
Diagnostics should integrate naturally with:
Developers should receive feedback while typing.
Analyzers should remain stateless.
All state should remain local to analysis.
Shared mutable state should be avoided.
Diagnostics exist only during compilation.
They contribute nothing to runtime size or execution cost.
Potential future analyzers include:
Each analyzer should remain modular.
Before adding a new diagnostic, ask:
If not, reconsider the design.
Diagnostics surround the entire compile-time pipeline.
Source Code
↓
Analysis
↓
Diagnostics
↓
Generation
↓
Runtime
They improve the framework without increasing runtime complexity.
The Diagnostics & Analyzer Architecture transforms structural, architectural, provider, and planning errors into clear compile-time diagnostics, allowing developers to correct issues before execution begins.
By combining incremental analyzers, deterministic validation, stable diagnostic identifiers, actionable messages, IDE integration, and optional code fixes, Foundgine delivers a modern developer experience while preserving a lightweight Runtime and strengthening the architectural integrity of the framework.
The generator’s own README additionally flags these concrete risk areas for the first real build against your mapping code:
MappingClassParser only understands the exact statement shapes used in the sample’s
ProductMapping.BuildMap(). Any other shape (loops, conditionals, helper method calls)
hits CBMAP005 and needs the parser extended.{ Enum.Value.ToString(), (int)Enum.Value } collection-initializer entries syntactically.EntityNavigationConvention’s principal-key convention ("{RelatedType.Name}Key") is
an assumption based on the sample mapping and may need adjusting for your schema.Generated output is deterministic — the same mapping input always produces the same generated source, which matters for incremental build performance and for reviewable diffs in generated code. See Pipeline Stages for how incremental generation scopes re-computation.
See Contributing → Testing for the layered testing strategy (parser tests, validation tests, identifier tests, snapshot tests) the generator is expected to carry.
| ← Previous: Mapping Generator | Next: Pipeline Stages → |