Analysez votre code en temps réel avec Roslyn (part 1)
Dans un souci de qualité, de sécurité, d’optimisation et d’uniformisation, l’analyse de code est un outil indispensable.
Jusqu’ici nous étions habitués à utiliser l’analyse de code lors de la compilation, via FXCop, StyleCop, Sonar ou autres. Le compilateur Open Source Roslyn a changé la donne, aujourd’hui l’analyse de code se fait en temps réel et propose des corrections automatiques.
Le format des analyseurs de code a changé. Les extensions Visual Studio sont devenues des packages Nugets. StyleCop et Sonar ont déjà adapté leurs solutions et proposent leurs analyseurs sous forme de package Nuget unique. Microsoft a dispatché les règles d’FXCop entre plusieurs packages Nuget proposés par l’équipe Roslyn, tandis que d’autres équipes telles que Azure et Visual Studio proposent leurs packages avec de nouvelles règles.
Pour les règles de style, Visual Studio depuis la version 2015, propose de les définir en natif, via l’interface de Visual Studio pour les règles utilisateur, ou via un fichier .editorConfig pour les règles projets. Ces règles de style sont dynamiques. Elles permettent de coder facilement des règles de Naming projet sans passer par des règles custom, que nous étudierons dans une deuxième partie.
Les règles de style utilisateur
General
Depuis la version 2015 de Visual Studio, un nouvel onglet est disponible dans les options : CodeStyle. Il permet de sélectionner les règles de style que l’on veut appliquer au niveau utilisateur.
Chaque règle contient une préférence de condition d’application de la règle (yes, no, never, when possible, when on single line…) et un niveau de sévérité, depuis None (désactivé) à Error (blocage de la compilation) en passant par Info et Warning correspondant au niveau des messages d’avertissement, générés au même niveau que les messages de compilation.
Quand une règle est enfreinte, Visual Studio souligne l’expression, affiche l’icône ampoule, et propose un scope quand la correction automatique est disponible et applicable : scope expression par défaut, la plupart du temps : document, projet ou solution, sont aussi proposés.
Exemple si j’active le level Erreur pour la règle de style : Prefer pattern matching over ‘as’ with ‘null’ check (préférez le mot clé ‘is’ au lieu de ‘as’ + test ‘null’):
Dès que la règle est enfreinte, l’ampoule apparaît. Si vous survolez l’expression, vous avez le détail de l’erreur. Si vous cliquez sur show potentiel fix, les correctifs disponibles apparaissent. Quand vous cliquez sur un scope proposé pour le fix, la violation est corrigée dans le scope choisi (pour le scope expression, cliquez sur le nom de la règle) :
Ces préférences sont stockées dans vos options Visual Studio.
La documentation est consultable ici : https://docs.microsoft.com/visualstudio/ide/code-styles-and-quick-actions.
Naming
Les règles de Naming disponibles via l’interface de Visual Studio ont la particularité d’être dynamiques.
Il est ainsi possible de définir des styles et des spécifications, pour appliquer des règles différentes suivant les options d’accessibilité ou les Modifiers des entités (class, method, field…). Si une entité est spécifiée plusieurs fois, elle obéit à la règle ayant le plus haut niveau de priorité suivant l’ordre défini, modifiable à l’aide des boutons [↑] et [↓] dans la colonne Reorder.
Cela permet de se passer des règles custom pour tout ce qui est Naming, un gain de temps considérable.
Par exemple, si je veux que toutes mes méthodes Async publiques soient formatées en Pascal Case avec le suffixe Async :
Je commence par créer le symbole « Method Async Public », en cliquant sur Manage specifications en bas de l’onglet Naming, puis sur [+] pour ajouter un Symbol.
Ensuite je crée le style « Async Suffix PascalCase », en cliquant sur Manage styles en bas de l’onglet *Naming*, puis sur [+] pour ajouter un style.
Je peux créer une règle à la suite des existantes avec le symbole et le style prédéfinis et lui affecter le level Error.
Le résultat apparaît en temps réel dès que la règle est enfreinte :
Les règles de style projet
.editorConfig
TOUTES les règles de style disponibles via l’interface de Visual Studio peuvent être transcrites dans un fichier .editorConfig.
Le fichier .editorConfig se place au niveau de la solution ; il est prioritaire sur les règles utilisateur ; il est multiplateforme et multi-langage.
Référence : https://editorconfig.org
Exemple de contenu d’un fichier .editorconfig
Pour connaître les options Visual Studio équivalentes, référez-vous à la documentation Microsoft : https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions.
Exemple : implémentation de la règle « Async Suffix Pascal Case » du paragraphe précédent :
# Définition du scope, fichiers cs et vb
[*.{cs,vb}]
# definition du symbol public_method_async
dotnet_naming_symbols.public_method_async.applicable_kinds =method
dotnet_naming_symbols.public_method_async.applicable_accessibilities =public
dotnet_naming_symbols.public_method_async.required_modifiers =async
# définition du style pascalcase_suffix_async
dotnet_naming_style.pascalcase_suffix_async.capitalization =pascal_case
dotnet_naming_style.pascalcase_suffix_async.required_suffix =Async
# définition de la règle public_async_method_pascalcase_suffix_async, avec la sévérité error
dotnet_naming_rule.public_async_method_pascalcase_suffix_async.symbols =public_method_async
dotnet_naming_rule.public_async_method_pascalcase_suffix_async.style =pascalcase_suffix_async
dotnet_naming_rule.public_async_method_pascalcase_suffix_async.severity =error
Il est aussi possible d’installer une extension Visual Studio qui facilite la rédaction de ce fichier : https://marketplace.visualstudio.com/items?itemName=EditorConfigTeam.EditorConfig
Cette extension ne propose que l’IntelliSense, et contient quelques bugs de jeunesse. Le TechClub SOAT vous proposera bientôt une extension “So’EditorConfig” avec une belle interface graphique.
La liste des langages supportés est infinie. Seules les propriétés disponibles changent. Voici quelques-uns des langages qui offrent le plus de possibilités : C#, Java, C++, VB, JS, Html, Xml, Css, PY, PS, TypeScript…
Voici une liste des IDE supportés, en natif ou avec un plugin :
IDE | Natif | Plugin |
AppCode | ✓ | |
Atom | ✓ | |
BBEdit | ✓ | |
Brackets | ✓ | |
Builder | ✓ | |
Clion | ✓ | |
Coda | ✓ | |
Code::Blocks | ✓ | |
CodeLite | ✓ | |
Eclipse | ✓ | |
Emacs | ✓ | |
Geany | ✓ | |
Gedit | ✓ | |
GitHub | ✓ | |
Gogs | ✓ | |
IntelliJIdea | ✓ | |
Jedit | ✓ | |
Komodo | ✓ | |
KTextEditor | ✓ | |
Micro | ✓ | |
NetBeans | ✓ | |
Notepad++ | ✓ | |
PhpStorm | ✓ | |
PyCharm | ✓ | |
RubyMine | ✓ | |
SourceLair | ✓ | |
Sublime Text | ✓ | |
Textadept | ✓ | |
Textmate | ✓ | |
TortoiseGit | ✓ | |
Vim | ✓ | |
Visual Studio | ✓ | |
Visual studio Code | ✓ | |
WebStorm | ✓ | |
XCode | ✓ |
Les règles d’analyse de code importées
Nuget
Il est aujourd’hui possible d’importer des packages Nuget qui contiennent des règles d’analyse de code. En cas de conflit, ces règles d’analyse de code sont prioritaires sur les règles de style, qu’elles soient locales ou dans le fichier .editorconfig.
Par exemple, les règles Microsoft “Managed rules for managed code” dont nous avions l’habitude – cf. https://msdn.microsoft.com/library/dd264893.aspx – sont pour la majorité d’entre elles maintenant disponibles via l’installation d’un package Nuget : Microsoft.CodeAnalysis.FxCopAnalyzers.
Ce package est un agrégat de 4 dépendances qui contiennent les règles d’analyse de code.
Dans l’onglet Analyzers (qui est vide pour un nouveau projet), on voit apparaître les règles incluses :
RuleSet
Ces règles sont installées avec un niveau de sévérité par défaut. Vous pouvez modifier ce niveau de sévérité, via un fichier ruleset, qui lui n’a pas changé depuis FXCop.
Vous pouvez ajouter un fichier ruleSet via l’interface de Visual Studio :
Il est créé avec les options par défaut des packages Nuget importés. Visual Studio propose une interface pour les éditer.
Sinon, si vous éditez le niveau de sévérité d’une règle dans l’onglet *Analyzer*, un fichier ruleset sera automatiquement créé.
Les fichiers ruleset se gèrent par projet .Vous pouvez en définir un commun, mais il faudra le renseigner spécifiquement sur chaque projet de la solution en éditant manuellement le fichier .csproj, sauf pour les projets .net framework qui contiennent toujours l’ancien onglet *code analysis* permettant de renseigner ce chemin.
Exceptions
Pour les règles code analysis, il est rare d’avoir une correction automatique, car elles sont souvent compliquées à coder. Dans ce cas, l’option : show potentiel fix apparaît tout de même, mais quand on clique dessus il ne se passe rien. Une petite amélioration à envisager.
Par contre, les règles code analysis proposent toujours d’établir une exception lorsque par exemple corriger une règle demande plus d’effort qu’elle n’apporte de valeur. Dans ce cas, Visual Studio propose soit d’écrire l’exception juste au-dessus de l’endroit où la règle est transgressée, soit de l’écrire dans un fichier GlobalSuppression.cs pour les centraliser. Ce fonctionnement est bien connu : il n’a pas changé depuis FXCop.
Par exemple, si dans un projet contenant le Nuget Microsoft.CodeAnalysis.FxCop, j’écris une méthode Dispose() sans l’instruction SuppressFinalize, je vais enfeindre 2 règles :
Microsoft.Design : CA1063 ImplementIDisposableCorrectly
Microsoft.Usage : CA1816 CallGCSuppressFinalizeCorrectly
Visual Studio m’avertit des 2 interdictions et me propose d’ajouter une exception dans le fichier source ou dans un fichier GlobalSuppression.cs.
Si je choisis le fichier source, la syntaxe des exceptions sera la suivante :
#pragma warning disable CA1063 // Implement IDisposable Correctly
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
public void Dispose()
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
#pragma warning restore CA1063 // Implement IDisposable Correctly
{
Dispose(true);
}
Si je choisis le fichier GlobalSuppression.cs, la syntaxe des exceptions sera la suivante (à vous de remplacer le texte de la justification) :
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "<Pending>", Scope = "member", Target = "~M:Soat.Roslyn.MyClass.Dispose")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "<Pending>", Scope = "member", Target = "~M:Soat.Roslyn.MyClass.Dispose")]
Conclusion
Vous avez les outils en main pour contrôler en temps réel vos règles de Naming ou de style, et les clés pour installer des règles d’analyse de code qui pourront renforcer la sécurité, l’évolutivité et la performance de votre code, via des packages Nuget.
Pour compléter l’arsenal mis à notre disposition, nous verrons dans notre prochain article comment implémenter des règles custom, codées par nos soins.