notes · · 3 min

Roslyn Under the Microscope: What 67 Remote AST Tools Found Inside the C# Compiler

We evaluated prod-code AST tools against dotnet/roslyn: 6,499,949 lines of C# across 18,176 files, 394 projects, 15,300+ compiler invariants, and cluster-verified OmniSharp refactoring.

On this page · 4 sections
  1. The 394-Project Compiler Hierarchy and Bootstrapping Loops
  2. Syntax Assertions Across 6.5M Lines: 1.8% Test Duplication
  3. 15,300+ Compiler Invariants: Structural Search and Codemod
  4. Remote C# Refactoring and OmniSharp Cluster Verification

Compilers written in their own source language represent the pinnacle of software engineering complexity. The .NET Compiler Platform (dotnet/roslyn) is the self-hosting C# and Visual Basic compiler engine that powers Visual Studio, the dotnet CLI, OmniSharp, and the C# Dev Kit language server. Unlike batch compilers of the past, Roslyn exposes its entire compiler pipeline as an open API: syntax trees, semantic binding models, symbol tables, control-flow graphs, incremental analyzers, and MSIL emission.

Roslyn is among the largest open-source managed codebases in existence, containing 6,499,949 lines of C# across 18,176 source files:

$ git ls-files '*.cs' | wc -l
18176
$ git ls-files | wc -l
35115
$ git ls-files '*.cs' | xargs wc -l | grep -v 'total$' | awk '{s+=$1} END {print s}'
6499949

Attempting to run full semantic indexing or interactive AST refactorings on a 6.5-million-line compiler codebase on a local development laptop causes severe memory starvation and CPU thermal throttling. We deployed prod-code’s suite of 67 remote code intelligence tools against Roslyn HEAD, delegating all AST parsing, dependency mapping, clone harvesting, and OmniSharp language server validation to dedicated remote cluster compute nodes.

The 394-Project Compiler Hierarchy and Bootstrapping Loops

Modern compiler platforms maintain strict layer separation between pure immutable data structures and downstream IDE hosting services. Roslyn organizes its codebase into 394 projects linked by MSBuild project references.

Executing prod-code dependencies across the entire solution mapped the complete inter-project dependency graph:

$ prod-code dependencies
Scope: modules | Nodes: 394 | Dependencies: 2,103

top coupled modules (by afferent coupling Ca):
  Name                                            Ca    Ce  Instability
  ─────────────────────────────────────────────────────────────────────
  Microsoft.CodeAnalysis.CSharp                  112    37    0.25
  Microsoft.CodeAnalysis                         105    36    0.26
  Microsoft.CodeAnalysis.CSharp.Workspaces        77    31    0.29
  Microsoft.CodeAnalysis.Test.Utilities           62    44    0.42
  Microsoft.CodeAnalysis.Workspaces               57    75    0.57
  Microsoft.CodeAnalysis.Compiler.Test.Resources  35     1    0.03
  Microsoft.CodeAnalysis.Features                 35    59    0.63
  Microsoft.CodeAnalysis.CSharp.Features          34    28    0.45
  Microsoft.CodeAnalysis.LanguageServer.Protocol  34    55    0.62
  Roslyn.Test.PdbUtilities                        34    14    0.29
  Microsoft.CodeAnalysis.Scripting                32    15    0.32

The coupling metrics clearly reflect Roslyn’s core architectural hierarchy:

  • Foundational Compiler Core: Microsoft.CodeAnalysis (Cₐ = 105, Cₑ = 36) and Microsoft.CodeAnalysis.CSharp (Cₐ = 112, Cₑ = 37) form the gravitational center of the compiler. Over 100 projects directly consume the core parser, binder, and emission symbols.
  • The Workspaces Abstraction Layer: Microsoft.CodeAnalysis.Workspaces (Cₐ = 57, Cₑ = 75) and Microsoft.CodeAnalysis.CSharp.Workspaces (Cₐ = 77, Cₑ = 31) introduce solution-level abstractions, text diffing, and formatting engines.
  • IDE Features and LSP: Microsoft.CodeAnalysis.Features and Microsoft.CodeAnalysis.LanguageServer.Protocol layer high-level refactoring and editor protocols on top of the immutable semantic models.

The dependency analysis discovered 100 circular paths. In Roslyn, these cycles are driven by self-hosting bootstrapping requirements: code generator tools (CSharpSyntaxGenerator) emit syntax factories consumed by analyzers (Microsoft.CodeAnalysis.Analyzers), whose unit tests link back against the compiler core.

Syntax Assertions Across 6.5M Lines: 1.8% Test Duplication

A compiler must maintain thousands of regression tests verifying that malformed or incomplete syntax inputs recover gracefully without panicking the parser.

Running prod-code duplicates analyzed 18,140 files containing 6,495,296 lines of code:

$ prod-code duplicates
Files Scanned: 18140 | Lines: 6495296 | Clone Groups: 20 | Duplication: 1.8%

Discovered Clone Groups:
  1. [Clone Group #258538] 6 lines | 1,500 occurrences (Type-2 Parameterized)
     • Occurrence 1: src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs:824-829
     • Occurrence 2: src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs:900-905
     ... (1,498 parser token assertion blocks across Syntax/Parsing test suites)

Across 6.5 million lines, Roslyn demonstrates an extraordinarily disciplined 1.8% code duplication ratio. The production compiler layers (src/Compilers/Core and src/Compilers/CSharp/Portable) are completely free of clone clusters. All major clone groups belong to parser unit tests:

  • Clone Group #258538 contains 1,500 occurrences of structured syntax token sequence assertions across IncrementalParsingTests.cs, StatementParsingTests.cs, and DeclarationParsingTests_MissingIdentifiers.cs.
  • These tests assert the exact recovery sequence of open and close delimiters (SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken) when syntax errors occur.

15,300+ Compiler Invariants: Structural Search and Codemod

Compilers enforce strict type and binding invariants at every node in the abstract syntax tree. In Roslyn, invariant checking is split between standard runtime assertions and a specialized framework abstraction (RoslynDebug.Assert) used across legacy multi-target builds.

We executed prod-code structural-search to catalog invariant checks across the repository:

$ prod-code structural-search "Debug.Assert(\$A)"
matched 14,101 instances across 2,049 files (68,787.39 ms)

$ prod-code structural-search "RoslynDebug.Assert(\$A)"
matched 1,208 instances across 230 files (19,343.53 ms)

Across 18,177 files, the remote AST search uncovered 15,309 invariant assertions protecting binder scopes, symbol resolution tables, and IL generator state.

To evaluate automated AST modernization, we executed prod-code codemod to evaluate converting the custom RoslynDebug.Assert helper to standard Debug.Assert:

$ prod-code codemod "RoslynDebug.Assert(\$A) ==>> Debug.Assert(\$A)"
2418 changed line(s) in 230 file(s)

--- a/src/Compilers/CSharp/Portable/Binder/Binder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs
@@ -30,6 +30,6 @@
         internal Binder(CSharpCompilation compilation)
         {
-            RoslynDebug.Assert(compilation != null);
-            RoslynDebug.Assert(this is BuckStopsHereBinder);
+            Debug.Assert(compilation != null);
+            Debug.Assert(this is BuckStopsHereBinder);
             this.Flags = compilation.Options.TopLevelBinderFlags;
             this.Compilation = compilation;

The codemod transformed 1,208 call sites across 230 files, changing 2,418 lines of code in memory with zero syntax errors, demonstrating scalable AST rewriting across a massive codebase.

Remote C# Refactoring and OmniSharp Cluster Verification

Automated refactoring inside a compiler requires validating type flow against complex Roslyn syntax trees. In src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs, helper methods check syntax node hierarchy:

public static bool IsAliasQualifier(SyntaxNode node)
{
    var p = node.Parent as AliasQualifiedNameSyntax;
    return p != null && p.Alias == node;
}

Using prod-code extract-function, we extracted the alias check predicate into a dedicated static helper MatchesAlias:

$ prod-code extract-function src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs 25 20 --to 25:48 --name MatchesAlias
`fn MatchesAlias` extracted (src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs);
the selection now reads `MatchesAlias(p, node)`
- no other place in the file has the selection's text

--- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
@@ -15,3 +15,8 @@
 namespace Microsoft.CodeAnalysis.CSharp
+private static void MatchesAlias(object p, the node)
+{
+    return p != null && p.Alias == node;
+}
+
 {
     public static partial class SyntaxFacts
@@ -24,3 +29,3 @@
             var p = node.Parent as AliasQualifiedNameSyntax;
-            return p != null && p.Alias == node;
+            return MatchesAlias(p, node);
         }

the analyzer accepts the result: 0 errors
nothing was written; pass `apply: true` to make this edit

The remote cluster OmniSharp instance received the virtual overlay, validated the extracted helper against Roslyn’s syntax types, and confirmed zero compiler diagnostics.

A self-hosting compiler that parses millions of lines of code is the ultimate test of code intelligence; offloading AST extraction, dependency analysis, and language server compilation to remote cluster nodes makes evaluating a 6.5-million-line compiler fast, responsive, and completely silent on the developer’s laptop.

Cite this article
Citation
Alexander Panasenko (2026-09-30). Roslyn Under the Microscope: What 67 Remote AST Tools Found Inside the C# Compiler. https://prod.codes/blog/roslyn-under-the-microscope-67-ast-tools/