VSCodeでclangのclangdでclang-tidy

### clang-tidyとは
https://qiita.com/syoyo/items/0e75410c44ed73d4bdd7
要するに、問題のあるコードを報告したり直してくれるツール

# VSCodeでclang-tidyを使うには
clangdを使う
https://clang.llvm.org/extra/clangd/

clang-tidyとclangdは別々のツールだったが、
LLVM 9.0からclangdからclang-tidyの機能を使えるようになった。

以下、Windows10で話を進めます。

#### clang-tidyを手に入れる
LLVMをインストールするとclang等と一緒についてくる。

LLVM公式サイト
http://llvm.org/
公式サイトにある`Download!`の下にある`LLVM 9.0.0`（執筆時点）をクリック。
今回は`Windows (64-bit)`をダウンロード。
インストールして環境変数`Path`にLLVMのインストールディレクトリを追加する。

#### VSCodeに拡張を入れる
`vscode-clangd`という公式が出している拡張を入れる。
`VSCode左下の歯車マーク`から`設定`を開く。
`拡張機能`の`clangd configuration`以下にある
`Clangd: Arguments`に`-clang-tidy`
`Clangd: Path`に`clangd.exe`を入れる。
![{03924299-31F1-4BE0-85C2-F918EDAF032B}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321399/ef2f75e6-7c20-f08d-3917-bf80e561a985.png)


# clangdとclang-tidyに必要なファイル
## clangdに必要なファイル
`compile_commands.json`または`compile_flags.txt`
これらのファイルにはコンパイルに使用するフラグが保存されている。
どちらかのファイルをプロジェクトルートに置く。
両方のファイルが存在する場合、`compile_flags.txt`が使用されるようだ

#### compile_commands.json
`compile_commands.json`はCMakeで`CMAKE_EXPORT_COMPILE_COMMANDS`フラグを使うと生成できる。
だたしこの方法ではソースファイルに関する情報しか出力されず、ヘッダファイルの編集にclangdを使えない。
`compile_commands.json`にヘッダー情報を含めたいならPythonの`compdb`を使う。
この方法では`プロジェクトルート/.clangd/index`にインデックス情報が作成される。

#### compile_flags.txt
`compile_flags.txt`の場合は一行ごとにフラグを指定する。
すべてのファイルで同じフラグを使用するので作成は楽。
ただし`compile_commands.json`と違ってファイルごとのフラグを指定できない。
この方法ではインデックス情報は作成されない。

#### clangdをインストールしたら
設定からC/C++拡張（Microsoftが提供してるやつ）の項目を開きインテリセンスをオフにします。
`C_Cpp: Intelli Sense Engine`を`Disable`
`C_Cpp: Formatting`を`Disable`(clangdからclang-formatを呼び出せる)

###### おまけ
`compile_flags.txt`から`compile_commands.json`を生成するPythonスクリプトを作ってみた。
[flags_to_db.py](https://github.com/araki-syun/flags_to_db)

## clang-tidyに必要なファイル
`.clang-tidy`
YAML形式で記述する。

これらのファイルはプロジェクトのルートディレクトリに置く。

# `.clang-tidy`の要素
``` yaml:.clang-tidy
Checks: "-*,\
readability-*,\
-readability-convert-member-functions-to-static,\
-readability-uppercase-literal-suffix,\
-readability-static-accessed-through-instance,\
"
WarningsAsErrors: ''
HeaderFilterRegex: 'source.*\.(hpp|h)'
AnalyzeTemporaryDtors: false
FormatStyle:     file
CheckOptions:
  - key:   readability-identifier-naming.ClassCase
    value: CamelCase
  - key:   readability-identifier-naming.StructCase 
    value: CamelCase
```

##### Checks
有効化するチェックと無効化するチェックを記述する。
`*`アスタリスクはすべてのチェック項目を表す。
`-`ハイフンは続くチェック項目を無効化する。
基本的にはチェックグループを記述して、不要なチェック項目を除外する。
[チェックグループ] (https://clang.llvm.org/extra/clang-tidy/index.html#id2)

上記の場合、最初にすべてを無効化 ⇒ `readability-`から始まるチェックを有効化 ⇒ `readability`から一部のチェックを無効化、という流れとなっている。

##### WarningsAsErrors
警告をエラーとするか否か。

##### HeaderFilterRegex
このフィルターに一致するヘッダーから警告を出力する。

##### AnalyzeTemporaryDtors
デストラクタに関するものらしい

##### FormatStyle
`file`を指定しても、いまいち効果が分からない。（要検証）

##### CheckOptions
チェック項目のオプションを設定する
`key` チェック項目名
`value` 設定値

例えば`readability-identifier-naming`は命名規則を検証する。
`readability-identifier-naming.ClassCase`では以下の文字列を設定できる。

- lower_case
- UPPER_CASE
- camelBack
- CamelCase
- camel_Snake_Back
- Camel_Snake_Case
- aNy_CasE

効果は名前通り。

# clang-tidyの局所的無効化
ここまでclang-tidyを使う方法を書いてきたが、時として使いたくないケースに出てくるだろう。
例えば

``` cpp
template <class BASIC_JSON_TYPE>
void to_json(BASIC_JSON_TYPE& j, const FOO& foo){
    j["bar"] = foo.bar;
}
```
このテンプレート関数は[nlohmann/json](https://github.com/nlohmann/json)というjsonライブラリにおいて、自作クラスをjsonオブジェクトに代入できるようにする関数なのだが、関数名を`lower_case`にしなくてはならない。
`readability-identifier-naming.FunctionCase`を`lower_case`以外にするとclang-tidyは`to_json`という名前をfixするよう提案してくるだろう。
かといって命名規則を変更すると全ての関数名に影響が及ぶ。
そこで使うのが`// NOLINT` `// NOLINTNEXTLINE`コメントだ。

``` cpp
template <class BASIC_JSON_TYPE>
void to_json(BASIC_JSON_TYPE& j, const FOO& foo){ //NOLINT
    j["bar"] = foo.bar;
}
template <class BASIC_JSON_TYPE>
// NOLINTNEXTLINE
void from_json(const BASIC_JSON_TYPE& j, FOO& foo){
    foo.bar = j.at("bar").get<int>();
}
```
`NOLINT`は当該行
`NOLINTNEXTLINE`は次の行でclang-tidyのチェックを無効化する。

## 使ってみて
ファイル内全ての問題をFixするには、ターミナルからclang-tidyを呼ぶしかないのだろうか