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

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

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

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

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

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

# 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](https://qiita-image-store.s3.amazonaws.com/0/47856/3d202fc4-eefc-3685-0eae-76c832273881.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/f9c130c5-4200-09be-0142-5f5431ee33f1.png)


また、最後にサマリを出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/a6004ecb-75e7-ee48-7018-9eac5825930e.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の不具合と思われます。

https://github.com/kucherenko/jscpd/blob/master/packages/html-reporter/src/index.ts#L25

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

```patch
--- 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](https://qiita-image-store.s3.amazonaws.com/0/47856/9471933c-f8d9-7377-2b73-460ca6a8d4cf.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は次のようないい加減な実装でも動作します。

```js
  // 最後のコンマが余分
  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](https://qiita-image-store.s3.amazonaws.com/0/47856/cbd0b1bc-0543-09b5-d0a9-e2e449e33391.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](https://qiita-image-store.s3.amazonaws.com/0/47856/221dfe69-e158-d9cd-96e2-6268ee6feee5.png)


3. 各ビルドを行うことで以下のようなレポートが作成されます。
![clone.png](https://qiita-image-store.s3.amazonaws.com/0/47856/6b96a200-44c0-4447-0ac1-327e0aa87645.png)