Loupe

Comment écrire soi-même une règle CodeAnalysis

Avoir une façon uniforme, cohérente et pertinente de nommer les objets de notre code est le premier pas vers du code de qualité. Des outils comme CodeAnalysis nous permettent de vérifier tout cela(entre autre) de manière automatique en définissant un jeu de “règles”.  Certaines fois, on ne trouve pas celle qui correspond à nos conventions de nommage et il est temps d’en créer une nouvelle !

 

Dans notre exemple nous allons chercher à vérifier que tous les champs privés non statiques de nos objets ont un nom commencant par un underscore. Je trouve cette convention de nommage intéressante car cela permet de voir au premier coup d’oeil si une variable à un scope de “classe” ou un scope de “fonction” (ces denières n’ayant pas d’underscore au début de leur nom).

 

Définition de la règle

Pour créer une règle il faut commencer par un nouveau projet de type bibliothèque de classes. Une fois cela fait, il est nécessaire de référencer plusieurs dlls :

  • FxCopSdk.dll
  • Microsoft.VisualStudio.CodeAnalysis.dll
  • Microsoft.Cci.dll

 

Ces DLLs se trouvent dans le dossier “\Team Tools\Static Analysis Tools\FxCop\” de l’emplacement d’installation de VisualStudio. Sur mon PC cela donne : “C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Static Analysis Tools\FxCop\Microsoft.Cci.dll”

 

Une fois cela fait, nous allons créer un fichier XML de définition des règles que nous utilisons

Dans mon cas, je l’appelle “Rules.xml” et je m’assure que son content type est bien “Embedded resource” dans la fenêtre de propriété.

<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="Infinite Square Rules">
  <Rule 
    TypeName="AllNonStaticPrivateFieldsShouldStartWithAnUnderscore" 
    Category="InfiniteSquare.Naming" 
    CheckId="ISR001">
    <Name>
        All Non Static Private Fields Should Start With An Underscore
    </Name>
    <Description>
        All Non Static Private Fields Should Start With An Underscore.
    </Description>
    
    <Url>http://infinitesquare.com</Url>
    <Resolution>
        The name of the private field {0} does not start with the prefix '_'.
        Add the prefix to the field name.
    </Resolution>
    <MessageLevel Certainty="95">Warning</MessageLevel>
    <FixCategories>Breaking</FixCategories>
    <Email />
    <Owner />
  </Rule>
</Rules>

 

Création de la règle

L’étape suivante consiste à créer une classe de base pour toutes nos règles.

Celle-ci va dériver de BaseIntrospectionRule et utiliser son constructeur pour indiquer le nom de la règle et l’emplacement de son fichier de définition.

public abstract class BaseRule : BaseIntrospectionRule
{
    protected BaseRule(string name)
        : base(

            // Nom de la règle
            name,

            // Nom du fichier XML sans l'extension
            typeof(BaseRule).Assembly.GetName().Name + ".Rules",

            // Assembly de définition de la règle (ici la courante)
            typeof(BaseRule).Assembly)
    {
    }
}

 

Ensuite nous allons créer notre règle en dérivant de cette première classe “BaseRule”. Attention, le nom de la règle passé au constructeur de base doit bien être celui défini dans le fichier XML. De même nous overridons la propriété TargetVisibility pour indiquer que la règle ne doit être appelée que sur les champs non-visibles en dehors de la classe.

public class AllNonStaticPrivateFieldsShouldStartWithAnUnderscoreRule 
    : BaseRule
{

    public AllNonStaticPrivateFieldsShouldStartWithAnUnderscoreRule()
        : base("AllNonStaticPrivateFieldsShouldStartWithAnUnderscore")
    {


    }

    // Uniquement les champs privés
    public override TargetVisibilities TargetVisibility
    {
        get
        {
            return TargetVisibilities.NotExternallyVisible;
        }
    }

 ...

}

 

Ecriture de la règle

Pour écrire la règle il suffit alors d’overrider une des méthodes CheckXXX de notre règle et de retourner une collection d’objets “Problem”.

public override ProblemCollection Check(Member member)
{

    var field = member as Field;
    if (field == null)
    {
        //On ne teste que les Fields
        return null;
    }


    if (
        //Pas un champ static
         !member.IsStatic

        //Commence bien par un underscore
        && !member.Name.Name.StartsWith("_")
        )
    {
        
        //Lecture dans le fichier XML
        var resolution = GetResolution(member.Name.Name);
        var problem = new Problem(resolution, member)
        {
            Certainty = 100,
            FixCategory = FixCategories.Breaking,
            MessageLevel = MessageLevel.Warning,

        };
        Problems.Add(problem);
    }


    return Problems;
    
}

 

Attention de bien retourner la collection de problèmes remplie.

Utilisation de la règle

Il est maintenant temps d’utiliser la règle dans votre projet. Pour cela, il faut d’abord rajouter un jeu de règles Code Analysis en utilisant le wizard d’ajout d’items à la solution.

RuleSetAdd

Ensuite, il faut l’éditer à la main (clic droit –>ouvrir avec –> éditeur XML) et ajouter un noeud “RuleHintPaths” pour indiquer où se trouve la DLL. Dans cet exemple je la place à la racine de la solution dans un dossier “InfiniteSquareRules”.

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Infinite Square Rules" Description="Infinite Square Rules" ToolsVersion="10.0">
  <RuleHintPaths>

    <Path>InfiniteSquareRules</Path>

  </RuleHintPaths>
</RuleSet>

 

Vous pouvez alors sauvegader et réouvrir le fichier pour voir apparaitre votre règle dans la liste des vérifications effectuées.

rulesSetYoupi

Bon code !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus