リンク: Amazon.co.jpで静的解析コード解析の本を探す?
AdLintとは
- オージス総研が提供している静的解析ツール
- 今のところ、C言語のみ解析
- 無償、GPLライセンス
- Rubyで作られている
実は商用の静的解析ソフトは非常に高い。安い方で38万、高い方は年間数百万 (moi総研調べ)。とても個人では手を出せない。それがなぜ無料なのか?
オージス総研はもう一つツールを提供している。
こちらは無料だが、
但し、1年間に1件以上の測定データ提出が必要になります。
および、
提供先は、『製造メーカーの組み込みソフトウェア開発現場』のみに限らせて頂きます。
とある。このソフトを使う前準備として、QACまたはAdLintが必要になるのだが、このQACというソフト、150万は下らない (moi総研調べ)。そして、オージス総研という会社はIT系のコンサルティングを行なっている。
つまり、私の推測 (邪推) はこうだ。
QACが買えないような弱小メーカーのソースコードの品質測定データを得て、コンサルティングに活かす
まあ、ここまではどんな人でも想像するだろう。そして、私の想像もここまで。一個人としてありがたく使わせて頂きます。
インストールしてみる
(2014年4月29日 更新)
インストール方法 もあるので、特に説明することはないため、私のやったことだけ説明する。
Windows環境で使うとき、以下の3通りの方法がある。
- コマンドプロンプトで使う
- MinGWで使う
- Cygwinで使う
1を選択した。私の場合、Windows版 vim (kariya版) を用いているため、vimから呼び出して実行できないと具合が悪いと感じた。
まずファイルをダウンロード・インストールする。
- rubyinstaller-2.0.0-p451.exe
- DevKit-mingw64-32-4.7.2-20130224-1151-sfx.exe
をダウンロードし、起動した。Rubyのインストーラは展開する場所を聞いてくるので、デフォルト設定 (Cドライブ直下) のC:\Ruby200とした。DevKitのインストーラは、デフォルトの設定は今ある場所に展開するだけなので、C:\DevKit を作ってからそこにファイルを移動し、実行する。
次にパスを設定する。
- C:\Ruby200\bin
- C:\DevKit\bin
- C:\DevKit\mingw\bin
を環境変数PATHに追加する。
ただし、システムのパスに追加したくないときは、次のバッチファイルを作り、環境変数を設定する
SetAdLintEnv.bat:
@set PATH=C:\Ruby200\bin;%PATH% @set PATH=C:\DevKit\bin;%PATH% @set PATH=C:\DevKit\mingw\bin;%PATH%
上記ファイルのショートカットを作成し、リンク先を次のように変更する。
cmd.exe /Q /K path\SetAdLintEnv.bat
「path\」には、実際にはSetAdlintEnv.batが置かれているパスを記入する (C:\binに置かれているときは、「C:\bin\SetAdlintEnv.bat」)
最後に、コマンドプロンプトを起動し、
> gem install adlint --no-ri --no-rdoc
を実行した。
>gem install adlint --no-ri --no-rdoc Fetching: adlint-3.2.12.gem (100%) ------------------------------------------------------------------------------- ___ ____ __ ___ _________ / | / _ |/ / / / | / /__ __/ Source Code Static Analyzer / /| | / / / / / / / |/ / / / AdLint - Advanced Lint / __ |/ /_/ / /___/ / /| / / / /_/ |_|_____/_____/_/_/ |_/ /_/ Copyright (C) 2010-2014, OGIS-RI Co.,Ltd. Thanks for installing AdLint! Please visit our project homepage at <http://adlint.sourceforge.net/>. ------------------------------------------------------------------------------- Successfully installed adlint-3.2.12 1 gem installed
ちゃんとインストールされた。
使い方
私の作ったmoiforthのコードを解析する。コマンドプロンプトでコードが置いてある場所に行き、
adlintize -o adlint
を実行する。adlintフォルダができ、その中にファイルがいろいろ出来る。
解析を実行する。先に出来たadlintフォルダに移動し、
make verbose-all
すると、解析結果がファイルに保存されるのだが、一筋縄ではいかない。以降、AdLintが使えるようになるための作業を順を追って説明する。
解析エラーの対処
(2014年5月1日 更新)
「make verbose-all」すると、
>make verbose-all adlint -t adlint_traits.yml -o . -p 1 -v ../token.c ../main.c ../token.c [cpp] |==== | 0.050s! An error was occurred while processing `../token.c'. See `token.c.msg.csv' and `token.c.log' for more details. ../main.c [cpp] |==== | 0.060s! An error was occurred while processing `../main.c'. See `main.c.msg.csv' and `main.c.log' for more details. 0.920s user, 0.374s system, 00:00:01.29 total make: *** [adlint_verbose-all] Error 4
エラーが出た。*.log ファイルや *msg.csv ファイルに原因が書かれている。
E,../main.c,12,1,core,E0009,ERR,X99,ヘッダファイル <stdio.h> が見つかりません。
私はコンパイラにMinGWを使っている。MinGWのstdio.hの場所は、C:\MinGW\includeのようである(DevKit下のstdio.hは、C:\DevKit\mingw\i686-w64-mingw32\include\stdio.hのようである)。そこで、AdLintが生成したファイル、adlint_traits.yml を編集し、このパスを追加した。
compiler_traits: # Pathname of the compiler specific initial header file. # This header file is automatically included above the first line of the # project specific initial header file. initial_header: "adlint_cinit.h" # Compiler specific include paths. # Ex. # file_search_paths: # - "/usr/include" # - "/usr/local/include" file_search_paths: - "C:/MinGW/mingw32/include"
パスは"/"で区切る。再度実行。
>make verbose-all adlint -t adlint_traits.yml -o . -p 1 -v ../token.c ../main.c ../token.c [prs] |========== | 1.980s! An error was occurred while processing `../token.c'. See `token.c.msg.csv' and `token.c.log' for more details. ../main.c [prs] |========== | 4.374s! An error was occurred while processing `../main.c'. See `main.c.msg.csv' and `main.c.log' for more details. 7.425s user, 0.483s system, 00:00:07.90 total make: *** [adlint_verbose-all] Error 4
まだエラーが出る。
E,C:/MinGW/mingw32/include/stdio.h,34,1,core,E0009,ERR,X99 ,ヘッダファイル <stddef.h> が見つかりません。
そこで、パスに
- "C:/MinGW/mingw32/lib/gcc/mingw32/4.8.1/include"
を追加し、再度実行。が、まだエラーが出る。
E,C:/MinGW/mingw32/lib/gcc/mingw32/4.8.1/include/stdarg.h,40,9,core,E0008,ERR,X99 ,トークン `__builtin_va_list __gnuc_va_list' で構文エラーを検知しました。
AdLintのフォーラムから、size_tの構文エラーなるものを見つけたので、以下のことを行なってみる。
adlint_cinit.hの編集
> touch a.c (空の.cファイルを作る) > gcc -v -E -dM a.c 1>a.h 2>a.log (標準出力をa.h、標準エラー出力をa.logに出力する)
GCCコマンド・オプションによると、
- -v
- (広義の)コンパイル処理の各段階において実行されたコマンドを (標準エラー出力に) 表示します。 また、 コンパイラ・ドライバ・プログラム、 プリプロセッサ、 狭義のコンパイラの各バージョン番号も表示します。
- -dM
- プリプロセッサに対して、 前処理の終了時点において有効なマクロ定義の一覧だけを出力するよう指示します。 `-E'オプションとともに使います。
- -E
- Cプリプロセッサだけを実行します。 指定されたすべてのCソース・ファイルを前処理して、 その結果を標準出力、 もしくは、 指定された出力ファイルに出力します。
すると、a.hとa.logの出力は以下のようになる。
a.hに出力された内容をadlint_cinit.hにコピーし、さらに以下もadlint_cinit.hに追加する。
#define __const const #define __restrict restrict #define __inline inline #define __builtin_va_list void * #define __builtin_va_start(a, b) (0) #define __builtin_va_end(a) (0) #define __builtin_va_arg(ar, t) (0) #define __builtin_va_copy(d, s) (0) #define __builtin_offsetof(type, member) (0) #define __alignof__(type) (0)
コンパイラのインクルードパスの設定
a.logには、サーチパスが出力されている。これを元に、adlint_traits.ymlにある、compiler_traitsセクションのfile_search_paths:を修正する。
compiler_traits: initial_header: "adlint_cinit.h" file_search_paths: - "C:/MinGW/mingw32/lib/gcc/mingw32/4.8.1/include" - "C:/MinGW/include" - "C:/MinGW/mingw32/lib/gcc/mingw32/4.8.1/include-fixed" - "C:/MinGW/mingw32/include"
結果は、、
E,C:/MinGW/include/stdio.h,152,1,core,E0008,ERR,X99 ,トークン `( (' で構文エラーを検知しました。
まだ出る。stdio.hの154行は、
__MINGW_IMPORT FILE _iob[]; /* An array of FILE imported from DLL. */
コンパイラの独自拡張の無効化
さらに、コンパイラの独自拡張を無効化する設定というのを加えてみる。
adlint_traits.ymlにある、compiler_traitsセクションのextension_substitutions:を修正する。
extension_substitutions: "__extension__": "" "__attribute__(__adlint__any)": "" "__inline__": "inline" "__asm__ __adlint__any(__adlint__any)": "" "__signed__": "signed"
結果は、、
E,C:/MinGW/include/stdlib.h,190,21,core,E0008,ERR,X99 ,トークン `__restrict__ __nptr' で構文エラーを検知しました。
また出た。でももしかして、extension_substitutions にこれを加えればいい?
"__restrict__": ""
ビンゴ!ついにエラーが消えた。
MPLAB XC32コンパイラ用の設定
(2014年5月9日追加)
Microchip Technology社製の、MPLAB XC32コンパイラは、gccベースのコンパイラである。そのため、前記の、MinGWに対して行った対処を実施する。
さらに、XC32のstdio.hには次のようなマクロ定義があり、AdLintが止まってしまう。
#if defined(__C32_VERSION__) #if !defined(__STRICT_ANSI__) #define define_smartio_variants(rettype,funcname,...) \ extern rettype _ ## funcname ## _cdeEfFgGnopsuxX (__VA_ARGS__); \ extern rettype _ ## funcname ## _cdeEfFgGnopuxX (__VA_ARGS__); \ extern rettype _ ## funcname ## _cdeEfFnopsuxX (__VA_ARGS__); \
そこで、adlint_traits.yml の arbitrary_substitutions に次の設定を追加する。
arbitrary_substitutions: ...その他の設定... "define_smartio_variants(__adlint__any);": ""
これを行うと、可変長引数を持つマクロ定義を削除してくれる。
注: この問題は、「AdLint 3.2.14」で修正され、上記対策を行う必要はない (2014年5月14日)
警告メッセージ
以下のファイルに警告メッセージやメトリクスが保存されている。
ソースファイル | メッセージファイル | メトリクスファイル |
---|---|---|
main.c | main.c.msg.csv | main.c.met.csv |
token.c | token.c.msg.csv | token.c.met.csv |
moiforth.msg.csv | moiforth.c.met.csv |
main.c に対する警告
さて、main.c はファイル行数 771、に対し、警告は、48種類443件。main.cの実際のステートメント数は、194。1行に2件の警告がある計算だ。
警告 | 件数 | メッセージ | コメント |
---|---|---|---|
W0705 | 62 | 配列の添字が領域外を指すことがあります。 | 確かにそうかもしれないが、、アクセスする前にインデックス用変数の値チェックをしろとでもいうのか?そうですよね、ハイすみません |
W9003 | 46 | 暗黙的に `%s' 型のオブジェクトが `%s' 型のオブジェクトに変換されています。 | 必ずキャストをしないさいということ |
W0576 | 33 | 基本ソース文字集合に含まれない文字が、ソースファイル `%s' のコメントの中で使われています。 | どうやら、漢字と@はコメントに使っては駄目っぽい。日本人なので、それはちょっと、、 |
W0118 | 32 | 外部結合を持つ `%s' の宣言が、ヘッダファイルの中にありません。 | プロトタイプ宣言してあるだけなのだが。外部で使われていないから、static にすべきか? |
W0246 | 27 | 暗黙的に unsigned char 型から signed int 型に型変換されています。 | 負の数になる可能性がある。明示的にキャストする |
W0117 | 26 | `%s' の定義は外部結合を持ちますが、定義より前にその宣言がありません。 | グローバル変数はヘッダでの宣言が必要になるらしい。外部参照されないので、staticにしておくか? |
W0947 | 23 | 文字列リテラルが直接使われています。 | 確かに大規模なプログラムだとソースコードのあちこちに直接文字列があると、保守性が落ちるのかもしれない。しかしこのプログラムは200行弱だから許して欲しい |
W0422 | 15 | 値が NULL になることがあるポインタに対して間接参照が行われています。 | ポインタがヌルかチェックしてから参照しろとでも言うのか?そうですよね、ハイす(略) |
W0100 | 14 | 変数 `%s' は値が変わりません。const 付きで宣言できる可能性があります。 | 確かに最初の代入後、値が変わらない。const 付けたほうが安全と |
W0136 | 14 | 暗黙的に signed int 型から char 型に型変換されています。 | 小さい方への代入は情報の損失がある。明示的ならいいよね? |
W0104 | 11 | 仮引数 `%s' は値が変わりません。const 付きで宣言できる可能性があります。 | 確かに値が変わらない。そんなに const が好きなのか? |
W0512 | 11 | `++' あるいは `?' 演算子の結果が式の中で使われています。 | 複雑になるから、らしい |
W0950 | 11 | 配列のサイズ定義に、数値 `%s' が直接使われています。 | 配列のサイズ指定はわかりやすい名前を付けたマクロでやれと |
W0105 | 10 | ポインタ仮引数 `%s' が指しているオブジェクトは関数内で変わらないので、const オブジェクトを指すポインタ型として宣言できる可能性があります。 | なるべく const を付けるんですね |
W0723 | 10 | 符号付きの式の値がオーバーフローする可能性があります。 | 計算する前にオーバフローしないかチェックしろとでも言うのか?そうですよね、ハ(略) |
W0165 | 8 | 暗黙的に signed int 型から unsigned char 型に型変換されています。 | はいキャストします |
W0948 | 8 | 文字定数 %s が直接使われています。 | 保守性が落ちるのかもしれないが、行数見て判断してもらうことは出来ないでしょうか? |
W1073 | 8 | 関数 `%s' の戻り値を破棄しています。 | 確かに printf() にも戻り値があるよね、、 |
W0123 | 7 | 暗黙的に char 型から signed int 型に型変換されています。 | キャストキャスト |
C0001 | 6 | この識別名 `%s' で警告を検知しました。 | 他の警告と対で使われる |
W0023 | 6 | ポインタ型の変数に対して算術演算が行われています。 | これもキャストすれば良い? |
W0147 | 5 | 暗黙的に unsigned int 型から signed int 型に型変換されています。 | 問題なければキャストする? |
W0024 | 4 | ポインタ型の変数に対してインクリメントまたはデクリメントが行われています。 | でもやらないといけないし、、キャスト? |
W0771 | 4 | グローバルな識別子 `%s' が、二つ以上のファイルで宣言されています。 | どうやら消すのを忘れていた |
W0108 | 3 | 代入演算子が論理演算式の中で使われています。 | コーディングミスの場合もあるため、なるべく使わないようにする |
W1071 | 3 | 関数 `%s' には複数の終了点があります。 | 紛らわしいからかな? |
W0059 | 2 | このファイルの行末に Ctrl-M(^M) 制御文字が見つかりました。 | ファイルの最後が改行で終わっては駄目らしい |
W0114 | 2 | この制御式は明示的な論理演算ではありません。 | ブール値を返す関数なら良い? |
W0167 | 2 | 暗黙的に signed int 型から unsigned int 型に型変換されています。 | 明示的なキャストを |
W0247 | 2 | 暗黙的に unsigned char 型から signed long 型に型変換されています。 | ただキャストするのではなく、検討してから |
W0248 | 2 | 暗黙的に unsigned short 型から signed int 型に型変換されています。 | キャスト |
W0272 | 2 | signed int 型の値が関数 `char %s()' から返されています。 | 確かに-1は int型だ、、 |
W0425 | 2 | 一つの行に複数の宣言または文が存在します。 | ちょっと位いいじゃん、とはいかないのが静的解析 |
W0442 | 2 | 関数形式マクロが定義されています。 | ちょっと位いいじゃん、、 |
W0478 | 2 | このマクロは、認識不可能なコードの断片を定義しています。 | 自分には分かる。みんな分かってくれると思う、、 |
W0492 | 2 | 同じ識別子名 `%s' が構造体/共用体のメンバの他に、ラベル、タグ、または通常の識別子としても使われています。 | だって意味が同じだし、、 |
W0742 | 2 | 負の整数定数式が符号無し型に変換されています。 | char型を返す関数で-1を返している。signed char としろと言うこと? |
W0755 | 2 | 暗黙的に signed long 型から signed int 型に型変換されています。 | キャストする |
W1047 | 2 | 構造体、共用体、あるいは配列の初期化子がリテラルではありません。 | ええ?駄目なの? |
W0073 | 1 | このインクルードファイルには、インクルードガードが記述されていません。 | つけます、、 |
W0166 | 1 | 暗黙的に signed int 型から unsigned short 型に型変換されています。 | キャストする |
W0169 | 1 | 暗黙的に signed long 型から unsigned char 型に型変換されています。 | キャストする |
W0256 | 1 | char 型の値が関数 `signed char %s()' から返されています。 | もう疲れてきた、、 |
W0268 | 1 | unsigned char 型の値が関数 `char %s()' から返されています。 | 、、、 |
W0502 | 1 | + - * / % 以外の二項演算子が、異なる優先順位の二項演算子と共に使われています。`()' で結合を明確にすることを勧めます。 | カッコ推奨 |
W0575 | 1 | 基本ソース文字集合に含まれない文字が、文字列リテラルの中で使われています。 | '@' 禁止 |
W0609 | 1 | この論理演算の結果は常に真になります。 | (c = fgetc(fp)) != EOF だが、もちろんそんなことはない |
W0625 | 1 | 外部結合の定義または宣言の中で `%s' が使われていますが、その typedef がヘッダファイルの中で宣言されていません。 | typdef はヘッダ内で |
W0752 | 1 | 暗黙的に unsigned int 型から unsigned short 型に型変換されています。 | キャスト |
token.c に対する警告
token.cは、113行、ステートメント数32行、なのに警告数は21種類96件。
警告 | 件数 | メッセージ | コメント |
---|---|---|---|
W0422 | 36 | main.c で既出 | |
W0948 | 9 | 〃 | |
W0576 | 8 | 〃 | |
C0001 | 4 | ||
W0136 | 4 | main.c で既出 | |
W0771 | 4 | 〃 | |
W0104 | 3 | 〃 | |
W0123 | 3 | 〃 | |
W0059 | 2 | 〃 | |
W0100 | 2 | 〃 | |
W0105 | 2 | 〃 | |
W0114 | 2 | 〃 | |
W0272 | 2 | 〃 | |
W0488 | 2 | 関数呼び出し `()'、配列添字演算 `[]'、あるいは構造体/共用体メンバ演算 `->' または `.' が、`&&' あるいは `||' と共に使われています。`()' で結合を明確にすることを勧めます。 | 確かにわかりにくい |
W0490 | 2 | 二項演算子が、`&&' あるいは `||' と共に使われています。`()' で結合を明確にすることを勧めます。 | カッコで分かりやすく |
W0502 | 2 | main.cで既出 | |
W9001 | 2 | この文には制御が到達しません。 | break の前に return がある |
W0073 | 1 | main.cで既出 | |
W0118 | 1 | main.cで既出 | |
W0489 | 1 | 単項演算子が、`&&' あるいは `||' と共に使われています。`()' で結合を明確にすることを勧めます。 | カッコで分かりやすく |
W0497 | 1 | シフト演算子、関係演算子、あるいは等価演算子が複数回使われています。`()' で結合を明確にすることを勧めます。 | カッコで分かりやすく |
W1071 | 1 | main.cで既出 |
moiforth 全体に対する警告
moiforth.msg.csv は、ファイル間の問題の警告のためにある?
警告 | 件数 | メッセージ | コメント |
---|---|---|---|
W0593 | 19 | オブジェクト `%s' は、定義されている翻訳単位の中でのみ参照されています。 | static にした方が良い |
C0001 | 18 | Wと対で指摘される項目 | |
W0591 | 16 | 関数 `%s' は、定義されている翻訳単位の内でのみ参照されています。 | static にした方が良い |
W0628 | 16 | 関数 `%s' が定義されていますが、このプロジェクトでは使われていません。 | いらない関数 |
W0589 | 12 | オブジェクト `%s' は、定義されている翻訳単位の内の、関数 `%s' のみから参照されています。 | static にした方が良い |
W0770 | 8 | 外部結合をもつ識別子 `%s' の宣言が、二つ以上存在しています。 | 1つだけにする |
、、、正直、何が重要で何が重要でないのか、よくわからない。しかし、これらを潰していけば、また、警告が出ないようにプログラムを組んでいけば、コードの品質が上がる、はずである。
やってみよう。
警告の抑止
AdLint 3.0.0 からは、警告の抑止機能がある。警告が余計なお世話だったり、どうしてもその方法で書かなければいけないとき、毎回警告が出るのは鬱陶しい。そこで、ソースコードにコメントを埋め込むことで警告を抑止する。
使い方は、AdLint 3.0.4 利用者ガイド に書いてある。
例えば、W0576。コメントに漢字が書いてあるだけで警告を発する、抑止第一候補。これをばっさりなくすには、ファイルの先頭に
/* ADLINT:SF:[W0576] */
と書けば、そのファイルに対する警告から W0576 はすべて消える。
W0512は式の中で "++" を使うなとのお達し。これをこの行だけ抑止するには、その行のコメントとして
(*jump_table[prog[prog_cnt++]])(); /* ADLINT:SL:[W0512] */
を加えれば、この行だけ W0512 が消える。
vimとの連携
AdLintが警告メッセージを出力するフォーマットは、コンパイラと同じ形式とのこと。
を参考にした。
vimでAdLintを利用する
- vimでソースコードを開く
- AdLintの設定・結果のあるディレクトリに移動する(:cd adlint)
- (もし必要なら、:set makeprg=make を実行する)
- :copen でQuickfixバッファを開く
- :make all を実行する
コマンドプロンプト上で
make verbose-all
とすると、ファイルごとに処理の進捗状況が表示される。make all だと、それは表示されず、警告メッセージが標準出力に出力される。vimは、その出力をコンパイラのエラーメッセージと同様に解釈し、Quickfixバッファにリスト表示する。ここからEnterキーでそれぞれの警告にジャンプできる。
また、*.msg.csv ファイルにも、警告メッセージが保存されている。これは、
:cg ファイル名
でQuickfixバッファ内で開け、ジャンプできるように加工処理される、、はずなんだが、ジャンプリストにならない。そこで、_gvimrc に
set errorformat+=%t\\,%f\\,%l\\,%c\\,%m
を加え、再起動または :source _gvimrc し、再度 :cg してみると、、できた。
このフォーマットは、vimdoc-ja / doc / quickfix.jaxの、「7. エラーフォーマット」に説明がある。エラーフォーマット中の「,」を記述するのに、「\\,」とする必要がある。
なお、先述のようにAdLint関連のパスをシステムパスに追加していないときは、vim上で「:make all」してもAdLintを起動できない。その場合は、次のようにすると良い。
まず、次のようなバッチファイル「mkall.bat」を作る。
@set PATH=C:\Ruby200\bin;%PATH% @set PATH=C:\DevKit\bin;%PATH% @set PATH=C:\DevKit\mingw\bin;%PATH% make all
次に、vimで次の様に設定する。
:set makeprg=mkall.bat
これで、「:make」と入力するだけでAdLintを実行できる。
キーマップで便利にする
さて、警告メッセージ間を移動するには、「:cn」、「:cp」すればいいのだが、3文字打つのは面倒くさい。そこで、_gvimrc に以下の設定を追加した。
nnoremap <s-f2> :cn<cr> nnoremap <s-f3> :cp<cr>
これで、Shiftキーを押しながらF2で次の警告、Shiftキーを押しながらF3で前の警告に移動する。
警告抑止指定の "/* ADLINT:SL:[Wxxxx] */" は何度も入力するのは面倒そうだ。そこで、_gvimrc に以下の設定を追加した。
nnoremap <s-f4> A/* ADLINT:SL:[W] */<LEFT><LEFT><LEFT><LEFT>
これで、行末に "/* ADLINT:SL:[W] */" が入力され、カーソルは 'W' の直後に移動する。このまま数値だけを入力すれば良い。
警告を潰していく
自分で作った moiforth を、メッセージが出ないようにリファクタリングしてみる。
抑止した警告
以下の警告メッセージは抑止した。こんなので警告が出たらかなわん、という感じ。
警告 | メッセージ | コメント |
---|---|---|
W0422 | 値が NULL になることがあるポインタに対して間接参照が行われています。 | |
W0442 | 関数形式マクロが定義されています。 | デバッグ用に関数形式マクロが2つあるが、問題ないと判断した |
W0575 | 基本ソース文字集合に含まれない文字が、文字列リテラルの中で使われています。 | 文字列中に '@' などがあっても問題ないと判断した |
W0576 | 基本ソース文字集合に含まれない文字が、ソースファイル `%s' のコメントの中で使われています。 | コメント中に '@' があっても問題ないと判断した |
W0608 | 値が負になることがありうる整数式が符号無し型に変換されています。 | 警告に対処するためにキャストしたら、今度はこれが出てきた。どうしろと、、 |
W0705 | 配列の添字が領域外を指すことがあります。 | 問題ないと判断した |
W0723 | 符号付きの式の値がオーバーフローする可能性があります。 | 問題ないと判断した |
W0745 | 配列の添字が、領域外を指しています。 | '*(long *)(&stack[stack_pos])' の最初のカッコの位置で警告が出た。問題ないと判断した |
W1047 | 構造体、共用体、あるいは配列の初期化子がリテラルではありません。 | 関数ポインタ配列なので、仕方ない |
W1073 | 関数 `%s' の戻り値を破棄しています。 | printf()に対する警告を抑止した |
修正した警告
こつこつと修正する。
警告 | メッセージ | コメント |
---|---|---|
W0100 | 変数 `%s' は初期値を設定後に再代入されません。変数を const 付きで初期値を指定して定義できる可能性があります。 | const 付けた |
W0104 | 仮引数 `%s' は値が変わりません。const 付きで宣言できる可能性があります。 | const 付けた |
W0117 | `%s' の定義は外部結合を持ちますが、定義より前にその宣言がありません。 | 内部でしか使っていないため、static にした |
W0118 | 外部結合を持つ `%s' の宣言が、ヘッダファイルの中にありません。 | 内部でしか使っていないため、static にした |
W0165 | 暗黙的に signed int 型から unsigned char 型に型変換されています。 | 問題ないことを確認後、キャストした |
W0167 | 暗黙的に signed int 型から unsigned int 型に型変換されています。 | 問題ないことを確認後、キャストした |
W0246 | 暗黙的に unsigned char 型から signed int 型に型変換されています。 | 問題ないことを確認後、キャストした |
W0492 | 同じ識別子名 `%s' が構造体/共用体のメンバの他に、ラベル、タグ、または通常の識別子としても使われています。 | 構造体のメンバである pos が別の場所で変数名に使われていたため、そちらを stack_pos に変更した |
W0705 | 配列の添字が領域外を指すことがあります。 | 参照前に範囲チェックした |
W0723 | 符号付きの式の値がオーバーフローする可能性があります。 | 参照前に範囲チェックした |
W9003 | 暗黙的に `%s' 型のオブジェクトが `%s' 型のオブジェクトに変換されています。 | 関数ポインタ配列の初期化のために関数名を書いていたが、"&関数名" に変更した |
W0723 「符号付きの式の値がオーバーフローする可能性があります」、W0705 「配列の添字が領域外を指すことがあります」 について
範囲チェックを全部入れるのは大変だし、オーバーヘッドになる。ほとんどは問題ないのだと思うがAdLintの方針が「誤検出してもいいから安全側に」だったはず。これへの対処として、次のような方法が考えられる。
- コードを目視チェックし、問題ないと判断したら警告を抑止する
- 式の手前で範囲チェックを行う
- getter / setter を用意し、その中で範囲チェックを行う
重要なところは範囲チェック、他はassert関数や目視でチェックし、問題ないと判断したら無効にするほうがいいのかも。
将来は「変数の値がxxを超えるとオーバーフローする可能性があります」のようになれば嬉しい。