コンテンツにスキップ

LINT系関数の仕様と制約

LINT.* 系関数は、読み込み済み辞書の解析結果(関数内のステートメントと数式の項)から、関数・変数の使用箇所を取り出すデバッグ用の関数群です。yayalint のような静的検査を YAYA 辞書自身で組むための材料になりますが、以下の仕様と制約があります。

関数一覧

関数 対象 重複 スコープ { } 読み書きの区別 行
LINT.GetVarRefs 変数・関数・ブロック 出現ごと あり(種類付き) あり あり
LINT.GetFuncUsedBy 関数(ユーザー定義・システム) 除去・名前順 なし - なし
LINT.GetUserDefFuncUsedBy ユーザー定義関数 除去・名前順 なし - なし
LINT.GetGlobalVarUsedBy グローバル変数(読み書き両方) 除去・名前順 なし なし なし
LINT.GetGlobalVarLetted グローバル変数(代入のみ) 除去・名前順 なし - なし
LINT.GetLocalVarUsedBy ローカル変数(読み書き両方) 出現ごと あり なし なし
LINT.GetLocalVarLetted ローカル変数(代入のみ) 出現ごと あり - なし

新しく検査を組む場合は LINT.GetVarRefs を使うと、他の関数の情報をすべて含んだ形で得られます。

Tc574-1 での変更

  • LINT.GetVarRefs を追加しました。
  • GetGlobalVarLetted / GetLocalVarLetted
    • 配列要素への代入(_a[0] = 1、G_a[0] = 1)を、その変数への代入として列挙するようになりました。
    • foreach _list ; _v のループ変数 _v を代入として列挙するようになりました。
  • GetLocalVarUsedBy
    • 同じステートメント内では、代入先の変数を最後に並べるようになりました。代入は右辺の評価後に行われるため、_z = _z + 1 は _z(読み)→ _z(代入)の順になります。
  • GetLocalVarUsedBy / GetLocalVarLetted
    • case 構文が内部で生成するローカル変数(_CaSe_ExPr_PrEfIx_ で始まる名前)を列挙しないようになりました。

制約

辞書の読み込みが前提

解析対象は読み込みに成功した辞書です。構文エラーなどで辞書が読み込めない場合は緊急モードになり、LINT 系関数を使う辞書自体が動きません。重複定義・構文エラー・括弧付きの未定義関数呼び出し(E0071)などは、YAYA 本体が読み込み時にエラーとして出力します。

位置情報

  • 行は LINT.GetVarRefs で得られます。ステートメント単位の行番号です。
  • / による行の継続やヒアドキュメントで複数行にわたるステートメントは、その最後の行の番号になります。
  • 桁は得られません。YAYA 内部にも保持されていません。読み込み時に行の結合・#define の置換・コメント除去・{ } ; での分割を行うためです。

名前の見え方

  • #define / #globaldefine は読み込み時に文字列として置換されるので、置換後の名前が見えます。
  • EVAL('名前') や LETTONAME('名前', …) のように文字列で指定される名前は追えません。
  • LINT を行う辞書自身の関数・変数も一覧に含まれます。接頭辞などで除外してください。
  • 埋め込み式 %(式) の展開で内部的に使われる TOSTR が、関数の呼び出しとして含まれます(LINT.GetFuncUsedBy / LINT.GetVarRefs)。ユーザーが書いた TOSTR と区別できません。

読み・書きの区別

  • LINT.GetVarRefs では、= の代入先は w、複合代入(+= など、++ -- を含む)の代入先は rw です。
  • 配列要素への = の代入(_a[0] = 1)は w として扱います。配列の他の要素は残りますが、未定義の変数への要素代入も許されるためです。
  • 並べ替え(代入先を最後に並べる)はステートメント単位です。1つの式に複数の代入が含まれる場合、実行順とは一致しないことがあります。
  • LINT.GetVarRefs 以外の関数には読み・書きの印がありません。
    • GetGlobalVarUsedBy / GetGlobalVarLetted は重複を除去するため、「代入されているが一度も読まれていないグローバル変数」を判別できません。
    • GetLocalVarUsedBy の各要素が読みか代入かを、GetLocalVarLetted と1件ずつ突き合わせて決めることはできません。

構文の情報

  • ブロックの種類と、出現した構文の種類(条件式・for の各部・case の式など)は LINT.GetVarRefs で得られます。
  • 変数・関数を含まないステートメント(break / continue / 式なしの return / --、リテラルだけの出力など)は列挙されません。
  • others は内部で else に変換されるため、「case ブロックの直下にある else」を others と判定しています。
  • when は内部で if / elseif に変換されるため、case の内部変数を含むかどうかで判定しています。

解析の範囲

  • 分岐やループの流れは、ブロックの種類と深さから辞書側で組み立てる必要があります。
  • 関数をまたいだ解析(引数 _argv の使われ方、グローバル変数の代入元など)は、関数ごとの結果を辞書側で突き合わせる必要があります。

検査の組み立て例

検査 可否 方法
未使用関数 可 GETFUNCLIST の全関数について LINT.GetVarRefs の func,user を集め、どこにも現れない関数を列挙する(イベント関数など外部から呼ばれるものは除外)
未使用ローカル変数 可 スコープ({ })ごとに、w しかなく r / rw がない変数を探す
未定義ローカル変数の読み 可(分岐は辞書側で考慮) 先頭から順に追い、そのスコープまたは外側のスコープで w / rw が現れる前の r / rw を探す
未使用グローバル変数 可 全関数の var,global を集め、w しかない変数を探す(EVAL などで読まれるものは追えない)
条件式内の代入 可 context が if / elseif / while / switch / for-cond で、access が w / rw の件を探す
case の不正な節 一部可 case ブロックの直下(深さ+1)に、when / others 以外のブロックや context が when 以外の件がないか調べる。変数・関数を含まない記述は検出できない
名前の打ち間違い候補 可 GETFUNCLIST / GETVARLIST との編集距離を辞書側で計算する
行・桁の表示 行のみ可 桁は得られない