概要
大規模なリファクタリングを安全に行うため、Google製のJava静的解析ツールであるError Proneにて、自作のRefaster templateに対してPatch作成機能を利用したソースコードの一括置換を試みました。
本記事では、その手順をまとめています。
実行手順
1. Refaster Templateクラスを作成する
例: Apache Commons DbUtilsのDbUtils#closeQuietlyの呼び出しの効率化を題材とします。
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.apache.commons.dbutils.DbUtils;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
public class UseCloseQuietly {
@BeforeTemplate
public void calledCloseQueitly3Times(Connection conn, PreparedStatement ps, ResultSet rs) {
DbUtils.closeQuietly(rs);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(conn);
}
@AfterTemplate
public void optimizedMethod(Connection conn, PreparedStatement ps, ResultSet rs) {
DbUtils.closeQuietly(conn, ps, rs);
}
}
ただし、(おそらく) @BeforeTemplate
の構文木が複雑になる場合(if文やtry-catchが多く含まれている場合など)は、後述のrefasterファイルによるPatchを生成できませんでした。小分けにすると動作することもあったので、現状ではサポートしているケースが限られているのだと考えています。
2. refasterファイルを生成する
error_prone_refasterや参照しているライブラリのjarファイルをクラスパスに含めた状態で、javacコマンドを実行します。(要: Java 11)
すると、 myrule.refaster
ファイルが生成されます。
javac \
-cp "error_prone_refaster-2.26.1.jar;commons-dbutils-1.8.1.jar" \
"-Xplugin:RefasterRuleCompiler --out myrule.refaster" \
UseCloseQuietly.java
3. Gradleプロジェクトに適用する
- 生成されたmyrule.refasterを対象のGradleプロジェクトに配置する
- gradle-errorprone-pluginを利用するため、build.gradleに下記の内容を追記する
-
gradle clean compileJava
を実行すると、error-prone.patch
が生成される -
patch -p0 -u -i error-prone.patch
を実行し、パッチを適用する
plugins {
id("net.ltgt.errorprone") version "3.1.0"
}
import net.ltgt.gradle.errorprone.CheckSeverity
compileJava {
options.errorprone.checks = ['Refaster': CheckSeverity.ERROR]
options.errorprone.errorproneArgs = ['-XepPatchChecks:refaster:' + buildscript.sourceFile.getParent() + '/myrule.refaster',
'-XepPatchLocation:' + buildscript.sourceFile.getParent()]
options.errorprone.checkOptions = ['Refaster': 'NamePattern=.*']
}
dependencies {
errorprone("com.google.errorprone:error_prone_core:2.26.1")
}
なお、-XepPatchLocation:IN_PLACE
と設定すれば、修正を直接適用できます。
参考: Can Error Prone Auto-Apply Suggested Fixes?
結果
対象プロジェクトのソースコードに対して、下記のような変更が一括で適用されます。
変数名が異なっていても問題なく置換されますし、必要なimport文も自動で生成されます。
(余計な空行があるのはご愛嬌…別途、コードフォーマッターを適用しましょう)
} finally {
- DbUtils.closeQuietly(rs);
- DbUtils.closeQuietly(ps);
- DbUtils.closeQuietly(conn);
+ DbUtils.closeQuietly(conn, ps, rs);
+
+
}