CamelCase
/ snake_case
の使い分け程度。
wrong name
" 等を検索すると、
100万以上の結果が返される。
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やろ。
}
プロジェクト | 名詞トップ5 | 動詞トップ5 |
---|---|---|
ant (ビルドツール) | file, name, function, class, output | set, get, add, create, execute |
antlr4 (構文解析) | string, rule, token, code, name | get, set, add, remove, visit |
bcel (バイトコード解析) | constant, class, string, type, value | visit, get, accept, set, dump |
compress (データ圧縮) | stream, entry, archive, data, input | get, set, read, write, close |
jedit (テキストエディタ) | jj, action, line, string, buffer | get, set, add, run, remove |
jhotdraw (図形描画) | action, figure, color, name, property | get, set, create, add, read |
junit (ユニットテスト) | test, class, method, failure, runner | get, assert, run, test, validate |
lucene (文書検索) | doc, next, string, value, bytes | get, set, compare, add, read |
tomcat (Webサーバ) | name, string, session, max, class | get, set, add, remove, create |
weka (機械学習) | text, tip, options, action, string | get, set, add, create, remove |
xerces (XMLパーサ) | element, name, decl, type, impl | get, set, create, add, start |
xz (データ圧縮) | stream, input, size, output, memory | get, write, read, close, set |
view
"
view
"
message
と msg
など
変数名の使い方に着目する:
open()
の返り値を受けとり、write()
の第1引数に渡される」 → B「その変数名が out
である」
out = open(...); write(out, ...);
open()
の返り値を受けとり、write()
の第1引数に渡される」
??? = open(...); write(???, ...);
???
が out
ならば一貫性あり。
一般には、ある規則がつねに 100% 成り立つということはあまりない。 そこで、確率的に成り立つ規則を導入する。
ここでは、ある変数に代入される値が通る一連の処理 (または関数の引数および返り値が通るデータフロー) を、 その変数の「使い方」と定義する。 ここではこのデータフローを「データパス」と呼ぶ。
問. 以下の例で変数 line
に着目する。
private BufferedReader fp; public String get() { String line = fp.readLine(); int i = line.indexOf(' '); return line.substring(0, i); }
グラフ化するとこうなる:
グラフ中の赤線部分が
変数 line
のデータパスである:
fp.readLine()
→ line
→ #this:indexOf()
→ #arg1:substring()
これは、変数 line
になにが代入され、
それがどのように使われるかを示している。
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
となっていることに着目。
これをグラフ化するとこうなる:
最終的に、
変数 line
のデータパスは:
fp.readLine()
→ line
→ #this:indexOf()
→ #arg1:substring()
→ assign:name
→ L:+
→ #arg0:println()
line
の使われ方」を表すことになる。
単純化のため、いまのところパスの分岐は考慮しない。
以上のようなデータパスを、 プログラム中のすべての変数に対して (interprocedural で) 抽出し、 変数名との相関を学習する。 ここでは「データパスを与えると、変数名を予測する」ようなモデルを学習する。
たとえば、以下のようなデータパスが出てきたら:
fp.readLine()
→ ???
→ #this:indexOf()
→ #arg1:substring()
→ assign:name
→ L:+
→ #arg0:println()
ここでの ???
= line
ではないか、
と予測するということである。もしここで line
以外の名前が使われていたら、
その変数名は (この使われ方に対しては) 一貫していない、ということになる。
具体的には、
データパス
→ outputBufferName
ではなく
output
buffer
name
CamelCase
あるいは snake_case
で区切られる)
#this:indexOf()
→ #arg1:substring()
→ assign:name
というデータパスは、
#this:indexOf()
→ #arg1:substring()
#arg1:substring()
→ assign:name
以下のプロジェクトに対して検証をおこなった: (実際には #エッジ < #データパス)
プロジェクト | KLOC | #変数 | #ノード | #エッジ |
---|---|---|---|---|
ant (ビルドツール) | 112k | 23,971 | 350k | 5,211k |
antlr4 (構文解析) | 31k | 7,131 | 74k | 1,103k |
bcel (バイトコード解析) | 31k | 6,583 | 80k | 1,190k |
compress (データ圧縮) | 24k | 5,896 | 69k | 929k |
jedit (テキストエディタ) | 115k | 21,977 | 294k | 6,106k |
jhotdraw (図形描画) | 80k | 17,367 | 235k | 2,351k |
junit (ユニットテスト) | 9k | 2,384 | 21k | 280k |
lucene (文書検索) | 109k | 30,341 | 414k | 7,146k |
tomcat (Webサーバ) | 238k | 49,275 | 649k | 11,799k |
weka (機械学習) | 324k | 59,274 | 943k | 13,224k |
xerces (XMLパーサ) | 114k | 21,852 | 314k | 7,017k |
xz (データ圧縮) | 7k | 1,825 | 23k | 299k |
(疑問: 論文では数値の詳細を略すのは悪印象か?)
被験者: 著者3人 + 大学院生6人 = 計9人
まず予備実験として、データパスを本手法に使うことの妥当性を判断する。 もしデータパスが変数名の「使われ方」をうまく表現できていれば、 「同じデータパス」をもつ異なる変数は「同じ使われ方」をしているはずである。
各プロジェクトから同じデータパスの類似度が高い (cosine > 0.90) 変数の組を選び、(もとの変数名は隠して) それらが表れているコードを 人間に見せて、同じ変数名にすべきかどうかを判定させる。
全12プロジェクト × 各プロジェクト5問 × 9人 = 計540問の結果:
プロジェクト | #a | #b | #c | #d | #全体 |
---|---|---|---|---|---|
ant | 23 | 17 | 5 | 0 | 45 |
antlr4 | 13 | 22 | 9 | 1 | 45 |
bcel | 3 | 11 | 31 | 0 | 45 |
compress | 10 | 9 | 24 | 2 | 45 |
jedit | 7 | 25 | 9 | 4 | 45 |
jhotdraw | 15 | 17 | 12 | 1 | 45 |
junit | 14 | 21 | 10 | 0 | 45 |
lucene | 18 | 21 | 6 | 0 | 45 |
tomcat | 11 | 27 | 6 | 1 | 45 |
weka | 4 | 28 | 12 | 1 | 45 |
xerces | 12 | 22 | 9 | 2 | 45 |
xz | 4 | 15 | 26 | 0 | 45 |
計 | 134 | 235 | 159 | 12 | 540 |
#a + #b の割合が全体の 68% (369/540) を占めている。 したがって、データパスが類似している変数は同じ名前をもつ可能性が高い。
なお、被験者は各組の cosine を知らされていないが、 もともと 0.90 以上の対のみに絞ってしまったので、 あまり blind test とは言えなくなくなってしまった。 (#a + #b の平均 cosine は 0.980, #c の平均 cosine は 0.976 である)
For each 変数 について、(名前は隠して) それが使われている部分を見せ
int
型ならば変数名はつねに i
)
の中から選ばせる (blind test)。 なお、各プロジェクトごとに、 提案されたスコアが高いものから 10個を選んだ。
全12プロジェクト × 各プロジェクト10問 × 9人 = 計1080問のうち、 本手法の提案した変数名を選んだ人の割合は以下のようになる:
プロジェクト | #本手法 | #全体 |
---|---|---|
ant | 39 | 90 |
antlr4 | 34 | 90 |
bcel | 48 | 90 |
compress | 53 | 90 |
jedit | 24 | 90 |
jhotdraw | 4 | 90 |
junit | 34 | 90 |
lucene | 40 | 90 |
tomcat | 34 | 90 |
weka | 29 | 90 |
xerces | 31 | 90 |
xz | 46 | 90 |
計 | 416 (39%) | 1080 |
なお、9人の被験者による合意度 (Fleiss' Kappa) は 0.45 (Moderate) であった。
ちなみに、データパスから素性を生成するとき、 異なるアルゴリズムを使った場合の実験結果は以下のようであった。
Methods | Interproc. | Name | Type | Length | % |
---|---|---|---|---|---|
5 | + | + | + | 5 | 39% |
5 | + | + | + | 3 | 14% |
5 | + | + | + | 1 | 7% |
5 | + | - | - | 5 | 6% |
5 | + | + | - | 5 | 10% |
5 | + | - | + | 5 | 8% |
5 | - | + | + | 5 | 10% |
1 | + | + | + | 5 | 16% |
また、各プロジェクトごとに生成された提案の数とそのスコア内訳は以下のようである:
プロジェクト | 90% | 80% | 70% | 60% | 50% | 40% | 30% | 20% | 10% | 0% | 計 |
---|---|---|---|---|---|---|---|---|---|---|---|
ant | 2 | 15 | 61 | 144 | 302 | 637 | 1031 | 1985 | 3061 | 5784 | 13022 |
antlr4 | 7 | 9 | 41 | 74 | 144 | 217 | 344 | 453 | 658 | 965 | 2912 |
bcel | 3 | 5 | 12 | 55 | 103 | 203 | 294 | 501 | 644 | 1042 | 2862 |
compress | 4 | 6 | 16 | 45 | 98 | 214 | 276 | 504 | 822 | 1012 | 2997 |
jedit | 7 | 14 | 58 | 199 | 345 | 606 | 965 | 1621 | 2349 | 3837 | 10001 |
jhotdraw | 12 | 22 | 68 | 150 | 251 | 441 | 648 | 895 | 1284 | 1695 | 5466 |
junit | 6 | 12 | 27 | 40 | 89 | 89 | 89 | 149 | 182 | 135 | 818 |
lucene | 9 | 18 | 48 | 137 | 292 | 567 | 945 | 1735 | 2913 | 4546 | 11210 |
tomcat | 13 | 42 | 112 | 264 | 544 | 1165 | 1729 | 3038 | 4897 | 8422 | 20226 |
weka | 9 | 22 | 63 | 200 | 407 | 970 | 2054 | 3468 | 6905 | 10672 | 24770 |
xerces | 5 | 27 | 49 | 131 | 289 | 435 | 756 | 1263 | 1924 | 3371 | 8250 |
xz | 3 | 9 | 8 | 14 | 24 | 33 | 74 | 86 | 157 | 111 | 519 |
各プロジェクトに対して計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.
各プロジェクトごとに 本手法で提案されたスコアが高いものから低いものまで 5個を選び、 証拠とともに評価させる:
全12プロジェクト × 各プロジェクト5問 × 9人 = 計540問の結果:
プロジェクト | #a | #b | #c | #d | #全体 |
---|---|---|---|---|---|
ant | 7 | 8 | 30 | 0 | 45 |
antlr4 | 8 | 8 | 29 | 0 | 45 |
bcel | 10 | 15 | 20 | 0 | 45 |
compress | 5 | 5 | 34 | 1 | 45 |
jedit | 6 | 6 | 33 | 0 | 45 |
jhotdraw | 4 | 5 | 36 | 0 | 45 |
junit | 2 | 7 | 36 | 0 | 45 |
lucene | 3 | 14 | 27 | 1 | 45 |
tomcat | 7 | 7 | 31 | 0 | 45 |
weka | 1 | 10 | 34 | 0 | 45 |
xerces | 6 | 10 | 28 | 1 | 45 |
xz | 1 | 7 | 35 | 2 | 45 |
計 | 60 | 102 | 373 | 5 | 540 |
#a + #b の割合が全体の 30% (162/540) しかないが、 以下に示すように、スコアが高い提案は #a + #b の評価になる 割合が高く、スコアが低いものは #c になる割合が高い:
%score | #total | #a+#b | #c |
---|---|---|---|
> 80% | 207 | 71 (34%) | 133 (64%) |
> 60% | 108 | 40 (37%) | 66 (61%) |
> 40% | 108 | 20 (19%) | 88 (81%) |
> 20% | 108 | 24 (22%) | 84 (78%) |
> 0% | 9 | 7 (78%) | 2 (22%) |
しかしこれは本手法によるスコアの妥当性を示しているにすぎず、 全体としては論拠の有用性は示せなかった。 (そもそもどうすれば有用性を示せたのか、いまとなっては謎である)
org/apache/bcel/Const.java: - public static short getNoOfOperands(final int index) { - return NO_OF_OPERANDS[index]; + public static short getNoOfOperands(final int opcode) { + return NO_OF_OPERANDS[opcode];
gjt/sp/jedit/bsh/classpath/BshClassPath.java: - void errorWhileMapping( String s ) { + void errorWhileMapping( String msg ) { ...
org/apache/jasper/compiler/Generator.java - String pkgName = className.substring(0, lastIndex); - genPreamblePackage(pkgName); + String packageName = className.substring(0, lastIndex); + genPreamblePackage(packageName);
org/apache/xerces/impl/xpath/regex/RegexParser.java - ReferencePosition(int n, int pos) { + ReferencePosition(int n, int offset) {
src/org/tukaani/xz/lz/Hash234.java - void normalize(int normalizeOffset) { - LZEncoder.normalize(hash2Table, HASH_2_SIZE, normalizeOffset); + void normalize(int normalizationOffset) { + LZEncoder.normalize(hash2Table, HASH_2_SIZE, normalizationOffset); ...
本実験の結果から…