Enforcing .NET code style rules at compile time
Code quality in .NET - Part 2
Introduction
Static code analysis is a great tool for spotting some kinds of error in your code, for example, not disposing of objects that implement IDisposable. Also, it helps to enforce and validate if the code written is following a defined standard, for example, using PascalCase for class names and camelCase for parameter names.
In this post I’ll show how to use Roslyn Analyzers with C# to enforce some standards of code quality and code style on your code, throwing errors at compile time if any rules are not being respected and not allowing the code to be pushed to protected branches of the repository.
Roslyn Analyzers
Roslyn is the compiler platform for .NET. Roslyn Analyzers are static code analysis tools for Roslyn. They inspect your code for style, quality, maintainability, and practices that are likely to cause bugs. They work based on predefined rules that can have their severity configured in the EditorConfig
file.
.NET 5 and later have the analyzers enabled by default. To enable them in earlier versions of .NET, you can set the property EnableNETAnalyzers
to true
on project files that uses a project SDK or install them as a nuget package:
Setting EnableNETAnalyzers on the project file
|
|
Installing as a nuget package
Install-Package Microsoft.CodeAnalysis.NetAnalyzers
Enabling more analyzers
By default, only some rules are enabled, but we can configure this with the AnalysisMode
property in the project file:
|
|
The AnalysisMode
property values allowed are different for .NET 6 and .NET 5 SDKs. Details here.
How to enable .NET Analyzers in VS Code
.NET Analyzers work by default in Visual Studio, but they have to be enabled in VS Code.
1 - Navigate to File > Preferences > Settings
.
2 - Navigate to Extensions > C# configuration
or search for omnisharp.enableRoslynAnalyzers
.
3 - Check the Omnisharp: Enable Roslyn Analyzers
option.
4 - Navigate to Extensions > C# configuration
or search for omnisharp.enableEditorConfigSupport
.
5 - Check the Omnisharp: Enable Editor Config Support
option.
6 - Restart C#/Omnisharp extension or VS Code.
Types of rules
.NET Analyzers have many categories of rules, but here I’ll list just a few to explain how they interact with Visual Studio’s features.
Standard formatting: Default Editorconfig options, like indent size and tabs or spaces;
Code Style - .NET Formatting: Language specific indentation, whitespaces, and wrapping. For instance, use spaces before parentheses in method definitions.
Code Style - .NET Language: C# and Visual Basic specific rules. Examples: using
var
instead of an explicit type, prefer auto properties instead of backing fields.Code Style - Naming Conventions: Rules about the naming of code elements, like enforcing PascalCase for classes’ names and Async at the end of async methods’ names.
Code Style - Unnecessary code: Rules for code that is unreachable or unused variables, fields, etc.
Code Quality: Rules to improve code quality. These rules help identify code that are likely to cause bugs or security problems. Examples: Do not declare static members on generic types, and Enums should have zero value.
The table below shows in which features of Visual Studio the fixes for these types of rules are applied on.
Fixes applied on | 🖹 Format | 🧹 Code Cleanup | 💡 Code Fix |
---|---|---|---|
Standard Formatting | ✔️ | ✔️ | ✔️ |
.NET Formatting | ✔️ | ✔️ | ✔️ |
.NET Language | ✔️ | ✔️ | |
Naming Conventions | ✔️ | ||
Unnecessary Code | ❗ | ✔️ | |
Code Quality | ❗ |
❗ Only some rules have fixes applied.
💡 In the previous post of this series, I explain how to configure Visual Studio to apply this rules on Code Cleanup and how to auto execute Code Cleanup on file save.
Enforcing rules in our code
Rules are configured in the EditorConfig file (that I explained in a Part 1 of this series) and their severity can be defined in three levels. Conflicts in the rules are resolved in the following order:
- Specific rules
- Category rules
- All analyzers rules
In the example below, Naming rules violations (IDE1006
) will be considered Warning
, because it is defined for the specific rule:
|
|
1. Generate an EditorConfig file from Visual Studio
First, we need to create an EditorConfig file with the configuration of the rules we will use as standards.
Visual Studio has a tool to help you configure the code style rules of your EditorConfig file, showing a snippet of code of how the rules work.
- Go to Tools > Options > Text Editor > C# > Code Style;
- Configure your Code Style preferences in the General, Formatting and Naming sub-menus. ⚠️ Don’t bother setting the severities here; some of them are only respected by Visual Studio and are not enforced on build and other IDEs;
- Back in the General sub-menu, click
Generate .editoconfig file from settings
and save it in the folder your solution file is in (.sln
).
⚠️ If you are not using Visual Studio, you can use a sample and change it to your preferences, like the one from Roslyn.
2. Configure all projects to use the recommended .NET Analyzers
Next, we set the AnalysisMode property in all our project files.
For .NET 6 SDK and later, set it to Recommended
or All
.
|
|
3. Set severity Error for all analyzers rules
In our EditorConfig, include this line to set severity to error
for all rules.
# Set severity = error for all analyzers
dotnet_analyzer_diagnostic.severity = error
4. Correct the errors and override the severity for rules you don’t want to use
If you are enabling the analyzers in an existing project, many errors will be shown. Correct them and override their severity if they don’t apply for you or you won’t correct them at the moment.
💡 In the previous post of this series, I explain how to add fixers to Visual Studio’s Code Cleanup. You can customize it to fix some rules violations.
Setting rules severity directly in EditorConfig file
|
|
Setting rules severity from Visual Studio’s Error List
For errors showing up in the Error List, you can right click on the rule and click on Set severity > Choose a severity
. The severity configuration will be added to the EditorConfig file.
Setting rules severity from Visual Studio’s Solution Explorer
From Solution Explorer, open the Dependencies > Analyzers
node below your project, then right click on the rule and click on Set severity > Choose a severity
. The severity configuration will be added to the EditorConfig file.
5. Enforce the rules on build
Enabling the analyzers only shows the messages in our IDE. To really enforce those rules, we have to inform the compiler to fail in case of rules violations, blocking changes that are not compliant to the standard to be merged into protected branches of the repository.
To do this, we need to enable the property EnforceCodeStyleInBuild
in all our project files.
|
|
Examples of rules being enforced
Rules being enforced on Visual Studio
Rules being enforced on VS Code
Rules being enforced on dotnet build command
Creating additional naming conventions
Here are some naming conventions of the C# language:
Symbols | Convention | Example |
---|---|---|
class/record/struct | PascalCase | PhysicalAddress |
interface | “I”+PascalCase | IWorkerQueue |
public members | PascalCase | StartEventProcessing |
private/internal fields | “_"+camelCase | _workerQueue |
static fields | “s_"+camelCase | s_workerQueue |
local variables *️ | camelCase | isValid |
parameters | camelCase | name |
async methods | PascalCase+“Async” | GetStringAsync |
More details here.
By default, Visual Studio doesn’t create naming conventions for static fields, local variables, parameters and async methods. If we want to use them, we have to manually set those rules, as shown below.
*️ Not specified in the docs, but Roslyn uses this convention.
Creating the naming convention for async methods
|
|
Creating the naming convention for local variables and parameters
|
|
How to ignore the CA1707 rule (Identifiers should not contain underscores) on test projects
Some conventions for naming test methods use underscore. If that is your case, you will receive a violation for the CA1707 rule.
To disable the rule only on the test project, create a file named GlobalSuppressions.cs
in the root of your test project with the content below.
|
|
Third-party analyzers
There are third-party analyzers that can have additional rules that can be useful. These are some: