トップ 差分 一覧 Farm ソース 検索 ヘルプ RSS ログイン

AdLintを使ってみる

リンク: Amazon.co.jpで静的解析コード解析の本を探す?

AdLintとは

オープンソースの静的解析ツール AdLint(アドリント)

  • オージス総研が提供している静的解析ツール
  • 今のところ、C言語のみ解析
  • 無償、GPLライセンス
  • Rubyで作られている

実は商用の静的解析ソフトは非常に高い。安い方で38万、高い方は年間数百万 (moi総研調べ)。とても個人では手を出せない。それがなぜ無料なのか?

オージス総研はもう一つツールを提供している。

ソースコード品質評価ツール Adqua(アドクア)

こちらは無料だが、

但し、1年間に1件以上の測定データ提出が必要になります。

および、

提供先は、『製造メーカーの組み込みソフトウェア開発現場』のみに限らせて頂きます。

とある。このソフトを使う前準備として、QACまたはAdLintが必要になるのだが、このQACというソフト、150万は下らない (moi総研調べ)。そして、オージス総研という会社はIT系のコンサルティングを行なっている。

つまり、私の推測 (邪推) はこうだ。

QACが買えないような弱小メーカーのソースコードの品質測定データを得て、コンサルティングに活かす

まあ、ここまではどんな人でも想像するだろう。そして、私の想像もここまで。一個人としてありがたく使わせて頂きます。

インストールしてみる

(2014年4月29日 更新)

インストール方法 もあるので、特に説明することはないため、私のやったことだけ説明する。

Windows環境で使うとき、以下の3通りの方法がある。

  1. コマンドプロンプトで使う
  2. MinGWで使う
  3. 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と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日)

リンク: Amazon.co.jpでFORTHの本を探す?

警告メッセージ

以下のファイルに警告メッセージやメトリクスが保存されている。

ソースファイル メッセージファイル メトリクスファイル
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 との連携方法

を参考にした。

  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の方針が「誤検出してもいいから安全側に」だったはず。これへの対処として、次のような方法が考えられる。

  1. コードを目視チェックし、問題ないと判断したら警告を抑止する
  2. 式の手前で範囲チェックを行う
  3. getter / setter を用意し、その中で範囲チェックを行う

重要なところは範囲チェック、他はassert関数や目視でチェックし、問題ないと判断したら無効にするほうがいいのかも。

将来は「変数の値がxxを超えるとオーバーフローする可能性があります」のようになれば嬉しい。

Amazonリンク
Articles on Forth Programming Language Family, Including: Forth (Programming Language), 51-Forth, Open Firmware, Bashforth, Colorforth, Muf