リファクタリング支援のための コードクローンに含まれる識別子の対応関係分析 ○工藤 良介 伊達 浩典 石尾 隆 井上克郎(阪大)
概要 ソフトウェアの保守コストを抑えるためコードクローンを集 約することが提案されている 全てのコードクローンが集約できるわけではない 集約の期待されるクローン,困難なクローンの割合を調 査する 集約の期待されるクローンは全クローンの約4分の1程 度であることがわかった
コードクローン(クローン)とは ソースコード中の同一,または類似したコード片の組 (用語)クローンセット:クローンとなっているコードの集合 クローンのひとつにバグが見つかった場合,その他のクローンについても 修正を検討する必要がある コードクローンの存在が保守コストを増大させる要因となっている (用語)クローンセット:クローンとなっているコードの集合 バグ調査 バグ発見 クローンとなっている コード片 バグ調査
コードクローンとは(2) コードクローンは3つのタイプに分けられる タイプ1 タイプ2 タイプ3 タイプ1:空白、改行などを除き同一のもの タイプ2:変数名や型名など識別子名が異なるもの タイプ3:一部に文の挿入や削除が行われたもの x = y + z; if(x >=0) a = getValue(x); … タイプ1 x = y + z; if(x >=0) a = getValue(x); … k = y + z; if(k >=0) a = getValue(k); … タイプ2 x = y + z; System.out.println(x); if(x >=0) a = getValue(x); タイプ3
コードクローンに対するリファクタリング リファクタリングとは コードクローンのリファクタリング 全てのクローンが集約できるとは限らない 外部から見た振る舞いを変えずに内部を整理すること コードクローンのリファクタリング 全てのクローンが集約できるとは限らない 手続きとして独立させる hoge(){ } 手続き呼出しに変更 hoge(); hoge(); 集約 こういうふうにリファクタリングしますみたいなの 手続きを作る場合: 参照する変数の違い = 手続きの引数として扱う 参照する変数の型の違い = 扱えることもある 参照する手続きの違い = 手続きとしてはまとめにくい すべてのクローンが集約できるとは限らない クローン間の識別子名の違いを吸収する必要性がある hoge();
例)コードクローンの集約 引数を設定 違いを吸収 できない hoge(int y,int t){ int y = getY(); System.out.println( “Freefall:”); int t =freefall(height) } hoge(){ int y = ???(); System.out.println( “Freefall:”); int t =freefall(height) } 集約可 集約困難 … int height = getY(); System.out.println( “Freefall:”); int time =freefall(height) … int y = getY(); System.out.println( “Freefall:”); int t =freefall(y) … int y = getHeight(); System.out.println( “Freefall:”); int t =freefall(y) 変数名が違う 手続き名が違う
集約の期待されるクローン,困難なクローン 集約の期待されるクローン 集約の困難なクローン 完全に一致するクローン 識別子名の中で変数名のみが異なるクローン 手続きに引数を設定することで違いを吸収 集約の困難なクローン 識別子の対応が1対1でないクローン(後述) 型名が異なるクローン メソッド名が異なるクローン クローンになっているコード片の間で識別子を比較し,識別子名の変更状況を調査 変更された識別子の種類 変更された識別子の対応関係
研究目的・内容 目的 研究内容 調査対象 コードクローンのリファクタリングがどの程度適用できるのかを 確認する 識別子の対応関係を調べ,リファクタリングの候補となるクロー ンの割合を調べる 調査対象 Java で書かれたオープンソースソフトウェア 2142個 Apache.Commons,SourceForge.net から収集したもの 細かいので省く: CCFinder 標準の長さ 30トークン以上のコード片,RNR=0.5以上
調査手法 ソースコード中のクローンセット情報を出力する ソースコード中に出現する識別子のリストを作成する クローン中の識別子をそれぞれリストから抽出し,比較 することで,識別子の対応関係表を作成する 対応関係表によってクローンセットを分類する ④ ① クローンセット情報 ③ 2 クローン検出 3 比較 ソースコード 対応関係表 ② 識別子リスト
調査手法 ソースコード中のクローンセット情報を出力する ソースコード中に出現する識別子のリストを作成する クローン中の識別子をそれぞれリストから抽出し,比較 することで,識別子の対応関係表を作成する 対応関係表によってクローンセットを分類する ④ ① クローンセット情報 ③ 2 クローン検出 3 比較 ソースコード 対応関係表 ② 識別子リスト
1.クローンセットの検出 コードクローン検出ツールCCFinderを利用 コードクローンの位置情報を出力 タイプ1及びタイプ2のクローンセットを検出 … int height = getY(); System.out.println( “Freefall:”); int time =freefall(height) … int y = getY(); System.out.println( “Freefall:”); int t =freefall(y) … int y = getHeight(); System.out.println( “Freefall:”); int t =freefall(y)
調査手法 ソースコード中のクローンセット情報を出力する ソースコード中に出現する識別子のリストを作成する クローン中の識別子をそれぞれリストから抽出し,比較 することで,識別子の対応関係表を作成する 対応関係表によってクローンを分類する ④ ① クローンセット情報 ③ 2 クローン検出 3 比較 ソースコード 対応関係表 ② 識別子リスト
2.識別子のリスト作成 ANTLRを利用してJavaの構文解析を行う 出力する情報は{識別子名,出現位置,種類} 識別子の種類は{変数,メソッド,型,レシーバ,リテラル,その他} とした 名前 行 桁 種類 int 10 1 型 y 5 変数 getY 9 メソッド System.out. 11 レシーバ println 12 “Freefall:” 19 リテラル t freefall 18 … int y = getY(); System.out.println( “Freefall:”); int t = freefall(y)
調査手法 ソースコード中のクローンセット情報を出力する ソースコード中に出現する識別子のリストを作成する クローン中の識別子をそれぞれリストから抽出し,比較 することで,識別子の対応関係表を作成する 対応関係表によってクローンを分類する ④ ① クローンセット情報 ③ 2 クローン検出 3 比較 ソースコード 対応関係表 ② 識別子リスト
3.識別子の対応関係表の作成 クローン間で識別子名の比較を行い,対応関係表を作 成する 対応する識別子名 識別子の種類 対応関係 クローン部分の識別子をリストから抽出することで比較 を行う コード片1 コード片2 コード片3 種類 1対1対応 完全一致 {A} {D} {H} 変数 ○ × 1 リテラル {B} {E} {Y,Z} 「対応する位置にある識別子」があること,「名前の対応関係」と「種別の対応関係」を区別しないと説明は難しい
(定義)識別子の対応関係 識別子名の対応関係を以下のように定義する 完全一致 不一致 1対1対応 N対N対応 クローン間で識別子名をお互いに1対1で置換できる N対N対応 1対1対応ではない識別子の対応がある a = 1; c = a + b; a⇔a 完全一致 a⇔x a = 1; c = a + b; x = 1; c = x + b; 1対1対応 y = 1; c = z + b; N対N対応 a⇔x,y
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル {B} {E} {Y} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル {B} {E} {Y} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル {B} {E} {Y,Z} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル {B} {E} {Y,Z} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 {1} リテラル {B} {E} {Y,Z} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
例)対応関係表の作成 A=1; B=B+A; D=1; E=E+D; H=1; Y=Z+H; コード片1 コード片2 コード片3 コード片1 種類 1対1対応 完全一致 {A} {D} {H} 変数 ○ × {1} リテラル {B} {E} {Y,Z} ・1対1対応の例が間違い.a-d-h の対応関係のはず ・字を大きく [このままだと遠くからはまったく見えない]
調査手法 ソースコード中のクローンセット情報を出力する ソースコード中に出現する識別子のリストを作成する クローン中の識別子をそれぞれリストから抽出し,比較 することで,識別子の対応関係表を作成する 対応関係表によってクローンを分類する ④ ① クローンセット情報 ③ 2 クローン検出 3 比較 ソースコード 対応関係表 ② 識別子リスト
4.クローンの分類 以下の2点からクローンを分類 ・どの種類の識別子名が異なるか ・その識別子に1対1対応でない対応関係が含まれるかどうか ・どの種類の識別子名が異なるか ・その識別子に1対1対応でない対応関係が含まれるかどうか コード片1 コード片2 コード片3 種類 1対1対応 完全一致 {A} {D} {H} 変数 ○ × 1 リテラル {B} {E} {Y,Z} このクローンは「変数名が異なり」「1対1でない対応を含む」と分類 変数名 A, D, H は1対1対応.コード片1に出現するすべての変数 A について,ADという変換を適用するとコード片2に変換できる. (どのコード片からでも,他のコード片に変換できる) 変数B, F, {G,P} はN対N対応.1対1変換が定義できないから. 識別子以外の部分( “=” や “;” ) は,CCFinder は完全一致で検出してくれる. 変数名A⇔D⇔H ・・・・・・・・ 1対1対応 変数名B⇔E⇔{Y,Z} ・・・・・・・・ N対N対応 1対1でない対応があると 集約が難しい
調査する点 集約の期待されるクローン 集約の困難なクローン 完全に一致するクローン 識別子の中で変数名のみが異なり,それが1対1対応になるク ローン 集約の困難なクローン 識別子の対応が1対1でないクローン メソッド名が異なるクローン 型名が異なるクローン クローンになっているコード片の間で識別子を比較し,識別子名の変更状況を調査 変更された識別子の種類 変更された識別子の対応関係
調査対象 Java で書かれたオープンソースソフトウェア 2142個 Apache.Commons,SourceForge.net から収集したもの 総クローンセット数 695484
調査結果 総クローンセット数 695484 集約の期待できるクローン・・・ 26.0% 集約の困難なクローン ・・・ 72.5% 総クローンセット数 695484 集約の期待できるクローン・・・ 26.0% 集約の困難なクローン ・・・ 72.5% ・集約の期待できるクローン 分類 クローン数 総数に対する割合 全ての識別子が1対1対応 完全一致 156284 22.4% 100% 変数のみ異なる 27634 4.0% 91.2% 型が変更 162622 23.4% 78.3% メソッドが変更 288419 41.5% 74.0% 型またはメソッドが変更 364005 52.4%
調査結果 総クローンセット数 695484 集約の期待できるクローン・・・ 26.0% 集約の難しいクローン ・・・ 72.5% 総クローンセット数 695484 集約の期待できるクローン・・・ 26.0% 集約の難しいクローン ・・・ 72.5% ・集約の困難なクローン 分類 クローン数 総数に対する割合 完全一致 156284 22.4% 1対1対応のみ 310184 44.6% 1対1でない対応を含む 229016 32.9% 分類 クローン数 総数に対する割合 全ての識別子が1対1対応 型が異なる 162622 23.4% 78.3% 手続きが異なる 288419 41.5% 74.0% 型または手続きが異なる 364005 52.4% 75.5% 型が変更 162622 23.4% 78.3% メソッドが変更 288419 41.5% 74.0% 型またはメソッドが変更 364005 52.4%
まとめと今後の課題 識別子の対応という観点からリファクタリング候補を抽出 した 集約の期待されるクローンは全体の4分の1程度であっ た これらの候補が実際にどの程度リファクタリングできるか, またすべきであるかを調査したい