LoginSignup
475
476

More than 1 year has passed since last update.

僕にそのコピペを直せというのか(2021年4月版)

Last updated at Posted at 2014-07-29

ソースコードをコピーアンドペーストで作成したコードクローンの方が信頼性がいいという言説もないことはないですが、多くの場合、コードクローンの存在は我々を土壇場で苦しめることが多いです。

担当者が逃げた、ソースコードを、夜遅くまで修正してリリースしたあとに、実は「コピペで作っているから別の所も全部なおしてテストしてね」とか言われて、作業をやり直すのは、とてもとても悲しいものです。

殆ど同じソースコードを間違い探しのように微妙に変更して、修正していくのは屈辱の極みです。

土壇場において、このような罰ゲームを避けるために、コピペの頻度というものは常時監視しておき、異常な頻度の場合はただちに是正すべきです。

ここでは、コピペの検出を行うツールについて説明します。

PMD-CPD

PMDはJavaで実装されたJavaのソースコードの潜在的問題を検出するツールです。
http://pmd.sourceforge.net/snapshot/

この機能の一部に重複したコードを検出するCPDコマンドが存在しており、次のプログラミング言語の重複を検出可能です。
・Java
・JSP
・C++
・Ruby
・Fortran
・PHP
・C#
・PLSQL
・Ecmascript

多くのプログラミング言語を解析してくれますが、vueファイルは解析してくれないので注意しましょう。

インストール方法

下記のいずれかのファイルをダウンロード、解凍して任意のフォルダに展開してください。
http://sourceforge.net/projects/pmd/files/pmd/

実行例

Windowsでの実行例

cpd --minimum-tokens 50 --language ecmascript --format text --encoding utf8 --files C:\tool\clonedigger\test\ > result.txt

Linuxでの実行例

bin/run.sh cpd --minimum-tokens 35 --format xml --language ruby --files /var/lib/redmine/app/ > result.xml

パラメータ

パラメータはwindowsとlinuxで共有です。

パラメータ 説明
--minimum-tokens 重複を検知するトークン数を指定します。
--format text,xml,csvが選択できます。xmlで出力した場合はjenkinsで使用できます。
--language プログラミング言語の種類を指定します.
--files 検査対象のソースコードのディレクトリを指定します。これは再帰的に検出します。
--encoding 検査対象のソースコードのエンコードを指定します

GUI

bin/cpdgui.batを実行することでGUIで動作することもできます。
clone.png

jscpd (2021/04/30追加)

jscpdはnode.jsで動作するコードクローンの検出ツールです。

https://www.npmjs.com/package/jscpd
https://github.com/kucherenko/jscpd

多くのプログラミング言語を解析でき、PMD-CPDでは無理だったvueファイルを解析してくれます。

なお、今回は以下の環境で検証しています。

  • macOS 10.15.7
  • node v12.20.1
  • jscpd v3.3.25

インストール方法

npm install -g jscpd

ソースコードからのビルド方法

バグ対応とかしたい場合は以下の手順でビルドを行うことができます。

git clone https://github.com/kucherenko/jscpd.git 
yarn install
yarn bootstrap
# ファイルを修正
yarn build
npx jscpd --version

実行例

以下の例では指定のフォルダ以下のコードクローンを検知します。

jscpd ../hoge/src

コードクローンを検出した場合は、発生箇所を出力します。
image.png

また、最後にサマリを出力します。
image.png

出力形式はデフォルトではコンソールに出力するだけですが、オプションにより、htmlやjson、PMD-CPDが出力するxml形式などに出力することができます。
以下のコマンド例はhtmlレポートを作成する例になります。

jscpd hoge/src --output=./tmp --reporters=html

FAQ

検知されるべきコードの重複が検知されていません。

jscpdはファイルの行数やファイルサイズの大きさでチェックをスキップします。デフォルトの設定では1000行を超えるか、100kbを超えるファイルはチェックしていません。
大きなファイルを含めてチェックするには以下のようなオプションを使用して、制限値を変更します。

jscpd hoge/src --max-size=10Mb --max-lines=10000

そのほかのオプションについては下記を参照してください。
https://www.npmjs.com/package/jscpd#options

htmlレポートが壊れている

vueやhtmlファイルを対象にjscpdを実行した場合、htmlレポートが壊れている場合があります。
これは、jscpd/html-reporterの不具合と思われます。

この実装だと、重複コードのjsonに「</script>」という文字列があった場合に、htmlの表示がうまくいきません。
以下のように修正する必要があります。

--- packages/html-reporter/src/index.ts
+++ packages/html-reporter/src/index.ts
@@ -22,7 +22,7 @@ export default class HtmlReporter implements IReporter {
           '<body>',
           `<body><script>
                        // <!--
-                       window.initialData = ${JSON.stringify(json, null, '  ')};
+                       window.initialData = ${JSON.stringify(json, null, '  ').replace(new RegExp('</script>', 'g'), '</" + "script>').replace(new RegExp('<script>', 'g'), '<" + "script>')};
                        // -->
                        </script>`
         ))
~                

修正方法としては、当該ファイルを修正してビルドするか、グローバルにインストールした場合は、以下のファイルを直接直す方法もあります。

maxOSでnodebrewを使用した場合:
~/.nodebrew/current/lib/node_modules/jscpd/node_modules/@jscpd/html-reporter/dist/index.js

複数ファイルの検知がうまく動作しません。

例えば、a,b,c,dというファイルで重複があった場合、aとb、cとd、bとdが重複していると表示されます。

現時点の制限です。
https://github.com/kucherenko/jscpd/issues/221

Clonedigger

ClonediggerはPythonとJavaで実装されたコピペ検出ツールです。
http://clonedigger.sourceforge.net/

2021/04/30注釈 2013年が最終更新日なので、2021年現在では別の手段をとった方がいいでしょう

検出可能なプログラミング言語は次の通りです。

・python
・java
・lua
・javascript

python以外のプログラミング言語の検出には、後述するコツがあります。

インストール方法

easy_install clonedigger

実行例

Pythonの検出例

ファイルを指定した場合

clonedigger -l python -o ./test.html C:\tool\clonedigger\test\test_utf8.py

フォルダを指定した場合

clonedigger -l python -o ./test.html C:\tool\clonedigger\test\test_utf8.py

フォルダを指定した場合は、サブフォルダも検出します。
-oで指定したファイルに以下のようなHTMLを作成します。

clone.png

もし、次のように--cpd-outputオプションを使用するとXML形式で出力されます。この出力形式はPMD/CPDと同じものになります。

clonedigger -l python --cpd-output -o test.xml C:\tool\clonedigger\test\python\

Javaの検出例

Python以外のソースコードを検出する場合、カレントディレクトリにjava_antlrが存在しないと動作しません。
Windows、Python2.7の場合は次のような操作が必要になります。

cd C:\Python27\Lib\site-packages\clonedigger-1.1.0-py2.7.egg\clonedigger
clonedigger -l java --cpd-output -o test.xml C:\tool\clonedigger\test\test.java

JavaScriptの検出例

JavaScriptを検出するときには、カレントディレクトリにjs_antlrが存在しないと動作しません。
Windows、Python2.7の場合は次のような操作が必要になります。

cd C:\Python27\Lib\site-packages\clonedigger-1.1.0-py2.7.egg\clonedigger
clonedigger -l js --cpd-output -o test.xml C:\tool\clonedigger\test\test.js

ブラウザで動作させるJavaScriptは次のようないい加減な実装でも動作します。

  // 最後のコンマが余分
  var questions = [
    {message: "ああああ", category: Category.emotionalExhaustion} ,
  ];

しかし、clonediggerではこのような不正な記述があると、解析が中断されエラーとなります。この時次のようなエラーメッセージが表示されるので、修正のヒントになるでしょう

line 14:2 rule arrayItem failed predicate: { input.LA(1) == COMMA }?

AIST CCFinderX

コメントで教えてもらった、AIST CCFinderXは下記のページからダウンロードできます。
http://www.ccfinder.net/ccfinderxos-j.html

動作させるに32ビットのJavaとPython2.6(上でも下でもダメ)が必要になります。
Windowsのバイナリをダウンロードした場合、32bitで動作させないと動かないので、gemx.batは以下のように修正する必要があります。

gemx.bat
set PATH=C:\Windows\SysWOW64;C:\TracLight\python;C:\TracLight\python\python\Scripts\;%~dp0\scripts
set CCFINDERX_PYTHON_INTERPRETER_PATH=C:\TracLight\python\python.exe

以下の画像はgemx.batで検知した画面の例になります。
無題.png

その他、散布図なども表示できます。これらのGUIは魅力的といえるでしょう。

コマンドラインから実行する場合は次のようになります。

ccfx p java -d c:\dev\java\

この時出力される、a.ccfxdはバイナリファイルでGemXで開くことが可能です。以下のコマンドでテキスト形式に変換できます。

>ccfx p a.ccfxd > test.txt

VisualBasicの対応

この手のツールとしてはめずらしくVisualBasicを対応しています。
VB6やVBAで作ったコードも解析しているようです。しかし、clsファイルは認識しないのでccfx_prep_scripts.iniを以下のように修正してください。

visualbasic=.vb;.bas;.frm;.cls

その他

・マルチバイトを扱っているソースに対する処理が失敗していることがあります。
・Jenkinsとの連携は調べた限り、簡単にはできそうもないです。
・更新履歴のページがデッドリンクになっていることから、現在はもうメンテナンスされていないようです。
 ソースコードがあるので、必要に応じて自分で治す必要があるでしょう。

Jenkinsによる監視

JenkinsのViolationsプラグインを用いることでコードクローンの遷移を監視できます。
https://wiki.jenkins-ci.org/display/JENKINS/Violations

  1. Jenkinsのスクリプト上にてワークスペース上にXMLを生成します。

  2. プロジェクトの設定のビルド後の処理でReportViolationsのcpdに1.のXMLを指定します。
    clone.png

  3. 各ビルドを行うことで以下のようなレポートが作成されます。
    clone.png

475
476
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
475
476