2023/08/15
clang-tidyの説明のtypoを修正
やること
- dockerコンテナ上にclangを使った環境を作成
-
Hello world
プログラムの作成とコンパイル、実行 - clang-formatを用いたコードの整形
- clang-tidyを用いた静的解析
clangとは
クランと読みます。
CやC++向けのコンパイラフロントエンドです。
バックエンドのLLVMと合わせて、C言語で書かれたプログラムから実行ファイルを生成します。
環境を作る
Dockerはすでにインストール済みの前提で説明します。
まだの人はこちらを参照。
https://docs.docker.jp/engine/installation/
docker containerの作成
以下のコマンドを実行して開発用コンテナを作成し接続します。
# 適当な開発用ディレクトリに移動
cd "path/to/working/dir"
# ベースイメージはなんでもいい。今回はdevianのslim版
docker pull debian:bookworm-slim
# コンテナを作成
docker run -dit -v $(pwd):/work -w /work --name clang-dev debian:bookworm-slim
# コンテナに入る
docker exec -it clang-dev bash
コンテナに入ったらパッケージをインストールします。
必要なのはこれ。
- clang
- コンパイル時に使います
- clang-format
- フォーマッタを使って整形します
- clang-tidy
- 静的解析に使います
apt update
apt install -y clang clang-format clang-tidy
最後にインストールできているかを確認するためにバージョン確認しましょう。
clang -v
clang-format --version
clang-tidy --version
以上で環境構築は完了です。
Hello worldの実行
作業ディレクトリに以下のファイルを作成します。
hello_worldしていないのは内緒。
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void) {
int i;
char input;
printf("数字を入力してください\n>> ");
input=getchar();
if(isdigit(input)){
i=input-'0';
}
printf("入力されたのは %d です。\n", i);
return 0;
}
コンテナ内のコマンドラインから以下のコマンドでコンパイルを実施します。
clang hello_world.c -o hello_world.out
hello_world.out
というファイルができればOK。
最後にこのhello_world.out
を実行します。
./hello_world.out
# 動作例
# 数字を入力してください
# >> 1
#入力されたのは 1 です。
以上でコンパイルと実行は完了です。
clang-formatを用いたコードの整形
ここで先ほど使用したコードをもう一度見て見ましょう。
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void) {
int i;
char input;
printf("数字を入力してください\n>> ");
input=getchar();
if(isdigit(input)){
i=input-'0';
}
printf("入力されたのは %d です。\n", i);
return 0;
}
インデントもされていないし、ちょっと見づらいですね。
ここでclang-format
の出番です。
次のコマンドを実行して、もう一度hello_world.cを開いてみます。
clang-format -i hello_world.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i;
char input;
printf("数字を入力してください\n>> ");
input = getchar();
if (isdigit(input)) {
i = input - '0';
}
printf("入力されたのは %d です。\n", i);
return 0;
}
このように適切にインデントされ、見やすくなったと思います。
.clang-format
ファイルに設定を記述すると{}
の位置やタブの文字数などを好きに設定することができます。
参考:clang-formatをイイ感じに設定する
clang-tidyを用いた静的解析
上のコード、ちゃんと動いたと思うのですがバグが混入されています。気づきましたか?
入力に数字以外を入れると、初期化されていない変数i
を出力しようとするので表示がバグります。
./hello_world.out
# 数字を入力してください
# >> a
# 入力されたのは -1401456832です。
こういう問題を未然に防ぐために、静的解析ツールが役立ちます。
以下のように実行します。
clang-tidy hello_world.c
clang-tidy hello_world.c
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "hello_world.c"
No compilation database found in /work or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
1 warning generated.
/work/hello_world.c:15:3: warning: 2nd function call argument is an uninitialized value [clang-analyzer-core.CallAndMessage]
printf("入力されたのは %d です。\n", i);
^ ~
/work/hello_world.c:6:3: note: 'i' declared without an initial value
int i;
^~~~~
/work/hello_world.c:11:7: note: Assuming the condition is false
if (isdigit(input)) {
^
/usr/include/ctype.h:192:21: note: expanded from macro 'isdigit'
# define isdigit(c) __isctype((c), _ISdigit)
^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/ctype.h:89:4: note: expanded from macro '__isctype'
((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/work/hello_world.c:11:3: note: Taking false branch
if (isdigit(input)) {
^
/work/hello_world.c:15:3: note: 2nd function call argument is an uninitialized value
printf("入力されたのは %d です。\n", i);
^ ~
warningが残っていても動きはしますが、バグを引き起こす可能性があるので基本的には消しておきましょう。
以上。