関数の戻り値を無視するコード(C++)のすべての場所を確認したいと思います。 gccまたは静的コード分析ツールを使用してそれを行うにはどうすればよいですか?
悪いコード例:
int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here I disregard the return value
return 1;
}
その点に注意してください:
GCCのwarn_unused_result
属性が必要です:
#define WARN_UNUSED __attribute__((warn_unused_result))
int WARN_UNUSED f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); ///// <<----- here i disregard the return value
return 1;
}
このコードをコンパイルしようとすると、次のようになります。
$ gcc test.c
test.c: In function `main':
test.c:16: warning: ignoring return value of `f', declared with
attribute warn_unused_result
これは Linuxカーネル ;で使用されていることがわかります。同じことを行う__must_check
マクロがあります。これを機能させるには、GCC3.4以降が必要なようです。次に、カーネルヘッダーファイルで使用されているマクロを見つけます。
unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
私の知る限り、この警告を出すGCCオプションはありません。ただし、特定の機能に関心がある場合は、次の属性でタグを付けることができます。
int fn() __attribute__((warn_unused_result));
これは、fn()の戻り値が使用されなかった場合に警告を出します。警告:私はこの機能を自分で使用したことがありません。
この便利なテンプレートを使用して、実行時に実行できます。
エラーコード(HRESULTなど)を返す代わりに、return_code <HRESULT>を返します。これは、値が読み取られずにスコープ外になった場合にアサートします。静的分析ツールではありませんが、それでも便利です。
class return_value
{
public:
explicit return_value(T value)
:value(value), checked(false)
{
}
return_value(const return_value& other)
:value(other.value), checked(other.checked)
{
other.checked = true;
}
return_value& operator=(const return_value& other)
{
if( this != &other )
{
assert(checked);
value = other.value;
checked = other.checked;
other.checked = true;
}
}
~return_value(const return_value& other)
{
assert(checked);
}
T get_value()const {
checked = true;
return value;
}
private:
mutable bool checked;
T value;
};
C++ 17の場合、 [[nodiscard]] 属性があるため、この質問に対する答えが変わります。 [dcl.attr.nodiscard] でカバーされています:
属性トークンnodiscardは、関数宣言のdeclarator-id、またはクラスまたは列挙型の宣言に適用できます。これは、各属性リストに最大1回表示され、属性引数句は存在しません。
そして
[例:
struct [[nodiscard]] error_info { /* ... */ }; error_info enable_missile_safety_mode(); void launch_missiles(); void test_missiles() { enable_missile_safety_mode(); // warning encouraged launch_missiles(); } error_info &foo(); void f() { foo(); } // warning not encouraged: not a nodiscard call, because neither // the (reference) return type nor the function is declared nodiscard
—例を終了]
したがって、例を変更します( ライブで参照 ):
[[nodiscard]] int f(int z) {
return z + (z*2) + z/3 + z*z + 23;
}
int main()
{
int i = 7;
f(i); // now we obtain a diagnostic
return 1;
}
Gccとclangの両方で診断を取得します。
warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
f(i); // now we obtain a diagnostic
^ ~
ここでは、静的アナライザーが最善の策です。ここではCoverityを使用していますが、 無料のツール も使用できます。
迅速で汚いソリューションが必要で、Linuxスタイルのシェルが手元にある場合は、次のような方法を試すことができます。
grep -rn "function_name" * | grep -v "="
これにより、指定された関数を参照しているが「=」を含まないすべての行が検索されます。多くの誤検知(および場合によっては誤検知)が発生する可能性がありますが、静的アナライザーがない場合は、開始するのに適した場所です。
静的分析コード(例: PC-Lint )は、それを伝えることができるはずです。 PC-Lintの場合、これが当てはまることを私は知っています。
静的アナライザーがあなたに代わって作業を行いますが、コードベースが些細なものである場合は、圧倒される準備をしてください;-)
従来の「lint」プログラムは、無視された値を返す関数について非常に不安定でした。問題は、これらの警告の多くが望ましくないことでした-糸くずの出力に過度のノイズが発生しました(無視したい綿毛を拾っていました)。おそらく、GCCに標準の警告がないのはそのためです。
もう1つの問題(裏側)は、「結果を無視していることがわかっているが、実際には気にしない場合に、警告をどのように抑制するか」です。そのための典型的なシナリオは次のとおりです。
_if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, sighandler);
_
signal()
からの最初の結果が気になります。 2番目がSIG_IGNになることを知っています(これに設定しただけなので)。警告から逃れるために、私は時々いくつかの変形を使用します:
_if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
old = signal(SIGHUP, sighandler);
_
これにより、両方の時間でold
に割り当てられます。 'assert(old == SIG_IGN)'でそれに従うことができます。