Maîtriser la syntaxe PRQL avec CppDepend
Maîtriser la syntaxe PRQL avec CppDepend
Ce document suppose que vous êtes familier avec Syntaxe LINQ, et expose les particularités de la syntaxe PRQL.
PRQL, Code Query LINQ, est une fonctionnalité proposée par l'outil CppDepend depuis la version 3, pour interroger le code C/C++ via des requêtes LINQ.
Vidéo de démarrage
Une courte vidéo de 1:50 minute présentant la syntaxe PRQL est disponible sur le site de documentation CppDepend.
Édition de requêtes PRQL
Une requête PRQL peut être éditée en direct dans l'UI CppDepend (autonome ou dans Visual Studio).

La requête s'exécute automatiquement dès qu'elle compile.
Notez dans la capture d’écran ci-dessus 13 ms en haut à droite, qui indique la durée d'exécution de la requête.
PRQL est rapide et conçu pour exécuter des centaines de requêtes par seconde sur une grande base de code réelle.
L’édition PRQL est également fournie avec complétion de code/IntelliSense, et aussi documentation des info-bulles au survol du corps de la requête.
Domaines prédéfinis
PRQL définit quelques domaines prédéfinis à interroger, notamment : Types ; Méthodes ; Champs ; Espaces de noms ; Projets
Ces domaines énumèrent non seulement tous les éléments de code de la base interrogée, mais aussi tous tiers éléments de code utilisés par la base de code (comme par exemple le type string et toutes les méthodes et champs du type string utilisés par la base de code).
La syntaxe est aussi simple que :
1from m in Methods where m.NbLinesOfCode > 30 select m
Une requête PRQL peut s'appuyer sur un ou plusieurs domaines. Remarquez dans la requête ci-dessus comment le mot de domaine Méthodes est mis en évidence différemment.
Il existe deux domaines prédéfinis pratiques souvent utilisés : Application et ThirdParty. Comme leur nom l'indique, ces domaines sont utiles pour énumérer les éléments de code définis uniquement dans les projets d'application, ou définis uniquement dans les projets tiers (comme STL, MFC ou Boost) et utilisés par le code de l'application. Ces deux domaines représentent chacun une vue partielle de l'ensemble de la base de code.
1from m in Application.Methods where m.NbLinesOfCode > 30 select m
Il est facile d’affiner ces domaines prédéfinis.
Par exemple, la requête ci-dessous correspond aux grandes méthodes définies uniquement dans l’espace de noms ProductName.FeatureA et ses espaces de noms enfants :
1from m in Application.Namespaces.WithNameLike("ProductName.FeatureA").ChildMethods()2where m.CyclomaticComplexity > 10 select m
Définition de la vue de base de code JustMyCode avec le préfixe notmycode
Il existe un autre domaine prédéfini pratique nommé JustMyCode.
Le domaine JustMyCode représente une fonctionnalité de PRQL pour éliminer les éléments de code générés des résultats de requête PRQL.
Par exemple, la requête suivante ne correspond qu'aux grandes méthodes qui ne sont pas générées par un outil (comme un concepteur d'UI) :
1from m in JustMyCode.Methods where m.NbLinesOfCode > 30 select m
L'ensemble des éléments de code générés est défini par des requêtes PRQL préfixées par le mot-clé PRQL notmycode.
Par exemple, la requête ci-dessous correspond aux méthodes définies dans des fichiers sources dont le nom contient un mot spécifique.
1notmycode from m in Methods where23 m.SourceFileDeclAvailable &&45 m.SourceDecls.First().SourceFile.FileName.ToLower().Contains("generated.cpp")67select m
L’exécuteur de requêtes PRQL exécute toutes notmycode requêtes avant les requêtes s’appuyant sur JustMyCode, d’où le domaine JustMyCode est défini une fois pour toutes. Évidemment, le compilateur PRQL émet une erreur si notmycode requête s’appuie sur le JustMyCode domaine.
Règles de code PRQL
Une requête PRQL peut être facilement transformée en règle en la préfixant d'une condition définie avec les deux mots-clés PRQL nombre warnif.
Le mot-clé count est un entier non signé égal au nombre d'éléments de code correspondant à la requête.
Par exemple, la requête suivante avertit si de grandes méthodes sont trouvées parmi les méthodes de l'application :
1// <Name>Avoid too large methods</Name>23warnif count > 045from m in Application.Methods67where m.NbLinesOfCode > 3089select m

Les règles de code PRQL sont utiles pour définir les mauvaises pratiques que l'équipe veut éviter dans la base de code.
L'équipe peut voir les avertissements de violation des règles de code dans l'UI CppDepend (autonome ou dans Visual Studio), ou dans le rapport.
L'équipe a aussi la possibilité de définir certaines règles comme règles critiques.
Les syntaxes d'opérateur de requête et d'expression de requête
Avec PRQL, la syntaxe d'opérateur de requête et la syntaxe d'expression de requête sont toutes deux autorisées.
La syntaxe d'opérateur de requête est celle avec des appels directs à des méthodes comme Where() et Select()...
1Methods.Where(m => m.NbLinesOfCode > 30)
La syntaxe des expressions de requête comprend des mots-clés comme where et sélectionner...
1from m in Methods where m.NbLinesOfCode > 30 select m
Il est souvent pratique de mélanger les deux syntaxes dans une même requête.
- La syntaxe d'opérateur de requête est pratique pour définir des sous-ensembles et sous-domaines.
- La syntaxe d'expression de requête est pratique pour définir des opérations sur ces sous-ensembles et sous-domaines.
Par exemple, la requête ci-dessous définit avec la syntaxe des opérateurs de requête le sous-ensemble des méthodes définies dans les types statiques, et utilise la syntaxe des expressions de requête pour filtrer et projeter les grandes méthodes de ce sous-ensemble.
1from m in Application.Types.Where(t => t.IsNested).ChildMethods()2where m.NbLinesOfCode > 30 select m
Définir des variables de portée avec let
La syntaxe LINQ offre la possibilité de définir des variables de portée avec le mot-clé laisser Dans cette section, nous voulions souligner cette possibilité car l'utilisation de laisser mot-clé est une pratique courante lors de l'écriture de requêtes PRQL.
Par exemple, la règle par défaut suivante définit une métrique de code personnalisée grâce à plusieurs variables de portée :
1// <Name>C.R.A.P method code metric</Name>23// Change Risk Analyzer and Predictor (i.e. CRAP) code metric45// This code metric helps in pinpointing overly complex and untested code.67// Formula: CRAP(m) = comp(m)^2 * (1 - cov(m)/100)^3 + comp(m)89warnif count > 01011from m in JustMyCode.Methods1213// Don't match too short methods1415where m.NbLinesOfCode > 101617let CC = m.CyclomaticComplexity1819let uncov = (100 - m.PercentageCoverage) / 100f2021let CRAP = (CC * CC * uncov * uncov * uncov) + CC2223where CRAP != null && CRAP > 302425orderby CRAP descending, m.NbLinesOfCode descending2627select new { m, CRAP, CC, uncoveredPercentage = uncov*100, m.NbLinesOfCode }
Notez que l’utilisation de nombreux laisser clauses dans la boucle principale de la requête peut réduire significativement les performances d'exécution de la requête.
Commencer une requête par let
Le compilateur PRQL étend l'utilisation de LINQ laisser mot-clé, car avec PRQL, le laisser mot-clé peut servir à définir une variable au début d'une requête PRQL.
Par exemple, la règle PRQL par défaut ci-dessous essaie d'abord de faire correspondre BaseClass types, et si trouvé, exécutez ensuite la requête.
1warnif count > 023let base = ThirdParty.Types.WithFullName("BaseClass").FirstOrDefault()45where base != null // base can be null if the code base doesn't use at all BaseClass67from t in Application.Types where89 !t.DeriveFrom(base) select t
Pour certaines autres règles PRQL, il peut être pratique de définir plusieurs sous-ensembles via plusieurs laisser expressions de mots-clés, avant d'exécuter la requête elle-même.
Par exemple, les règles PRQL par défaut ci-dessous définissent d'abord les sous-ensembles uiTypes et dbTypes avant de les utiliser dans le code de la requête.
1// <Name>UI layer shouldn't use directly DB types</Name>23warnif count > 045// UI layer is made of types in namespaces using a UI framework67let uiTypes = Application.Namespaces.UsingAny(89 Projects.WithNameIn("PresentationFramework", "MFC")1011 ).ChildTypes()1213// You can easily customize this line to define what are DB types.1415let dbTypes = ThirdParty.Assemblies.WithNameIn("Data").ChildTypes()1617 .Except(ThirdParty.Types.WithNameIn("DataSet", "DataTable", "DataRow"))1819from uiType in uiTypes.UsingAny(dbTypes)2021let dbTypesUsed = dbTypes.Intersect(uiType.TypesUsed)2223select new { uiType, dbTypesUsed }
Définir une procédure dans une requête
Avec la syntaxe LINQ, il est possible de créer une procédure dans une requête. Cela est utile si vous souhaitez invoquer cette procédure depuis différents endroits de la requête.
Cette possibilité est illustrée dans la règle par défaut ci-dessous, où la procédure pour vérifier si un type peut être considéré comme un type mort は 2 つの異なる場所から呼び出す必要があります:
1// <Name>Potentially dead Types</Name>23warnif count > 045// Filter procedure for types that shouldn't be considered as dead67let canTypeBeConsideredAsDeadProc = new Func<IType, bool>(89 t => !t.IsPublic && // Public types might be used by client applications of your assemblies.1011 t.Name != "ClassToExclude"1213// Select types unused1415let typesUnused =1617 from t in JustMyCode.Types where1819 t.NbTypesUsingMe == 0 && canTypeBeConsideredAsDeadProc(t)2021 select t2223// Dead types = types used only by unused types (recursive)2425let deadTypesMetric = typesUnused.FillIterative(2627types => from t in codeBase.Application.Types.UsedByAny(types).Except(types)2829 where canTypeBeConsideredAsDeadProc(t) &&3031 t.TypesUsingMe.Intersect(types).Count() == t.NbTypesUsingMe3233 select t)3435from t in deadTypesMetric.DefinitionDomain3637select new { t, t.TypesUsingMe, depth = deadTypesMetric[t] }
Essayez CppDepend aujourd'hui
Commencez votre essai gratuit de 14 jours avec accès complet à toutes les fonctionnalités de documentation. Sans carte bancaire.
