Leash 0.0.2

dotnet add package Leash --version 0.0.2
                    
NuGet\Install-Package Leash -Version 0.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Leash" Version="0.0.2">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Leash" Version="0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Leash">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Leash --version 0.0.2
                    
#r "nuget: Leash, 0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Leash@0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Leash&version=0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Leash&version=0.0.2
                    
Install as a Cake Tool

Leash

Leash is a syntax-only Roslyn analyzer that limits the size and parameter count of C# implementations. SPEC.md defines the rules.

LEASH0001: 'Foo' is 58 logical lines long (limit 50). Statements: 42, elements: 573.

Size is measured in logical lines: max(statements, ceiling(elements / 10)).

  • Elements are syntax tokens: identifiers, keywords, operators, punctuation, and literals.
  • Statements is a weighted count of declarations, assignments, increments, and control structures, not a raw count of C# statement nodes.

Comments and formatting do not count. A value equal to its limit is allowed.

Build and test

Requires .NET SDK 10.0.100 or later in the 10.0 family. global.json selects the Microsoft.Testing.Platform test runner.

dotnet restore Leash.slnx
dotnet build Leash.slnx -c Release --no-restore
dotnet test --solution Leash.slnx -c Release --no-build
dotnet pack Leash/Leash.csproj -c Release --no-build -o artifacts/packages
Project Purpose
Leash Roslyn analyzer and exemption-attribute generator; netstandard2.0, Roslyn 4.14
xUnitTest Tests for counting, diagnostics, configuration, exemptions, the generator, concurrency, and hosts; .NET 10
Benchmark BenchmarkDotNet timing and memory measurements; .NET 10

Compatibility:

  • The analyzer host must be Roslyn 4.14 or later.
  • The analyzer is written in C# 13 and targets .NET Standard 2.0.
  • The supported syntax baseline is C# 13, and tests use that language version.
  • Only the .NET SDK command-line host is verified. Test an IDE before claiming support for it.

Diagnostic help links use the base URL in DiagnosticRules.cs. Before distributing the package, publish the docs directory at that URL or change the base. The package also includes the help pages.

Use in another project

Reference the analyzer project directly:

<ItemGroup>
  <ProjectReference Include="../Leash/Leash.csproj"
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />
</ItemGroup>

Or install the locally packed NuGet package with PrivateAssets="all". The package places the analyzer under analyzers/dotnet/cs.

Set options in .editorconfig. These are the defaults:

[*.cs]
leash.max_lines = 50
leash.max_parameters = 6
leash.max_out_parameters = 1
leash.allow_exempt = true
Rule Meaning
LEASH0001 Too many logical lines
LEASH0002 Too many explicit parameters
LEASH0003 Too many explicit out parameters
LEASH0004 Invalid effective configuration
LEASH0005 Invalid or disabled exemption

All diagnostics are errors tagged NotConfigurable. Severity overrides, NoWarn, #pragma warning disable, and diagnostic suppressors cannot turn them off; change the limits instead. An invalid option is reported as an error, not replaced with its default. Code that Roslyn classifies as generated is not analyzed.

Known gap: Roslyn 4.14 still honors SuppressMessage and UnconditionalSuppressMessage attributes for these diagnostics, and a syntax-only analyzer cannot block them through the supported diagnostic API. Ban these attributes for Leash IDs through build policy and code review. Tests record this behavior; see the enforcement boundary.

Reasoned exemptions

A source generator in the package adds Leash.ExemptAttribute to every project that uses Leash, so you do not need to define it.

[Leash.Exempt("External contract requires this signature.")]
public void Execute(int a, int b, int c, int d, int e, int f, int g) { }

The unqualified [Exempt(...)] form is also recognized in files that import the Leash namespace.

  • The analyzer reads only the written attribute name and the reason, which must be a non-blank string literal. It does not resolve symbols or aliases.
  • Exemptions do not cross function boundaries. Exempting a nested function does not reduce its parent's size, and exempting a parent does not exempt its nested functions.
  • See LEASH0005 for supported targets.

The generated type is internal and marked [Microsoft.CodeAnalysis.Embedded], so projects linked by InternalsVisibleTo do not see each other's copies. It is also [Conditional("LEASH_ATTRIBUTES")], so its usages are not emitted to metadata unless that symbol is defined.

A project that defines its own ExemptAttribute still compiles. The generated type is visible without qualification only where the Leash namespace is imported, and a type in the global namespace or the file's own namespace takes precedence over an imported one. If a file imports both namespaces with using directives, write Leash.Exempt.

Benchmarks and implementation

dotnet run -c Release --project Benchmark -- --filter "*TraverseParsedTree*" --job short
dotnet run -c Release --project Benchmark -- --filter "*AnalyzeWithRoslyn*" --job short

The benchmarks vary the method count (10, 100, 1,000) and the code shape (ordinary statements, dense expressions, nested local functions). MemoryDiagnoser reports allocations.

  • TraverseParsedTree measures the engine alone. It excludes parsing, keeps every red node alive so that Roslyn does not re-create its weakly held statement lists between iterations, and uses limits that produce no diagnostics. It allocates nothing, because the engine reuses its stack and the tree is retained.
  • AnalyzeWithRoslyn runs the normal analyzer-driver pipeline with the default limits.

Use --job Dry only to check that the benchmarks run; its timings are not reliable. See the measured baseline for timings, allocations, and measurement limits.

The engine:

  • visits each syntax node and token once, and adds each completed child's measurements to its parent;
  • uses a reusable per-thread explicit stack instead of recursion, holding O(depth) state;
  • classifies each node with one lookup in a table indexed by syntax kind;
  • folds single-token nodes, such as identifiers, literals, and designations, into their parent without a stack frame;
  • checks for cancellation, and clears node references before caching the stack;
  • uses 64-bit counters, and rounds logical lines without an overflowing addition.

The analyzer assembly depends only on Roslyn. The analysis context is passed by value instead of through a reporting delegate, so apart from the per-thread stack, the analyzer allocates nothing per syntax tree until it reports a diagnostic. Roslyn may still create red nodes during the traversal.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.2 86 10/14/2026
0.0.1 85 10/14/2026