gccでuninitializedなdoubleを参照渡しした場合にwarningが出ない問題

## まとめ

- gccでuninitializedなdoubleを参照渡ししてもwarningが出ない
- cppcheckを使うと検知できることもあるが、検知できないこともある
- clang-tidy, clang analyzerでは検知できない

## 発端

以下のコード。
test2はコンパイルエラーになるが、testはパスしてしまう。
valgrindなら検出できるが、コンパイルエラーで検知してほしい。

```cpp
#include <algorithm>

#if 1
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif
```

コンパイル結果 (-Werror -Wall)
https://godbolt.org/z/D3L2oC

## 試行錯誤

### std::maxのせいではないか？

std::maxではなく、自前で似たような関数を作ってみると、-Wallで検知できる。

```cpp
#include <algorithm>

#if 1
int mymax(int a, int b) {
    return a > b ? a : b;
}
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
int test3() {
    int a = 3;
    int b;
    return mymax(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif
```

コンパイル結果
https://godbolt.org/z/a6kmmm

### const 参照渡しだと通ってしまった

```cpp
#include <algorithm>

#if 1
int mymax(const int &a, const int& b) {
    return a > b ? a : b;
}
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
int test3() {
    int a = 3;
    int b;
    return mymax(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif
```

コンパイル結果
https://godbolt.org/z/mXlKwm

### const ポインタ渡しだとパスした

```cpp
int max(const int *a, const int *b) {
    return *a > *b ? *a : *b;
}

int test() {
    int a;
    int b = 3;
    return max(&a, &b);
}
```

コンパイル結果
https://godbolt.org/z/CtRYmQ

### 参照渡しでコンパイルエラーにするにはどうすればいいか？

関連記事
https://stackoverflow.com/questions/32264463/w-flag-for-gcc-to-detect-taking-const-pointer-of-uninitialised-variable

-O2をつけてみたら、エラーになった
std::max: https://godbolt.org/z/elwQsT
const参照: https://godbolt.org/z/3-M74q
constポインタ: https://godbolt.org/z/BcbtE_

結論、最適化オプションをつけていれば問題なかった

### と思ったが、doubleだとパスする

```cpp
const double &mymax(const double &a, 
const double &b) {
    return a > b ? a : b;
}

double test(double x) {
    double a = 3;
    double b;
    return mymax(a, b);
}
```

コンパイル結果
https://godbolt.org/z/EodiVt

発端はdoubleだったので、困る

### 値渡しだと？

```cpp
double mymax(double a, double b) {
    return a > b ? a : b;
}

double test(double x) {
    double a = 3;
    double b;
    return mymax(a, b);
}
```

エラーになる。

コンパイル結果
https://godbolt.org/z/luJrGq

### どうする？

cppcheckで検知できました。
http://cppcheck.sourceforge.net/

## cmakeにcppcheckを組み込む方法

### 基本

https://stackoverflow.com/questions/48625499/cppcheck-support-in-cmake

### cppcheckのエラー時にcmakeのビルドをエラーにする方法

#### 背景

cppcheckはデフォルトだとエラーでもexit statusは0になる。
--error-exitcode=X オプションを使うとexit statusをエラー時のXにできる。
しかし、これを指定してもcmake経由で使うとエラーにならない。

#### 解決策

以下のプルリクを発見。
https://gitlab.kitware.com/cmake/cmake/merge_requests/2459

2018/10/12にmasterにマージされている。

多分最新版のcmakeを使えばOK
(具体的にどのバージョンから含まれているかは未調査)

### 必要なマクロを定義する

cppcheckは普通のコンパイラで暗黙に定義されているマクロが無いので、OSごとに分岐しているコードなどで、どのOSにも該当しないケースでこけることがある。

以下でかいけつ
https://srz-zumix.blogspot.com/2012/03/cppcheck.html

### 特定のエラーや特定のファイルを除外する

以下でかいけつ
https://stackoverflow.com/questions/37609705/cppcheck-how-to-skip-a-directory-of-third-party-header-files

## 悲報

発端となった実際のコードにcppcheckをかけたら、なぜかuninitialized doubleの参照渡しを検知できませんでした・・・

この用途ではcppcheckは使えませんでしたが、他のバグを検出できたのでよしとします。

-> gcc + -O2 + -ffast-mathで検知できました。詳細はコメント参照

## 他のstatic analyzer

uninitialized doubleの参照渡しを検知できるstatic analyzerを探します。

### clang-tidy

検知できなかった

### clang-analyzer

検知できなかった

### sonarqube

調べていない