2021年12月16日木曜日

Visual Studio でライブラリなど外部コードの警告を出さないようにする方法

これは「C++のカレンダー | Advent Calendar 2021 - Qiita」の16日目の記事です。

はじめに

警告はときにプログラムの問題を教えてくれます。
警告は基本的にないのが望ましいです。なぜなら、野原から四葉のクローバーを探し出すのは大変だからです。(見つけた四葉は幸運をもたらすか、不幸をもたらすか。。見つけるなら早いほうがいいですよ)

CI/CD 担当をやっていると警告を直してもらうための対応とかしたりします。
警告出てたらステータスを変えて通知とか、ちょっとずつ警告をエラーにしていくとか。
ここで問題になるのは、自分たちが改変することができない SDK やライブラリの警告です。
通知で頑張ってる場合はフィルターでなんとかなるかもしれません。
as Error にしている場合は困ります。

そんな場合は自分たちのコード以外をシステムインクルード(#include <...>)し別途警告レベルを制御することで解決できます。

Visual Studio で外部コードの警告を制御する

Visual Studio では 2017 (15.6) からシステムインクルードの警告レベルを別途設定できるようになりました。「/external (外部ヘッダー診断) | Microsoft Docs」こちらの公式ドキュメントにもありますが 2017 では試験機能となっています。/external オプションに加えて /experimental:external オプションも必要になるので注意してください。2019 からは正式機能となっており、設定も専用のページができました。

さて、これを利用すれば外部コードはシステムインクルードとすることで警告がでないようにできます。試しに以下のコードで試してみました。

// ConsoleApplication3.cpp : このファイルには 'main' 関数が含まれています。プログラム実行の開始と終了がそこで行われます。
//
#include <iostream>
#include "test.h"
//#include <test.h>
int main()
{
std::cout << "Hello World!\n";
}
// プログラムの実行: Ctrl + F5 または [デバッグ] > [デバッグなしで開始] メニュー
// プログラムのデバッグ: F5 または [デバッグ] > [デバッグの開始] メニュー
// 作業を開始するためのヒント:
// 1. ソリューション エクスプローラー ウィンドウを使用してファイルを追加/管理します
// 2. チーム エクスプローラー ウィンドウを使用してソース管理に接続します
// 3. 出力ウィンドウを使用して、ビルド出力とその他のメッセージを表示します
// 4. エラー一覧ウィンドウを使用してエラーを表示します
// 5. [プロジェクト] > [新しい項目の追加] と移動して新しいコード ファイルを作成するか、[プロジェクト] > [既存の項目の追加] と移動して既存のコード ファイルをプロジェクトに追加します
// 6. 後ほどこのプロジェクトを再び開く場合、[ファイル] > [開く] > [プロジェクト] と移動して .sln ファイルを選択します
#pragma once
struct A {
int f(int x) {
if (x); // C4390
{
return 42;
}
return 41;
}
};
struct S {
void member_fn(unsigned u) {
double x = 0;
for (int x = 0; x < 10; ++x) { // C4456
u += x; // uses local int x
}
x += u; // uses local double x
}
} s;
view raw test.h hosted with ❤ by GitHub

/external の設定

プロジェクトプロパティの「C/C++」→「外部インクルード」を開き、 /external:W0 を設定します。コード分析も外部インクルードに対して別途制御可能なので、有効にしている場合は設定しましょう。

外部インクルードディレクトリにパスを追加している場合は、システムインクルードすれば外部インクルードとして認識されます。もしただのインクルードディレクトリにパスを追加している場合は、/external:anglebracket オプションを有効にすることで外部インクルードとみなされます。(システムインクルードすれば警告でなくなると逃げ口として意図しない対応をされる可能性があるので、インクルードディレクトリの設定を変更するのをオススメします)

システムインクルードではない場合

C4390 と C4456 が出ます。

システムインクルードの場合

システムインクルードにすると警告が消えました!

消えない警告もある?

たまたま見つけたのですが、システムインクルードしたファイルの警告でも /external:W0 が効かずに警告されるケースがありました。(こういう書き方をすることはない気もしますが)
コードはこちらです。gcc/clang では警告されませんでした。(https://wandbox.org/permlink/kPn2z1MLCAvbOBny

↑は C4390/C4702 の2つ。↓は C4390 は消えるが、C4702 は消えない。

Visual Studio でシステムインクルードヘッダーの警告を厳しくする

逆にシステムインクルードの警告レベルを上げるとどうなるのでしょうか?
標準ライブラリはシステムインクルードしていると思いますが、それらのコードに警告はあるのか・・? /external:W4 にして試してみました。


結果としては警告は出ない、が警告があるのかないのかはわからない。でした。
というのも VS2017 より前から標準ライブラリの警告って見た記憶がないため(たぶん)、別途制御されているのでは?と思ったからです。
ちなみに後述してますが gcc/clang では(とあるコンパイルオプションをつけると)標準ライブラリからも警告が出ていたこともあります。
MSVC の STL は公開されているので、暇なときにビルドしてみて確認してみようかと思います。
https://github.com/microsoft/STL

gcc/clang の場合

gcc/clang の場合は古くからシステムインクルードの警告は除外されていました。
(Visual Studio が今までできてなくて 2017 でようやくという感じでした)
Wandbox で試してみたのでリンクを貼っておきます。
コードは Gist にも置いてあります。

システムインクルードではない場合

警告でます。
gcc: https://wandbox.org/permlink/MLrLASaM5o5A3UWm
clang: https://wandbox.org/permlink/x6TT2NFROSmyLhV4


システムインクルードの場合

警告でません。
gcc: https://wandbox.org/permlink/XVHXnWIMyOfDMjl3
clang: https://wandbox.org/permlink/strTYwduT4ZLH5NK

gcc/clang でシステムインクルードヘッダーの警告を有効にする

gcc/clang でもシステムインクルードの警告を有効にできます。-Wsystem-headers オプションを使います。
試しにしてみると・・

新しいコンパイラーと標準ライブラリの組み合わせだと警告でませんでした。
古いとそこそこ出ます。

gcc 9.1.0: https://wandbox.org/permlink/lhXWuNvWEJdKwJNh
gcc 8.3.0: https://wandbox.org/permlink/4dHKZKtdqmDGmXNU 
clang 6.0.0: https://wandbox.org/permlink/MaVYzrGDeT79eMZa
clang 5.0.0: https://wandbox.org/permlink/qjbmV9U2c8MxHe6A

まとめ

システムインクルードを使って外部コードの警告を除外、自身のコードの警告を取り除く/取り除いてもらうように自動化/自働化しましょう。

0 件のコメント:

コメントを投稿