Enforce explicit return types on public APIs with a Roslyn analyzer
A custom Roslyn analyzer flags `var` in public .NET method signatures inside Visual Studio, turning code-review nits into build-time diagnostics.
15 Dec 2025, 06:11 UTC

The problem: public APIs that hide their types
A teammate merges a public method whose signature uses var. The code compiles, but the return type is invisible to anyone reading the call site without hovering or building the solution. On a public API, that ambiguity becomes a contract problem: consumers cannot rely on IntelliSense alone, and code reviews burn cycles on "please use the explicit type."
The useful takeaway is that you can stop this pattern at compile time. A custom Roslyn analyzer runs inside Visual Studio’s normal build, writes the issue to the Error List, and offers a one-click code fix that replaces var with the inferred type.
Where a Roslyn analyzer fits
Roslyn is the compiler platform behind C# and Visual Basic in Visual Studio. Analyzers plug directly into the compile pipeline, inspect syntax and semantic information, and emit diagnostics alongside compiler errors. Because the analyzer runs during the build, developers do not need a separate linting tool; they only need to reference the analyzer package.
Worked example: flag var in public members
This example builds an analyzer that warns when a public method or property uses var in its signature, then suggests the inferred type as a fix.
- In Visual Studio, create a project from the "Analyzer with Code Fix" template. The template typically targets .NET Standard 2.0 so the analyzer can be referenced by .NET Framework and modern .NET projects.
- Define a diagnostic rule. The following is illustrative and has not been executed:
private static readonly DiagnosticDescriptor Rule =
new(
id: "VSEXPLICIT001",
title: "Use explicit type instead of 'var' in public member",
messageFormat: "Public member '{0}' uses 'var'; specify the explicit return type.",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
- Override a syntax action to visit
MethodDeclarationSyntaxandPropertyDeclarationSyntax. When the return type isvarand the member is public, callcontext.ReportDiagnosticwith the rule and the node location. - Add a
CodeFixProviderthat asks the semantic model for the inferred type and offers a replacement ofvarwith that type name. - Package the analyzer as a NuGet package or VSIX. A NuGet package is usually easier to share across a team.
Reference the package in a sample project:
<ItemGroup>
<Analyzer Include="..\build\Your.Analyzer.nupkg" />
</ItemGroup>
Build the project. If the reference resolves, the diagnostic should appear in the Error List, and the editor should show a light-bulb quick action on the flagged line. A risk is that an incorrect language-version target can cause the analyzer to miss syntax or report false positives.
Trade-offs and limitations
- Build performance: lightweight syntax checks add negligible time. Complex semantic analysis or calls to external services can slow large solutions.
- False positives: overly aggressive rules overwhelm developers and encourage suppression. Start with
Warning, notError. - Language version alignment: the analyzer must match the project’s
LangVersion. Newer C# syntax in an older project may be missed or misanalyzed. - Maintenance: analyzers are code. They need updates when the team adopts new language features or changes API conventions.
Verify before rolling out
- Add the analyzer package to a scratch console project.
- Add a public method that intentionally uses
varin its signature. - Run
dotnet build YourSolution.slnin a Developer Command Prompt for Visual Studio or a terminal with the .NET SDK. Required permissions are ordinary read/write access to the project folder. - Open the Error List and confirm diagnostic ID VSEXPLICIT001 appears with the quick fix. Do not treat this as guaranteed output; it depends on correct package references.
- To test stricter enforcement, set the rule to error in
.editorconfig:
[*.cs]
dotnet_diagnostic.VSEXPLICIT001.severity = error
- Run a CI build, such as Azure Pipelines, and confirm the pipeline fails when the rule triggers. Keep the rule at Warning on the main branch until the team has lived with it for at least one sprint.
Closing
Start with one rule, ship it as a warning, and collect feedback before tightening severity. A Roslyn analyzer will not replace code review, but it can remove repetitive typing debates so reviews focus on behavior instead of syntax.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.