2017-11-14 9 views
2

私はテストコードを解析するためにPVS-Studioを使用しています。私はまだ最後の行に対する警告V522 There might be dereferencing of a potential null pointer 'animal'を取得ただし、フォームPVS-StudioのBOOST_REQUIREの後に変数にNULLがないとマークします

const noAnimal* animal = dynamic_cast<noAnimal*>(...); 
BOOST_REQUIRE(animal); 
BOOST_REQUIRE_EQUAL(animal->GetSpecies(), ...); 

の構築物は、しばしばあります。

私はそれが「NULLを返さない」などの機能をマークすることが可能である知っているが、有効なNULLチェックとしての機能をマークするか、何らかの形で他のanimalBOOST_REQUIRE(animal);後にNULLにすることはできないことに注意してくださいPVS-メーカーを作ることも可能です?

これは、先にassertフレーバーでポインタがチェックされている場合にも発生します。

答えて

1

ありがとうございます。私たちは、BOOST_REQUIREマクロでできることを考えています。

瞬間、私はあなたの次のソリューション助言することができます:あなたが書くことができます

どこか

#include <boost/test/included/unit_test.hpp> 

後に:

#ifdef PVS_STUDIO 
    #undef BOOST_REQUIRE 
    #define BOOST_REQUIRE(expr) do { if (!(expr)) throw "PVS-Studio"; } while (0) 
#endif 

この方法は、あなたがにヒントを与えるだろうアナライザは、誤った条件によって制御フローが中止されることを示します。 これは最も美しい解決策ではありませんが、私はそれがあなたに伝える価値があったと思います。大1とコメントへの対応

+0

返信いただきありがとうございます。これは可能ですが、その定義をすべてのテストケースファイルに含めることは苦労でしょう。また、これは 'BOOST_REQUIRE'だけに限定されず、' assert'や 'SDL_Assert'やその他のカスタムマクロにも適用されます。だから私は2つの解決策を見てみましょう:ユーザーが "assert" -macro名のリストを指定できるようにしてください。その後、条件がtrueであると仮定されます(それがアサートの理由です)。 – Flamefire

+0

少なくとも、グローバルなPVS専用のファイルがあり、PVSによって読み込まれ、使用されることが想定されています( 'cpp.hint'に似ています)。したがって、そのような定義を中央の場所に置くことができます。しかし、私はこれが難しいと思います。なぜなら、定義はすべてのファイルに含まれるからです。 – Flamefire

1

はので、ここで、悪い考えで、以下のテーマに関する私の詳細な応答である:

これは可能ですが、すべての に定義する含めるように痛みだろうテストケースファイル。また、これはBOOST_REQUIREに限定されず、 は、アサート、SDL_Assert、またはユーザー が使用するその他のカスタムマクロにも適用されます。

一つは、テスト・マクロの3種類があることを理解する必要があり、それぞれが個別に議論されるべきです。

最初のタイプのマクロは、デバッグバージョンで何か問題が発生したことを警告するだけです。典型的な例はassertマクロです。次のコードは、警告を生成するために、PVS-Studioのアナライザを引き起こします:

T* p = dynamic_cast<T *>(x); 
assert(p); 
p->foo(); 

アナライザは、ここでは逆参照可能ヌル・ポインタを指摘すると、右になります。 assertを使用するチェックは、リリースバージョンから削除されるため、十分ではありません。つまり、チェックがないことが分かります。実装するより良い方法は、コードを次のように書き換えることです。

T* p = dynamic_cast<T *>(x); 
if (p == nullptr) 
{ 
    assert(false); 
    throw Error; 
} 
p->foo(); 

このコードは警告をトリガーしません。

あなたは、dynamic_castが決してnullptrを返さないことを100%確信していると主張できます。私はこの議論を受け入れません。キャストが常に正しいことを完全に確信している場合は、より速くstatic_castを使用する必要があります。それが確実でない場合は、逆参照する前にポインタをテストする必要があります。

さて、わかりました、あなたの意見があります。コードは問題ないと確信していますが、場合によってはdynamic_castでチェックする必要があります。 OK、その後、次のコードを使用します。

assert(dynamic_cast<T *>(x) != nullptr); 
T* p = static_cast<T *>(x); 
p->foo(); 

を私はそれが好きではありませんが、アナライザは黙ってする一方、より遅いdynamic_cast演算子は、リリースバージョンに取り残されるので、少なくともそれは、高速です。

次のタイプのマクロに移動します。

マクロは、デバッグバージョンで何か問題が発生し、テストで使用されていることを警告します。以前のタイプと異なる点は、条件が偽でエラーメッセージが生成された場合、テスト中のアルゴリズムを停止することです。

これらのマクロの基本的な問題は、関数が非復帰としてマークされていないことです。ここに例があります。

例外をスローしてエラーメッセージを生成する関数があるとします。

void Error(const char *message); 

そして、これは、テストマクロが宣言されている方法です:

#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0) 

ポインタを使用する:

T* p = dynamic_cast<T *>(x); 
ENSURE(p); 
p->foo(); 

アナライザはに関する警告が発行されますこれは、その宣言は次のようになりますnullポインタの逆参照が可能ですが、コードは実際に安全です。ポインタがNULLの場合、Error関数は例外をスローし、ポインタの逆参照を防止します。

私たちは、単に例えば、機能注釈手段のいずれかを使用して、そのことについて解析を伝える必要があります:

[[noreturn]] void Error(const char *message); 

か:

__declspec(noreturn) void Error(const char *message); 

これが偽の警告を排除するのに役立ちます。したがって、あなたが見ることができるように、あなた自身のマクロを使用している場合、ほとんどの場合、事を修正するのは非常に簡単です。

しかし、サードパーティ製のライブラリから不注意に実装されたマクロを処理するのは難しいかもしれません。

これは、3番目のタイプのマクロにつながります。それらを変更することはできず、アナライザは正確にどのように動作するか把握できません。これは、マクロが非常にエキゾチックな方法で実装される可能性があるため、一般的な状況です。

このケースでは、あなたのために残され、3つのオプションがあります:

  1. が偽陽性抑制のいずれかを使用して警告がdocumentationに記載の手段抑えます。
  2. は、前のanswerで説明した手法を使用します。
  3. メールでお問い合わせください。

一般的なライブラリからさまざまなトリッキーなマクロのサポートが徐々に追加されています。実際には、アナライザーは遭遇する可能性のあるマクロの大部分をすでによく知っていますが、プログラマーの想像力は無尽蔵であり、可能なすべての実装を予見できません。

+0

私はあなたにdynamic_castの話に同意しません。まず、dynamic_cast(複数の継承など)が必要な場合もありますが、失敗しないことが確実です(たとえば、仮想関数が型を返すなど)。または、第2にあなたはそれが有効であると主張しますが、UDの代わりにリリースモードでNull-Pointer-Exceptionを得るようにdynamic_castを保持します。これは、余分なチェックをして、(すべきである)不可能な場合をスローするよりも、IMOに優れています。 (例えば、関数が型を返すので、キャスト可能であるべきですが、その関数のC&Pエラーから保護したいと思っています)トリッキーなマクロ:そのため、 – Flamefire

関連する問題