研究進捗報告: 巨大プロジェクトにおける変数名の一貫性の検査

権藤研 冬ゼミ資料 2019/12/27
新山

0. 夏ゼミ時との違い

1. 背景: 一貫性は巨大ソフトウェアプロジェクトを保守するのに重要である

2. 目標: ソースコード中の一貫しない変数名を指摘する

void printResult(Stream out, String result) {
  out.writeLine("result:"+result);
}

void printStat(Stream out, int stat) {
  out.writeLine("stat:"+stat);
}

void printInfo(Stream strm, String info) {
  strm.writeLine("info:"+info);  // お前、ここはoutやろ。
}

2.1. 既存研究に対する立ち位置

3. 理論: そもそも「一貫性 (consistency)」とは何か?

3.1. 一般論

例1.

例2.

3.2. プログラムの変数名に適用する

変数名の使い方に着目する:

3.3. ベイズ化する

一般には、ある規則がつねに 100% 成り立つということはあまりない。 そこで、確率的に成り立つ規則を導入する。

4. 準備: 変数の「データパス」に着目する

ここでは、ある変数に代入される値が通る一連の処理 (または関数の引数および返り値が通るデータフロー) を、 その変数の「使い方」と定義する。 ここではこのデータフローを「データパス」と呼ぶ。

4.1. データパスの例

問. 以下の例で変数 line に着目する。

private BufferedReader fp;

public String get() {
    String line = fp.readLine();
    int i = line.indexOf(' ');
    return line.substring(0, i);
}

グラフ化するとこうなる:

get() cluster_a N1 fp N2 readLine() N1->N2 #this N4 indexOf() N2->N4 #this N6 substring() N2->N6 #this N3 ' ' N3->N4 #arg0 N4->N6 #arg1 N5 0 N5->N6 #arg0 N7 return N6->N7

グラフ中の赤線部分が 変数 line のデータパスである:

これは、変数 line になにが代入され、 それがどのように使われるかを示している。

4.2. Interprocedural 化する

private BufferedReader fp;

public String get() {
    String line = fp.readLine();
    int i = line.indexOf(' ');
    return line.substring(0, i);
}

public void show() {
    String name = get();
    System.out.println(name+"!!");
}

get() 中の変数 line (の処理結果) は、 いまや show() 中では name となっていることに着目。

これをグラフ化するとこうなる:

A cluster_a get() cluster_b show() N1 fp N2 readLine() N1->N2 #this N4 indexOf() N2->N4 #this N6 substring() N2->N6 #this N3 ' ' N3->N4 #arg0 N4->N6 #arg1 N5 0 N5->N6 #arg0 N9 name N6->N9 N11 + N9->N11 L N8 System.out N12 println() N8->N12 #this N10 !! N10->N11 R N11->N12 #arg0

最終的に、 変数 line のデータパスは:

となる。 この順列が、本プログラムにおける「変数 line の使われ方」を表すことになる。

単純化のため、いまのところパスの分岐は考慮しない。

5. 手法: 学習と予測

以上のようなデータパスを、 プログラム中のすべての変数に対して (interprocedural で) 抽出し、 変数名との相関を学習する。 ここでは「データパスを与えると、変数名を予測する」ようなモデルを学習する。

fp.readline() line #this:indexOf() #arg1:substring() assign:name

たとえば、以下のようなデータパスが出てきたら:

ここでの ??? = line ではないか、 と予測するということである。もしここで line 以外の名前が使われていたら、 その変数名は (この使われ方に対しては) 一貫していない、ということになる。

具体的には、

6. 実験と評価方法

以下のプロジェクトに対して検証をおこなった: (実際には #エッジ < #データパス)

プロジェクトKLOC#変数 #ノード#エッジ
ant (ビルドツール)112k23,971 350k5,211k
antlr4 (構文解析)31k7,131 74k1,103k
bcel (バイトコード解析)31k6,583 80k1,190k
compress (データ圧縮)24k5,896 69k929k
jedit (テキストエディタ)115k21,977 294k6,106k
jhotdraw (図形描画)80k17,367 235k2,351k
junit (ユニットテスト)9k2,384 21k280k
lucene (文書検索)109k30,341 414k7,146k
tomcat (Webサーバ)238k49,275 649k11,799k
weka (機械学習)324k59,274 943k13,224k
xerces (XMLパーサ)114k21,852 314k7,017k
xz (データ圧縮)7k1,825 23k299k

(疑問: 論文では数値の詳細を略すのは悪印象か?)

被験者: 著者3人 + 大学院生6人 = 計9人

RQ1. データパスは変数名の「使われ方」を表現するのに適切なのか?

まず予備実験として、データパスを本手法に使うことの妥当性を判断する。 もしデータパスが変数名の「使われ方」をうまく表現できていれば、 「同じデータパス」をもつ異なる変数は「同じ使われ方」をしているはずである。

実験1. データパスを使って同じ役割をもつ変数を判定する

各プロジェクトから同じデータパスの類似度が高い (cosine > 0.90) 変数の組を選び、(もとの変数名は隠して) それらが表れているコードを 人間に見せて、同じ変数名にすべきかどうかを判定させる。

  1. 2つの変数名は同じであるべきだ。
  2. 2つの変数名は同じでもよい。
  3. 2つの変数名は違っているべきだ。
  4. わからない。

全12プロジェクト × 各プロジェクト5問 × 9人 = 計540問の結果:

プロジェクト#a#b#c#d#全体
ant23175045
antlr413229145
bcel31131045
compress10924245
jedit7259445
jhotdraw151712145
junit142110045
lucene18216045
tomcat11276145
weka42812145
xerces12229245
xz41526045
13423515912540

#a + #b の割合が全体の 68% (369/540) を占めている。 したがって、データパスが類似している変数は同じ名前をもつ可能性が高い。

なお、被験者は各組の cosine を知らされていないが、 もともと 0.90 以上の対のみに絞ってしまったので、 あまり blind test とは言えなくなくなってしまった。 (#a + #b の平均 cosine は 0.980, #c の平均 cosine は 0.976 である)

RQ2. うまく一貫性のある変数名を予測できたか?

実験2-1. 提案した名前がよいものかどうか判定する

For each 変数 について、(名前は隠して) それが使われている部分を見せ

  1. オリジナルの変数名
  2. 本手法によって提案された変数名
  3. ベースライン手法によって提案された変数名 (型のみによって変数名を決定する、 たとえば int型ならば変数名はつねに i)

の中から選ばせる (blind test)。 なお、各プロジェクトごとに、 提案されたスコアが高いものから 10個を選んだ。

全12プロジェクト × 各プロジェクト10問 × 9人 = 計1080問のうち、 本手法の提案した変数名を選んだ人の割合は以下のようになる:

プロジェクト#本手法#全体
ant3990
antlr43490
bcel4890
compress5390
jedit2490
jhotdraw490
junit3490
lucene4090
tomcat3490
weka2990
xerces3190
xz4690
416 (39%)1080

なお、9人の被験者による合意度 (Fleiss' Kappa) は 0.45 (Moderate) であった。

ちなみに、データパスから素性を生成するとき、 異なるアルゴリズムを使った場合の実験結果は以下のようであった。

MethodsInterproc.NameTypeLength%
5+++539%
5+++314%
5+++17%
5+--56%
5++-510%
5+-+58%
5-++510%
1+++516%

また、各プロジェクトごとに生成された提案の数とそのスコア内訳は以下のようである:

プロジェクト90%80%70%60%50%40%30%20%10%0%
ant21561144302637103119853061578413022
antlr47941741442173444536589652912
bcel35125510320329450164410422862
compress4616459821427650482210122997
jedit7145819934560696516212349383710001
jhotdraw122268150251441648895128416955466
junit6122740898989149182135818
lucene9184813729256794517352913454611210
tomcat13421122645441165172930384897842220226
weka922632004079702054346869051067224770
xerces527491312894357561263192433718250
xz3981424337486157111519

実験2-2. システムの出力をもとに各プロジェクト用の修正パッチを作成し、作者のもとに送る

各プロジェクトに対して計12個のパッチを作成。

Hello, we're developing an automated system that detects inconsistent variable names in a large software project. Our system checks if each variable name is consistent with other variables in the project in its usage pattern, and proposes correct candidates if inconsistency is detected. This is a part of academic research that we hope to publish soon, but as a part of the evaluation, we applied our systems to your projects and got a few interesting results. We carefully reviewed our system output and manually created a patch to correct a few variable names. We would be delighted if this patch is found to be useful. If you have a question or suggestion regarding this patch, we'd happily answer. Thank you.

RQ3. 変数名の予測には説得力があったか?

実験3-1. 提案した名前とその論拠を表示し、説得力があるかどうかを判定する

各プロジェクトごとに 本手法で提案されたスコアが高いものから低いものまで 5個を選び、 証拠とともに評価させる:

  1. 提案した名前を支持する確固たる説得力がある。
  2. 提案した名前がそれなりに正しそうな証拠である。
  3. 提案した名前が正しそうという確信はもてない。
  4. わからない。

全12プロジェクト × 各プロジェクト5問 × 9人 = 計540問の結果:

プロジェクト#a#b#c#d#全体
ant7830045
antlr48829045
bcel101520045
compress5534145
jedit6633045
jhotdraw4536045
junit2736045
lucene31427145
tomcat7731045
weka11034045
xerces61028145
xz1735245
601023735540

#a + #b の割合が全体の 30% (162/540) しかないが、 以下に示すように、スコアが高い提案は #a + #b の評価になる 割合が高く、スコアが低いものは #c になる割合が高い:

%score#total#a+#b#c
> 80%20771 (34%) 133 (64%)
> 60%10840 (37%)66 (61%)
> 40%10820 (19%)88 (81%)
> 20%10824 (22%)84 (78%)
> 0%97 (78%)2 (22%)

しかしこれは本手法によるスコアの妥当性を示しているにすぎず、 全体としては論拠の有用性は示せなかった。 (そもそもどうすれば有用性を示せたのか、いまとなっては謎である)

Anecdotal Examples

7. Discussion

本実験の結果から…

  1. データパスは変数の使われ方を表現するのにある程度有効。
  2. 実際のプロジェクトで、39% の確率でもとの開発者よりもよい名前を提案できた。
  3. 各提案で人間に納得できる論拠がきちんと示せたかどうかは証明できなかった。

7.1. Threats to Validity

Internal Validity (実験はRQに答えているか?)

  1. 被験者のプログラミング能力に左右されている。
    → しかし、もし被験者がランダムに選んでいるとしたら Kappa = 0 になるはずなので、被験者9人は ある程度の一般的な基準を有しているといえる。
  2. 被験者が評価コードに対する前提知識があるかもしれない。
    → 今回のケースではない。
  3. Precision のみで Recall が測定できていない。
    → 「完璧なソースコード」を取得することが不可能なので、 Recall は無理。

External Validity (一般化可能か?)

  1. 現状では、プログラミング言語は Java のみ。
  2. 実験プロジェクトおよび被験者の数が充分でない。
  3. データパスの取得方法が完璧でない。 (実装上のミス、機能の欠損)
  4. 動的ディスパッチ、variable aliasing を全部追えていない。
  5. データパス全体を比較せず、エッジを比較するだけの判定では不十分である。
  6. Naive Bayes classifier の性能が不十分である。

8. 本論文の貢献

よい論文とは?


Yusuke Shinyama