CppDependでPRQL構文をマスター
CppDependでPRQL構文をマスター
このドキュメントは、読者が次に精通していることを前提としています LINQ構文、PRQL構文の特徴を示します。
PRQL(Code Query LINQ)は、バージョン 3 以降の CppDepend ツールが提供する機能で、LINQ クエリを通じて C/C++ コードをクエリします。
入門ビデオ
PRQL 構文を紹介する 1 分 50 秒の短いビデオが、CppDepend ドキュメント サイトで公開されています。
PRQLクエリ編集
PRQL クエリは、CppDepend UI(スタンドアロンまたは Visual Studio 内)でライブ編集できます。

クエリはコンパイルされるとすぐに自動実行されます。
上のスクリーンショットに注目 13ミリ秒 右上に、クエリの実行時間を示します。
PRQL は高速で、大規模な実際のコード ベースに対して 1 秒間に数百のクエリを実行するように設計されています。
PRQLエディションにはも付属 コード補完/IntelliSense、そして ツールチップドキュメント クエリ本体にマウスホバーで。
定義済みドメイン
PRQL は、次を含むいくつかの事前定義ドメインを定義します: 型 ; メソッド ; フィールド ; 名前空間 ; プロジェクト
これらのドメインは、クエリ対象のコード ベースのすべてのコード要素だけでなく、次のすべても列挙します サードパーティ コード ベースで使用されるコード要素(たとえば型 string とその型のすべてのメソッドとフィールド string コードベースで使用されるもの)。
構文は次のようにシンプル:
1from m in Methods where m.NbLinesOfCode > 30 select m
PRQL クエリは 1 つまたは複数のドメインに依存できます。上のクエリで、ドメイン語がどのように メソッド 異なる強調表示。
よく使用される便利な事前定義ドメインが 2 つあります: アプリケーション と ThirdParty。名前が示すように、これらのドメインは、アプリケーションプロジェクトでのみ定義されたコード要素、またはサードパーティプロジェクトでのみ定義されたコード要素(例: STL, MFC または Boost) と アプリケーション コードによって使用されます。これら 2 つのドメインは、それぞれコード ベース全体の部分的なビューを表します。
1from m in Application.Methods where m.NbLinesOfCode > 30 select m
これらの定義済みドメインの絞り込みは簡単です。
たとえば、次のクエリは、定義された大きなメソッドに一致します のみ 名前空間内 ProductName.FeatureA とその子名前空間:
1from m in Application.Namespaces.WithNameLike("ProductName.FeatureA").ChildMethods()2where m.CyclomaticComplexity > 10 select m
notmycode プレフィックスによるコード ベース ビュー JustMyCode の定義
もう 1 つの便利な事前定義ドメインがあります JustMyCode.
ドメイン JustMyCode は、PRQL クエリ結果から生成されたコード要素を除外する PRQL の機能を表します。
たとえば、次のクエリは、ツール(UI デザイナーなど)によって生成されていない大きなメソッドのみに一致します:
1from m in JustMyCode.Methods where m.NbLinesOfCode > 30 select m
生成されたコード要素のセットは、PRQL キーワードで始まる PRQL クエリによって定義されます notmycode.
たとえば、次のクエリは、名前に特定の単語を含むソース ファイルで定義されたメソッドに一致します。
1notmycode from m in Methods where23 m.SourceFileDeclAvailable &&45 m.SourceDecls.First().SourceFile.FileName.ToLower().Contains("generated.cpp")67select m
PRQLクエリランナーはすべてを実行 notmycode に依存するクエリの前のクエリ JustMyCode、よってドメイン JustMyCode は一度だけ定義されます。当然、次の場合 PRQL コンパイラはエラーを出力します notmycode クエリはに依存 JustMyCode ドメイン。
PRQLコードルール
PRQL クエリは、2 つの PRQL キーワードで定義された条件を先頭に付けることで、簡単にルールに変換できます warnifカウント.
キーワード count は、クエリに一致したコード要素の数に等しい符号なし整数です。
たとえば、次のクエリは、コード ベースのアプリケーション メソッドで大きなメソッドが一致した場合に警告します:
1// <Name>Avoid too large methods</Name>23warnif count > 045from m in Application.Methods67where m.NbLinesOfCode > 3089select m

PRQL コード ルールは、チームがコード ベースで避けたい悪いプラクティスを定義するのに役立ちます。
チームは、CppDepend UI(スタンドアロンまたは Visual Studio 内)でコード ルール違反の警告を確認できます。また レポート内.
チームは一部のルールを次のように定義することもできます 重大ルール.
クエリ演算子とクエリ式の構文
PRQL では、クエリ演算子構文とクエリ式構文の両方が使用できます。
クエリ演算子構文は、次のようなメソッドを直接呼び出す構文です Where() と Select()...
1Methods.Where(m => m.NbLinesOfCode > 30)
クエリ式の構文には次のようなキーワードがあります where と 選択...
1from m in Methods where m.NbLinesOfCode > 30 select m
多くの場合、1 つのクエリで両方の構文を混在させると便利です。
- クエリ演算子構文は、サブセットやサブドメインを定義するのに便利です。
- クエリ式構文は、これらのサブセットやサブドメインに対する操作を定義するのに便利です。
たとえば、以下のクエリは、クエリ演算子構文で静的型に定義されたメソッドのサブセットを定義し、クエリ式構文を使用してこのサブセットから大きなメソッドをフィルタリングおよび射影します。
1from m in Application.Types.Where(t => t.IsNested).ChildMethods()2where m.NbLinesOfCode > 30 select m
letで範囲変数を定義
LINQ 構文では、キーワードを使用して範囲変数を定義できます させる このセクションでは、この可能性を強調したいと思います。次を使用すると させる キーワードは、PRQL クエリを記述する際の一般的な手法です。
たとえば、次のデフォルト ルールは、複数の範囲変数を使用してカスタム コード メトリックを定義します:
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 }
多数の使用に注意 させる メイン クエリ ループ内の句は、クエリ実行のパフォーマンスを大幅に低下させる可能性があります。
letでクエリを開始
PRQL コンパイラは LINQ の使用を拡張します させる キーワード。PRQLでは、 させる キーワードを使用して、PRQL クエリの先頭で変数を定義できます。
たとえば、以下のデフォルト PRQL ルールは、まず次をマッチさせようとします BaseClass 型。見つかった場合は、次にクエリを実行します。
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
他の PRQL ルールでは、複数の次を通じて複数のサブセットを定義すると便利な場合があります させる クエリ自体を実行する前に、キーワード式を処理します。
たとえば、以下のデフォルト PRQL ルールは、まずサブセットを定義します uiTypes と dbTypes クエリコードで使用する前に。
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 }
クエリでプロシージャを定義
LINQ 構文を使用すると、クエリ内でプロシージャを作成できます。これは、クエリ内のさまざまな場所からそのようなプロシージャを呼び出したい場合に便利です。
この可能性は、次のデフォルト ルールで示されています。そこでは、型が次かどうかをチェックする手順が デッド型と見なせる doit être invoqué depuis deux emplacements différents :
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] }
