diff --git a/docs/design/api-mark-dot-net/dot-net-emitter.md b/docs/design/api-mark-dot-net/dot-net-emitter.md
index 2cbc0cc..97ada06 100644
--- a/docs/design/api-mark-dot-net/dot-net-emitter.md
+++ b/docs/design/api-mark-dot-net/dot-net-emitter.md
@@ -95,6 +95,11 @@ These helpers are grouped by concern:
`IsMemberPublic`, `IsMemberPublicOrProtected`, `IsPropertyPublicOrProtected`,
`GetVisibleMembers`, `ShouldIncludeMember`: determine which types and members are
included based on the configured visibility level and `IncludeObsolete` flag.
+ `GetVisibleNestedTypes` always excludes compiler-generated nested types (the
+ cached-lambda class, closures/display classes, async/iterator state machines,
+ etc.), even at `ApiVisibility.All`: they carry no source-level documentation,
+ and several of their names contain characters (`<`, `>`, `|`) that are invalid
+ in Windows file paths, which would otherwise break output generation.
- *Type/member classification* — `IsOperator`, `IsSpecialNameNonConstructor`,
`IsCompilerGeneratedField`, `IsDelegate`, `IsExtensionMethod`,
`IsCompilerGenerated(ICustomAttributeProvider)`, `IsCompilerGenerated(TypeDefinition)`,
diff --git a/src/ApiMark.DotNet/DotNetEmitter.cs b/src/ApiMark.DotNet/DotNetEmitter.cs
index e2759df..0bff99c 100644
--- a/src/ApiMark.DotNet/DotNetEmitter.cs
+++ b/src/ApiMark.DotNet/DotNetEmitter.cs
@@ -218,7 +218,11 @@ internal bool IsTypeVisible(TypeDefinition type)
/// Nested-type visibility is tested with the IsNested* flags rather than the
/// top-level IsPublic flag because Cecil assigns separate flags to each
/// nested-access level. Ordering by name ensures deterministic output regardless of
- /// metadata table order.
+ /// metadata table order. Compiler-generated nested types (closures, state machines,
+ /// the cached-lambda class, etc.) are always excluded, even at :
+ /// they are implementation details with no source-level documentation, and several of
+ /// their names contain characters (<, >, |) that are invalid in
+ /// Windows file paths.
///
/// The declaring type whose nested types are to be filtered.
///
@@ -228,6 +232,7 @@ internal bool IsTypeVisible(TypeDefinition type)
internal IEnumerable GetVisibleNestedTypes(TypeDefinition type)
{
return type.NestedTypes
+ .Where(t => !IsCompilerGenerated(t))
.Where(t => Model.Options.Visibility switch
{
ApiVisibility.Public => t.IsNestedPublic,
diff --git a/test/ApiMark.DotNet.Fixtures/CompilerGeneratedNestedClass.cs b/test/ApiMark.DotNet.Fixtures/CompilerGeneratedNestedClass.cs
new file mode 100644
index 0000000..de46ee4
--- /dev/null
+++ b/test/ApiMark.DotNet.Fixtures/CompilerGeneratedNestedClass.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DemaConsulting LLC. All rights reserved.
+// Licensed under the MIT License.
+
+namespace ApiMark.DotNet.Fixtures;
+
+///
+/// A class whose members trigger compiler-generated nested types (a cached-lambda class and
+/// a closure/display class), used to verify ApiMark excludes them at every visibility.
+///
+public class CompilerGeneratedNestedClass
+{
+ /// Doubles every value using a lambda that captures nothing, synthesizing a cached-lambda class.
+ /// The values to double.
+ /// An array containing each value doubled.
+ public int[] DoubleAll(int[] values) => Array.ConvertAll(values, v => v * 2);
+
+ /// Adds to every value using a lambda that captures it, synthesizing a closure class.
+ /// The values to offset.
+ /// The amount to add to each value.
+ /// An array containing each value plus .
+ public int[] AddOffsetToAll(int[] values, int offset) => Array.ConvertAll(values, v => v + offset);
+}
diff --git a/test/ApiMark.DotNet.Tests/DotNetEmitterTests.cs b/test/ApiMark.DotNet.Tests/DotNetEmitterTests.cs
index 9d27ca0..0f8ba72 100644
--- a/test/ApiMark.DotNet.Tests/DotNetEmitterTests.cs
+++ b/test/ApiMark.DotNet.Tests/DotNetEmitterTests.cs
@@ -474,4 +474,31 @@ public void DotNetEmitter_BuildPropertyAccessors_AsymmetricGetSet_UsesMostPermis
// Assert: getter must be prefixed with its restricted accessibility; setter must have no prefix
Assert.Equal("protected get; set;", result);
}
+
+ ///
+ /// Validates that excludes compiler-generated
+ /// nested types (cached-lambda classes, closures, etc.) even at ,
+ /// where they would otherwise be included because they are private/internal and carry no
+ /// meaningful documentation, and their names (e.g. <>c) are invalid Windows file names.
+ ///
+ [Fact]
+ public void DotNetEmitter_GetVisibleNestedTypes_AllVisibility_ExcludesCompilerGeneratedTypes()
+ {
+ // Arrange
+ var options = BuildOptions();
+ options.Visibility = ApiVisibility.All;
+ var emitter = (DotNetEmitter)new DotNetGenerator(options).Parse(new InMemoryContext());
+ using var assembly = AssemblyDefinition.ReadAssembly(FixturePaths.GetFixtureDll());
+ var type = assembly.MainModule.Types.Single(t => t.Name == "CompilerGeneratedNestedClass");
+
+ // Sanity check: the compiler did synthesize at least one nested type for the lambdas
+ Assert.NotEmpty(type.NestedTypes);
+
+ // Act
+ var visibleNestedTypes = emitter.GetVisibleNestedTypes(type).ToList();
+
+ // Assert: none of the synthesized nested types (whose names start with '<') are visible
+ Assert.Empty(visibleNestedTypes);
+ Assert.DoesNotContain(visibleNestedTypes, t => t.Name.Contains('<', StringComparison.Ordinal));
+ }
}