Ir al contenido principal

Linting C# in Visual Studio Code

Though very usual in programming environments as Javascript/Typescript, linting, or analyzing code for enforcing a set of coding style rules, is not usually present in the .NET based environments.

Rule enforcing is really useful when working on team shared codebases in order to keep them coherent, what in last term reduces both development times and coding errors.

A linting example

Maybe a practical example would be helpful for explaining what linting is to the newcomers (feel free to go on if you aren't).

Let's imagine you are a new member in a C# development team that has well established set of coding style rules. Instead (or apart) of putting them in a document, they've adopted a tool that checks these rules during the code building process. Your first code is such ambitious as this:

namespace HelloWorld
{
    using System;
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.Write("Hello world!");
        }
    }
}

Instead of reviewing the styling documentation in order to check if your code meets the rules, you're using a linter that checks those rules for you during the building process. So when you build that masterpiece you get this warning:

warning SA1600: Elements should be documented

WTF means it? When you check the linter documentation you find that the rule SA1600 states that the classes and their methods must be properly documented. So you fix your code in this way:

namespace HelloWorld
{
   using System;
   /// <summary>Program class.</summary>
   public class Program
   {
      /// <summary>Program entry point.</summary>
      /// <param name="args">Command line args.</param>
      public static void Main(string[] args)
      {
         Console.Write("Hello world!");
      }
   }
}

Now the code builds with no warnings and your first work is aligned with the team coding style at near-zero effort cost.

Adding linter support to C# in Visual Studio Code

As the blog entry claimed, the aim of this article is exposing how to use a Code Quality Analyzer (or linter) in an scenario defined by C# as programming language, .NET/.NET Core as framework and Visual Studio Code as IDE.

The proposed solution is based on StyleCopAnalyzers, a solution based on the original Microsoft's StyleCop that allows:
  • Applying rules to C# codebase using Visual Studio Code.
  • Customizing the behaviour of certain defined rules, modify rule criticity and even disabling rules.
  • Defining rulesets per project.
  • Checking the source code quality at build time, what allows seamless integration with CI tools, so code analysis can be performed when it's being deployed and, in case of finding unmet rules, stopping the deployment process.

To apply StyleCopAnalyzers to a C# project, the first step is installing it by opening a Visual Studio Code terminal (Menu Terminal/New Terminal) and typing:

dotnet add package StyleCop.Analyzers

That command will add the StyleCop.Analyzers package to the project and will perform the required changes in the .csproj file to start using it.

Now, when building your code is very probable that you received a good bunch of warnings dropped by StyleCop, so it's time to customize it.

Customization

The first, optional thing is converting those warnings into errors, so if the building process is executed by a CI tool it was able to detect a problem and stop the code deployment. To do it, edit the project file (extension .csproj) and add a TreatWarningsAsErrors property set to true:

<PropertyGroup>
	...
	<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

If you are not concerned by CI tools or, in general, treating the linting problems as warnings is not a problem, the above step can be omitted.

Now let's deal with the most important customization: The StyleCop rules and how they are applied. This behaviour is configured via two files:
  • The rule sets file: It contains the set of rules that the analyzer will apply and the criticity of each uncomplishment. In practice, it allows both creating a hierachical organization of rules based on their criticity (error, warning, info) and disabling rules by setting their criticity to none.
  • The stylecop.json file, that allows customizing how certain rules will be applied. Notice that we are not customizing their criticity but how code is being analyzed.

Regarding the rule sets file, it is not necessary: if it's not present, StyleCop will apply a set of predefined rules. However, my very best advice is always adding the rule set file that matches those predefined rules, so in that way modifying them was easier.

The default rule set file can be downloaded from here. In order to keep the project files organized, I recommend creating a .stylecop directory in the project directory root and putting that file in it renamed as stylecop.ruleset.xml.

Once done, configure the project to use the ruleset by adding a CodeAnalysisRuleSet property in the project file (extension .csproj):

<PropertyGroup>
	...
	<CodeAnalysisRuleSet>$(SolutionDir).stylecop/stylecop.ruleset.xml</CodeAnalysisRuleSet>
</PropertyGroup>

Finally, as the rule set file, the stylecop.json file is also not necessary, but again it's a good idea creating it even being empty. The main reasons are two: Keeping the project properly configured from scratch by setting the stylecop.json file location, and giving support to VSCode Intellisense by attaching the stylecop.json schema file.

So as a first step download the stylecop.json schema file from here and store it in the .stylecop folder as stylecop.schema.json. Then create a minimal stylecop.json file in the same folder with this content:

{
    "$schema": "./stylecop.schema.json",
    "settings": {}
}

Then configure StyleCop to use the newly created stylecop.json file by setting the AdditionalFiles property in the project file (extension .csproj):

<ItemGroup>
    ...
    <AdditionalFiles Include=".stylecop/stylecop.json" />
</ItemGroup>

Now you'll be able to fine-tune the behaviour of StyleCop by checking its documentation and setting the required options in the stylecop.json.

Last but not least

At this point StyleCop will behave as Javascript/Typescript linters except for one thing: It won't be able to apply default fixes, something really useful in order to save time when dealing with big basecodes.

This behaviour can be partly adopted by enabling analyzers in OmniSharp (thanks, Filip W.). To do so, go to Visual Studio Code preferences (menu File/Preferences/Settings) and type "omnisharp analyzers" in the settings search box. You'll be able to enable Omnisharp support to analyzers both globally (if enabled at User level) or at project level (if enabled at Workspace level).


Once set, reload Visual Studio Code and you'll start to receive StyleCop messages in the Problems window, the problems will be highlighted in code and a default fix (the holly bulb) will be proposed.


As you can see in the previous screenshot, among others a SA1200 problem has been detected at line 9 ("Using directive should appear within a namespace declaration"), the line is highlighted in the editor window and a fix ("Reorder using statements") is proposed.

Conclusion

At the date of writing this article, there's no way of automatically fixing all the problems found in Visual Studio Code (not so in Visual Studio). I'm not sure if that's good or bad, some programmers blaim against these cool features whereas other love them.

With it, StyleCop would fully perform as Javascript/Typescript linters. In my opinion, even lacking the automatic fixing feature, StyleCop with Omnisharp is damn good enough to be adopted in professional environments based on C#, .NET (Core) and Visual Studio Code in which a code analysis tool was required to keep the codebase homogeneous.

Comentarios

Entradas populares de este blog

ESlint: Ignore unused underscore variables

Some naming conventions promote the use of the underscore character (" _ ") for those variables that must be declared but are not being used. One common case is that in which a function signature contains some variables that will not be used, as for instance the Express error handlers: app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }); In the above example only the arguments err and res are being used, though all four must be defined in the handler signature. Thus, following the naming convention of using underscores for those unused variables, we could recode it as: app.use(function(err, _, res, __) { console.error(err.stack); res.status(500).send('Something broke!'); }); Though it makes the function more readable, it comes with a problem if using ESlint: it will blame by declaring unused variables. error '_' is defined but never used error '__' is define

Using Bitbucket app passwords with git on MacOS (OSX)

Learn how Bitbucket passwords are stored by git on MacOS.