Skip to content

chiouyazo/CodeStripper

Repository files navigation

CodeStripper

Mirrors a C# source tree 1:1 but empties every method/property/accessor body, keeping signatures, type layout, doc comments, constants and enum values.

Use case: hand an AI (or a person) the stripped source so they write integration tests against the public contract without reading the implementation. Tests still compile and run against the real build - the stripped tree is only context.

public int Add(int a, int b)          // in
{
    _log.Info("adding");
    return a + b;
}

public int Add(int a, int b) => throw null;   // out

It is a Roslyn CSharpSyntaxRewriter plus throw null - the same technique Microsoft.DotNet.GenAPI uses, but source to source: exact file/type structure, private members kept if you want, and optional statement-level redaction. Reference-assembly tools (GenAPI, <ProduceReferenceAssembly>, Refasmer) only give you a DLL or a reconstructed API surface.

Run

dotnet build -c Release
dotnet run --project src/CodeStripper -- --input <src-dir> --output <out-dir> [--config <file>] [--verbose]

Output must be a different directory from input; stripping in place is refused.

Example configs (examples/)

  • strip-for-ai.config.json - the anti-cheating case. Every body emptied (incl. private and internal), field initializers stripped so they cannot leak logic.
  • buildable-mirror.config.json - same, plus copies csproj/props/assets and drops a build override, so the stripped tree still compiles with a plain dotnet build (see below).
  • redact-noise.config.json - the opposite: keep the real logic, but remove logging calls and unwrap try/catch for clean review.

Configuration

Key Values Default Meaning
bodyReplacement throwNull | notImplemented | emptyForVoid throwNull What an emptied body becomes. notImplemented avoids the CS8597 warning.
computedProperties strip | keepTrivial | keepAll strip Expression-bodied properties/indexers (=> expr). keepTrivial keeps ones with no call/new/await (e.g. Id > 0).
publicMembers / protectedMembers / internalMembers / privateMembers keepFull | emptyBody | removeEntirely emptyBody Per-accessibility handling. removeEntirely drops the member.
keepXmlDocComments bool true Keep /// docs.
keepAttributes bool true Keep attributes.
keepConstants bool true Keep const values even when initializers are stripped.
keepEnumMembers bool true Keep enum member values.
stripFieldInitializers bool false Remove = expr on fields/properties (they can leak logic).
statementRules list [] Only affect keepFull bodies (see below).
include / exclude glob list **/*.cs / build+generated File selection, relative to input root.
stripBuildTooling bool false Strip generator/analyzer projects too. Off = mirror them verbatim (see below).
mirrorNonSourceFiles bool false Copy every non-.cs file verbatim, for a complete tree.
emitBuildOverride bool false Write a Directory.Build.targets that relaxes the strict build.
runCSharpier bool false Format output with CSharpier (dotnet tool install -g csharpier).

Statement rules

type Options Effect
removeLoggingCalls matchMembers (receiver names) Removes _log.Info(...), Log.Warning(...), await _log.XAsync(...).
unwrapTryCatch - Replaces try { X } catch/finally { ... } with { X } (drops finally).

Rules run before body-stripping, so they only mark bodies that survive (keepFull). Add one by implementing IStatementRule and registering it in StatementRuleFactory.

Source generators & analyzers

These are build tooling: emptying them stops them emitting code, breaking every consumer. CodeStripper detects them and mirrors them verbatim - no config needed. A project counts as tooling when its csproj sets IsRoslynComponent / EnforceExtendedAnalyzerRules, when it is referenced with OutputItemType="Analyzer", or when a file matches IIncrementalGenerator / [Generator] / DiagnosticAnalyzer. Set stripBuildTooling: true to strip them anyway.

Making the stripped tree build

A stripped tree inherently has unused parameters, unused members and throw null, which TreatWarningsAsErrors + analyzers turn into errors (a ~116-file solution produced 278 such errors). That is what stripping is, not a bug. emitBuildOverride writes a Directory.Build.targets at the output root (TreatWarningsAsErrors=false, RunAnalyzers=false, Nullable=disable, plus NoWarn); because targets are imported after every Directory.Build.props, its values win. With buildable-mirror.config.json the mirror compiles under a plain dotnet build. Validated on five in-house solutions (865 .cs files, one with a source generator).

Limitations

  • Types are always kept; removeEntirely applies to members, not whole types.
  • Accessibility is resolved syntactically (modifiers + containing declaration) - no compilation needed.
  • Output is valid C# but is meant as reference, not a build target; build and test against the original.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages