paint-brush
Spotting and Preventing Formatting Errors in Your Code by@danielgenezini
910 reads
910 reads

Spotting and Preventing Formatting Errors in Your Code

by Daniel GeneziniJanuary 9th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This is part two in a three-part series on setting up a uniform formatting standard in your code editor. 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.
featured image - Spotting and Preventing Formatting Errors in Your Code
Daniel Genezini HackerNoon profile picture

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.


This is part two in a three-part series on setting up a uniform formatting standard in your code editor. Read part 1 here.

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

<PropertyGroup>
  <EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

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:

<PropertyGroup>
  <AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>

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 the Part 1 of this series) and their severity can be defined in three levels. Conflicts in the rules are resolved in the following order:


  1. Specific rules

  2. Category rules

  3. All analyzers rules


In the example below, Naming rules violations (IDE1006) will be considered Warning, because it is defined for the specific rule:


# Defines that all analyzers rules are suggestions
dotnet_analyzer_diagnostic.severity = suggestion
# Defines that all Code Style analyzers rules are errors
dotnet_analyzer_diagnostic.category-Style.severity = error
# Defines that the rule IDE1006 is a warning
dotnet_diagnostic.IDE1006.severity = warning

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.


  1. Go to Tools > Options > Text Editor > C# > Code Style;
  2. 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;
  3. 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.

<PropertyGroup>
  <AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>

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

# Other rules ...

# Set severity = none to the rules that are not important for me
dotnet_diagnostic.IDE0075.severity = none

# Set severity = warning to the rules that need to be resolved later
dotnet_diagnostic.IDE0047.severity = warning

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.

<PropertyGroup>
  <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

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

dotnet_naming_rule.async_methods_should_be_pascalcase_async.severity = error
dotnet_naming_rule.async_methods_should_be_pascalcase_async.symbols = async_methods
dotnet_naming_rule.async_methods_should_be_pascalcase_async.style = pascalcase_async

dotnet_naming_symbols.async_methods.applicable_kinds = method
dotnet_naming_symbols.async_methods.applicable_accessibilities = *
dotnet_naming_symbols.async_methods.required_modifiers = async

dotnet_naming_style.pascalcase_async.required_suffix = Async
dotnet_naming_style.pascalcase_async.capitalization = pascal_case

Creating the naming convention for local variables and parameters

dotnet_naming_rule.locals_and_parameters_should_be_pascal_case.severity = error
dotnet_naming_rule.locals_and_parameters_should_be_pascal_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_and_parameters_should_be_pascal_case.style = camel_case

dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local

dotnet_naming_style.camel_case.capitalization = camel_case

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.


using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Not applicable for test names", Scope = "module")]

Third-party analyzers

There are third-party analyzers that can have additional rules that can be useful. These are some:

References and Links

Liked this post?

I post extra content in my personal blog. Click here to see.


Also published here.