6

プログラムが 64 ビット コードとして適切に実行されないようにするエラーを検出するのに役立つツールを探していました。ごく最近、私は Klocwork とそのカスタム チェッカー機能をいじっていました。これにより、XPath を使用してソース コードをツリーとしてナビゲートできます。これは、正規表現の「よりスマートな」代替手段として役立ちますが、型を認識させることはできませんでした。

たとえば、カウントにanまたは aをfor使用するループのすべてのインスタンスを見つけたいとします。次のコードは簡単に見つけることができます。intlong

for (int i = 0; i < 10; i++)
    // ...

変数定義はループ内にあるため、このコードの検索は簡単です。ただし、次の例を検討してください。

int i;
// ...
for (i = 0; i < 10; i++)
    // ...

変数定義がループから分離されており、必要な XPath 式が扱いにくく、バグが発生しやすいため、これを見つけるのは困難です。

では、カスタム Klocwork ルールは、解決typedef#defineステートメントなど、型認識が必要な場所でこのような式を見つけることができますか? これを行うことができる他のツールはありますか?

編集 1:次の例を検討してください。

typedef int myint;

void Foo() {
    int i;
    for (i = 0; i < 10; i++) {
        Bar();
    }

    myint j;
    for (j = 0; j < 10; j++) {
        Bar();
    }
}

の型がとして明示的に定義されているため、 ahmeddirie によって提供されるソリューションは最初のループを見つけます。ただし、typedef によって基になる型が隠されているため、2 番目のループは見つかりません。2 番目のループ変数が実際に であることを識別する方法で型を追跡するツールは?iintjint

4

3 に答える 3

2

タイプの伝播とテンプレートのインスタンス化の後に AST を生成するために、Clang ( http://clang.llvm.org ) または Elsa ( https://github.com/dsw/oink-stack/ ) を使用できます。どちらも適切な C++ API と、AST を読み取り可能なテキストにダンプする手段を提供しています。どちらのオプションも無料です。

于 2011-06-23T09:18:48.483 に答える
1

これがあなたが望むものかどうかは完全にはわかりませんが、組み込み関数を使用すると、いつでも簡単に型を解決できます。たとえば、あなたの質問に答えます (おそらくあなたの根本的なニーズではないかもしれませんが):

//ForStmt / Init::ExprStmt / Expr::BinaryExpr [ $type := Left.getTypeName() ] [ $type = 'int' | $type.contains('long') ]

これにより、'int' または 'long int' カウンター タイプを使用する 'for' ループが非常に便利に検出され、明らかに式ベースのステートメントの任意の要素に適用できます。

型定義は、プログラマー定義か言語定義かにかかわらず、この種の操作に適しています。ただし、プリプロセッサの定義は、ネイティブ言語の型のみを生成します (つまり、マクロ自体は KAST を介して操作することはできず、展開されるもののみを使用できます)。

于 2011-06-23T15:13:15.197 に答える
1

私が勤務している Semantic Designs Inc. は、プログラムの分析と変換のための一般的なインフラストラクチャと、さまざまなプログラミング言語用の特定の分析コンポーネントを組み込んだツールを提供しています。これらをまとめて DMS と呼びます。C++ の場合、DMS には、GCC3、GCC4、ISO14882c1998 (ANSI)、Visual C++ 6.0、およびアンマネージ Visual Studio 2005 C++ のそれぞれについて、統合されたレクサー、プリプロセッサ、パーサー、および名前と型の解決コンポーネントが含まれています。C のさまざまな方言には、制御フロー解析、副作用アナライザー、シンボル依存関係アナライザーもあり、ポインター チェッカー、非アクティブ コード リムーバー、関数プロファイラー、プログラム スライサーなどのツールが実装されています。

名前と型の解決コンポーネントは、完全なシンボル テーブル情報とルックアップ機能を提供するため、識別子への参照を型やその他の宣言情報に簡単に関連付けることができます。この情報は、コンパイラによって取り込まれて使用されるものと似ていますが、抽象構文ツリーと共に、コンポーネントを組み込んだツールによる適応型再利用に適した形式で保持されます。

Semantic Designs は最近、あなたの例のように、ループ宣言のインデックス変数の型に特に関連するカスタム ツールを構築しました。この場合の問題は、-fno-for-scope コンパイラ スイッチを使用する GCC2 コードをアップグレードすることでした。これにより、後の GCC 方言ではサポートされていないループ変数のスコープ解決規則が提供されました。このツールは、ループ変数の宣言を -fno-for-scope スコープ規則を保持する外部コンテキストに移動して、ループを変換する必要がありました。そのような変更が必要でない場合、変更は行われませんでした。

したがって、ツールはループ変数への各参照に関連付けられた型を識別し、スコープをマスキングする場合に区別し、コードを再構築して、GCC3 および GCC4 の名前解決が -fno を使用した GCC2 と同じ意味解釈になるようにする必要がありました。 -スコープ用。これには、各変数参照に関連付けられたシンボル テーブル情報にアクセスできる必要があり、コードが移動された場合は、宣言が移動された変数の型宣言の正しい構文を再構築する必要がありました。DMS C++ の名前および型解決コンポーネントによって提供されるシンボル テーブルおよび識別子参照テーブルには、必要なすべての情報が含まれており、規定の型構文を再構築するためのモジュールにより、正しい新しい型宣言の合成が可能になりました。

たとえば、次の例を考えてみましょう。

// loop variable hides variable in global scope
// will change meaning without -fno-for-scope
// fix: move decl. of cnt before for-loop
//   optionally rename globcnt loop variable

float globcnt = 0.0;

int Foo::foo3() {
    for (int globcnt = 0; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

GCC2 -fno-for-scope セマンティクスは、GCC3 がループ変数をスコープ外と見なし、グローバル変数への参照を解決する場合でも、ループ外の globcnt への参照がループ変数へのものであることを示します。このツールは、このコードを次のように変換しました。

float globcnt = 0.0;

int Foo::foo3() {
    int globcnt = 0;
    for (; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

コードが変換されていなければ、GCC4 は常に Foo:foo3 から値 1 を返します。ただし、変換された値は、もともと GCC2 用に設計されたループの反復の影響を受けていたはずです。ツールは、globcnt への最終参照が float 型のグローバル変数ではなく、int 型のローカル変数に対するものであることを認識する必要がありました。これは、シンボル テーブル ルックアップを介して行うことができ、それに応じて動作する必要がありました。

一方、ツールは次のコードで i への参照がループの外側にないことを認識したため、ループ変数の宣言をそのままにしておくことが許容されました (そして優先されました)。

int Foo::foo0() {
    for (int i = 0; i < 10; i++) {
        globalInt += i*i;
    }
    return 0;
}
于 2011-06-22T22:48:30.527 に答える