是否有一个命令行工具可以自动修复 Java 源代码中非格式化但看似简单的 CheckStyle 问题,例如:
- 避免内联条件
- 使“xxx”成为静态方法
我知道有各种工具可以fix formatting有些 IDE 具有相当先进的 quick fixers但到目前为止,我找不到任何可以在源代码文件夹上递归运行或集成到提交 Hook 中的东西。
最佳答案
听起来是一个不错的挑战,但我也无法找到可以做到这一点的自动工具。正如您已经描述的,有很多选项可以更改代码格式。对于其他小问题,您也许可以从命令行运行 Checkstyle 并过滤掉可修复的警告。用于解析和更改 Java 源代码的库可以帮助实际进行更改,例如 JavaParser 。也许您可以使用 JavaParser 等 Java 源代码操作工具在相对较短的时间内编写一个自定义工具。
(还有其他工具,如 ANTLR 可以使用;有关更多想法,请参阅 Stack Overflow 上的这个问题: Java: parse java source code, extract methods 。一些库,如 Roaster 和 JavaPoet 不会解析方法的主体,这使它们不太适合这种情况。)
作为一个非常简单的示例,假设我们有一个小型 Java 类,Checkstyle 为其生成两条消息(使用简约的 checkstyle-checks.xml
Checkstyle 配置文件,仅检查 FinalParameters
和 FinalLocalVariable
):
// Example.java:
package q45326752;
public class Example {
public static void main(String[] arguments) {
System.out.println("Hello Checkstyle...");
int perfectNumber = 1 + 2 + 3;
System.out.println("Perfect number: " + perfectNumber);
}
}
Checkstyle warnings:
java -jar checkstyle-8.0-all.jar -c checkstyle-checks.xml Example.java
[ERROR] Example.java:4:29: Parameter arguments should be final. [FinalParameters]
[ERROR] Example.java:7:13: Variable 'perfectNumber' should be declared final. [FinalLocalVariable]
使用 JavaParser,这两个警告可以像这样自动修复(代码试图演示这个想法;现在已经忽略了某些部分):
// AutomaticCheckstyleFix.java:
package q45326752;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.*;
import java.io.File;
import java.io.FileNotFoundException;
public class AutomaticCheckstyleFix {
private MethodDeclaration bestMatchMethod;
private int bestMatchMethodLineNumber;
private Statement statementByLineNumber;
public static void main(final String[] arguments) {
final String filePath = "q45326752\\input\\Example.java";
try {
new AutomaticCheckstyleFix().fixSimpleCheckstyleIssues(new File(filePath));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void fixSimpleCheckstyleIssues(File file) throws FileNotFoundException {
CompilationUnit javaClass = JavaParser.parse(file);
System.out.println("Original Java class:\n\n" + javaClass);
System.out.println();
System.out.println();
// Example.java:4:29: Parameter arguments should be final. [FinalParameters]
MethodDeclaration methodIssue1 = getMethodByLineNumber(javaClass, 4);
if (methodIssue1 != null) {
methodIssue1.getParameterByName("arguments")
.ifPresent(parameter -> parameter.setModifier(Modifier.FINAL, true));
}
// Example.java:7:13: Variable 'perfectNumber' should be declared final.
// [FinalLocalVariable]
Statement statementIssue2 = getStatementByLineNumber(javaClass, 7);
if (statementIssue2 instanceof ExpressionStmt) {
Expression expression = ((ExpressionStmt) statementIssue2).getExpression();
if (expression instanceof VariableDeclarationExpr) {
((VariableDeclarationExpr) expression).addModifier(Modifier.FINAL);
}
}
System.out.println("Modified Java class:\n\n" + javaClass);
}
private MethodDeclaration getMethodByLineNumber(CompilationUnit javaClass,
int issueLineNumber) {
bestMatchMethod = null;
javaClass.getTypes().forEach(type -> type.getMembers().stream()
.filter(declaration -> declaration instanceof MethodDeclaration)
.forEach(method -> {
if (method.getTokenRange().isPresent()) {
int methodLineNumber = method.getTokenRange().get()
.getBegin().getRange().begin.line;
if (bestMatchMethod == null
|| (methodLineNumber < issueLineNumber
&& methodLineNumber > bestMatchMethodLineNumber)) {
bestMatchMethod = (MethodDeclaration) method;
bestMatchMethodLineNumber = methodLineNumber;
}
}
})
);
return bestMatchMethod;
}
private Statement getStatementByLineNumber(CompilationUnit javaClass,
int issueLineNumber) {
statementByLineNumber = null;
MethodDeclaration method = getMethodByLineNumber(javaClass, issueLineNumber);
if (method != null) {
method.getBody().ifPresent(blockStmt
-> blockStmt.getStatements().forEach(statement
-> statement.getTokenRange().ifPresent(tokenRange -> {
if (tokenRange.getBegin().getRange().begin.line == issueLineNumber) {
statementByLineNumber = statement;
}
})));
}
return statementByLineNumber;
}
}
另一种方法可能是根据您尝试为其创建自动修复的插件创建新的 Checkstyle 插件。也许您有足够的信息不仅可以发出警告,还可以生成修复了这些问题的修改版本。
就我个人而言,我会犹豫是否要在提交时自动修复问题。当需要进行许多简单的修复时,欢迎自动化,但我想在提交之前检查这些更改。运行这样的工具并检查更改可能是解决许多简单问题的一种非常快速的方法。
我认为可以自动修复的一些检查:
- 添加静态
- 修复内联条件
- FinalParameters和 FinalLocalVariable : 添加最后
- ModifierOrder :重新排序修饰符(例如:final static private)
- NeedBraces : 添加大括号
关于java - 自动修复非格式化但简单的 CheckStyle 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45326752/