Ant/PMD のバックアップの現在との差分(No.14)
- title: AntでPMDを実行する author: aterai pubdate: 2012-01-25T00:21:32+09:00 description: ソースコードの静的解析を行うPMDをAntから実行するためのターゲットや、ruleset.xmlのサンプルなど。
概要
概要
Ant
からPMD
を実行します。
サンプルターゲット
サンプルターゲット
-
build.xml
に記述するターゲットサンプル<target name="pmd"> <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" /> <mkdir dir="${build.reports}" /> <pmd rulesetfiles="java-basic,java-imports,java-strings" encoding="${compile.encoding}"> <pmd rulesetfiles="ruleset.xml" encoding="${compile.encoding}" cacheLocation="${build.reports}/pmd/pmd.cache"> <sourceLanguage name="java" version="${compile.source}"/> <classpath refid="project.class.path" /> <formatter type="xml" toFile="${build.reports}/pmd.xml" /> <formatter type="text" toConsole="true" /> <fileset dir="${src.dir}"> <include name="**/*.java" /> </fileset> <fileset dir="${src.dir}" excludes="**/module-info.java" includes="**/*.java" /> </pmd> </target>
解説
- PMDからダウンロードした
pmd-bin-x.x.zip
のlib
以下にあるpmd-x.x.jar
などのjar
ファイルを%ANT_HOME%\lib
にコピー -
ant pmd
で、pmd.xml
を生成し、jenkins
のPMD
プラグインなどで読み込む
toFile or toConsole
FindBugs
のように、xml
ファイルとコンソールの両方に結果を表示したい場合が多いのですが、
ant -v pmd
では、情報が多すぎるのでformatter
要素のtoConsole
属性を使用します。
- PMD - Ant Task にあるように、
<formatter type="html" toFile="pmd_report.html" toConsole="true"/>
や、<formatter type="xml" toFile="${build.reports}/pmd.xml" toConsole="true" />
とtoFile
とtoConsole
を指定しても、ファイルにのみ結果が書き出されて、コンソールには何も出力されない -
<formatter type="text" toConsole="true" />
と、toFile
属性なしでtoConsole="true"
とする必要がある?-
<formatter type="text" toConsole="false" />
は、toFile or toConsole needs to be specified in Formatter
となって、BUILD FAILED
-
- ファイルとコンソールの両方に出力する場合は、
formatter
を複数指定しなくてはならない?
ruleset
チェックするルールをruleset.xml
などに記述して、これをrulesetfiles
属性で参照する方法もあります。
-
ruleset.xml
を使用するサンプルターゲット
-
PMD 6.0.0
向けのruleset.xml
サンプル#spandel <target name="pmd"> #spanend <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" /> <mkdir dir="${build.reports}" /> <pmd rulesetfiles="ruleset.xml" encoding="${compile.encoding}"> <sourceLanguage name="java" version="1.7"/> <classpath refid="project.class.path" /> <formatter type="xml" toFile="${build.reports}/pmd.xml" /> <formatter type="text" toConsole="true" /> <fileset dir="${src.dir}"> <include name="**/*.java" /> </fileset> </pmd> #spandel </target> #spanend #spandel
-
ruleset.xml
のサンプル
#spanend
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
#spandel
#spanend
<description>This ruleset checks my code for bad stuff</description>
#spandel
#spanend
<rule ref="rulesets/java/basic.xml" />
<rule ref="rulesets/java/braces.xml" />
<rule ref="rulesets/java/clone.xml" />
<rule ref="rulesets/java/codesize.xml" />
<rule ref="rulesets/java/controversial.xml">
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="OnlyOneReturn" />
<exclude name="AvoidLiteralsInIfCondition" />
<exclude name="NullAssignment" />
<!-- exclude name="AssignmentInOperand" / -->
<rule ref="category/java/bestpractices.xml">
<exclude name="AvoidPrintStackTrace" />
<exclude name="SystemPrintln" />
</rule>
<rule ref="category/java/codestyle.xml">
<exclude name="AtLeastOneConstructor" />
<exclude name="AvoidUsingShortType" />
<exclude name="OnlyOneReturn" />
<exclude name="ShortVariable" />
<exclude name="LongVariable" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="MethodArgumentCouldBeFinal" />
</rule>
<rule ref="rulesets/java/coupling.xml">
<rule ref="category/java/design.xml">
<exclude name="DataClass" />
<exclude name="LawOfDemeter" />
<exclude name="LoosePackageCoupling" />
<exclude name="NcssCount" />
</rule>
<rule ref="rulesets/java/design.xml">
<exclude name="AvoidSynchronizedAtMethodLevel" />
<exclude name="AvoidDeeplyNestedIfStmts" />
<!-- rule ref="category/java/documentation.xml" / -->
<rule ref="category/java/errorprone.xml">
<exclude name="BeanMembersShouldSerialize" />
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="DoNotCallSystemExit" />
<exclude name="NullAssignment" />
<exclude name="UseProperClassLoader" />
</rule>
<rule ref="rulesets/java/design.xml/AvoidDeeplyNestedIfStmts">
<properties>
<property name="problemDepth" value="4"/>
</properties>
<rule ref="category/java/multithreading.xml">
<exclude name="DoNotUseThreads" />
</rule>
<rule ref="rulesets/java/empty.xml" />
<rule ref="rulesets/java/finalizers.xml" />
<rule ref="rulesets/java/imports.xml" />
<rule ref="rulesets/java/migrating.xml">
<exclude name="ReplaceVectorWithList" />
</rule>
<rule ref="rulesets/java/naming.xml">
<exclude name="ShortClassName" />
<exclude name="ShortVariable" />
<exclude name="LongVariable" />
</rule>
<rule ref="rulesets/java/optimizations.xml">
<rule ref="category/java/performance.xml">
<exclude name="AvoidInstantiatingObjectsInLoops" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="MethodArgumentCouldBeFinal" />
</rule>
<rule ref="rulesets/java/strictexception.xml" />
<rule ref="rulesets/java/strings.xml" />
<rule ref="rulesets/java/sunsecure.xml" />
<rule ref="rulesets/java/typeresolution.xml" />
<rule ref="rulesets/java/unnecessary.xml" />
<rule ref="rulesets/java/unusedcode.xml" />
#spandel
#spanend
#spandel
<!--
#spanend
<rule ref="rulesets/java/comments.xml" />
<rule ref="rulesets/java/j2ee.xml" />
<rule ref="rulesets/java/javabeans.xml" />
<rule ref="rulesets/java/junit.xml" />
<rule ref="rulesets/java/logging-jakarta-commons.xml" />
<rule ref="rulesets/java/logging-java.xml" />
<rule ref="rulesets/java/migrating_to_13.xml" />
<rule ref="rulesets/java/migrating_to_14.xml" />
<rule ref="rulesets/java/migrating_to_15.xml" />
<rule ref="rulesets/java/migrating_to_junit4.xml" />
-->
</ruleset>
PMD 5.0.0
-
RuleSet short names now require a language prefix, 'basic' is now 'java-basic', and 'rulesets/basic.xml' is now 'rulesets/java/basic.xml'
- 上記のサンプルで、
rulesetfiles
属性のbasic
などを、java-basic
に変更
- 上記のサンプルで、
-
Removed -targetjdk use -version {name} {version} instead
- 上記のサンプルから、
targetjdk="${compile.source}"
を削除
- 上記のサンプルから、
解説
- PMDからダウンロードした
pmd-bin-x.x.zip
のlib
以下にあるpmd-x.x.jar
などのjar
ファイルを%ANT_HOME%\lib
にコピー、もしくは環境変数%PMD_HOME%
を設定した場所にlib
を展開 - 上記のような
pmd
ターゲットをbuild.xml
に追加し、ant pmd
でpmd.xml
を生成、jenkins
のPMD
プラグインなどで読み込む - チェックするルールをカスタマイズする場合は、
ruleset.xml
を記述してこれをrulesetfiles
属性で参照する
PMD 5.1.2
-
PMD 5.1.2
で以下のようなコードが、Avoid unused private methods such as 'makePanel(String,JComponent)'.
と警告されてしまう?- PMD / Issues / #1228 UnusedPrivateMethod returns false positives
-
C:\pmd-bin-5.1.2\bin\pmd.bat -dir /root/directory/for/sources -rulesets java-unusedcode
toFile or toConsole
-
FindBugs
のように、xml
ファイルとコンソールの両方に結果を表示する場合は、formatter
要素のtoFile
とtoConsole
属性を併用する-
ant -v pmd
は、出力される情報が多すぎる
-
- PMD - Ant Task にあるように、
<formatter type="html" toFile="pmd_report.html" toConsole="true"/>
や、<formatter type="xml" toFile="${build.reports}/pmd.xml" toConsole="true" />
とtoFile
とtoConsole
を指定しても、ファイルにのみ結果が書き出されて、コンソールには何も出力されない -
<formatter type="text" toConsole="true" />
と、toFile
属性なしでtoConsole="true"
とする必要がある?-
<formatter type="text" toConsole="false" />
は、toFile or toConsole needs to be specified in Formatter
となって、BUILD FAILED
-
- ファイルとコンソールの両方に出力する場合は、
formatter
を複数指定しなくてはならない?
更新履歴
PMD 6.5.0
-
*
を使用したインポート文と同名のメソッドが存在すると、UnnecessaryFullyQualifiedNameルールで誤検出が発生する - 例:
-
import java.util.stream.*;
で、Arrays.stream(...)
が誤って警告 -
import java.time.format.*;
で、LocalDate#format(...)
が誤って警告 -
import javax.swing.undo.*;
で、UndoManager#undo()
が誤って警告
-
> pmd.bat -dir C:\tmp -R category/java/codestyle.xml/UnnecessaryFullyQualifiedName C:\tmp\Test.java:9: Unnecessary use of fully qualified name 'Arrays.stream' due to existing import 'java.util.stream.*'
#spandel
import java.awt.*;
#spanend
#spandel
import javax.swing.*;
#spanend
#spanadd
import java.awt.Component;
#spanend
#spanadd
import java.awt.Container;
#spanend
#spanadd
import java.util.Arrays;
#spanend
#spanadd
import java.util.stream.*;
#spanend
#spanadd
// import java.util.stream.Stream; // *を使用しない場合は誤検出は発生しない
#spanend
#spandel
public class Foo {
#spanend
public JComponent makeUI() {
Box box = Box.createVerticalBox();
JTextField field = new JTextField();
box.add(makePanel("aaa", field));
return box;
}
private static JPanel makePanel(String title, JComponent c) {
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createTitledBorder(title));
p.add(c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Foo().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#spanadd
public final class Test {
#spanend
public static Stream<Component> stream(Container parent) {
return Arrays.stream(parent.getComponents())
.filter(Container.class::isInstance)
.map(c -> stream(Container.class.cast(c)))
.reduce(Stream.of(parent), Stream::concat);
}
}
PMD 5.1.3-SNAPSHOT
-
上記の誤検出はpmd-bin-5.1.3-SNAPSHOT.zip
ですべて無くなったけど、新規で今まで問題なかった?以下のようなコードのmakeButton(Action action, String title)
がUnusedPrivateMethod
となってしまう。
PMD 6.0.0
-
rulesets/java/basic.xml
などが非推奨になって、category/java/bestpractices.xml
などが使用可能になった - キャッシュディレクトリの指定がない場合、警告されるようになった
-
以下のようなJDK 1.8.0
で有効な総称型の型引数の省略が誤判定されて、Error while parsing ...
とエラーになる
PMD 5.1.3
-
上記の誤検出は修正済みpmd-bin-5.1.3-SNAPSHOT.zip
ですべて修正済み。ただし新規で今まで問題なかった以下のようなコードのmakeButton(Action action, String title)
がUnusedPrivateMethod
となってしまう
C:\temp>C:\pmd-bin-5.1.3-SNAPSHOT\bin\pmd.bat -dir . -rulesets java-unusedcode C:\temp\MenuBarFactoryTest.java:44: Avoid unused private methods such as 'makeButton(Action,String)'.
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
public final class MenuBarFactoryTest {
public static JMenuBar makeManuBar() {
public static JMenuBar makeMenuBar() {
JMenuBar mb = new JMenuBar();
mb.add(makeFileMenu());
return mb;
}
private static JMenu makeFileMenu() {
JMenu menu = new JMenu("File");
menu.add(makeEditMenuItem(makeEditButtonBar(Arrays.asList(
makeButton(new DefaultEditorKit.CutAction(), "Cut"),
makeButton(new DefaultEditorKit.CopyAction(), "Copy"),
makeButton(new DefaultEditorKit.PasteAction(), "Paste")))));
return menu;
}
private static JMenuItem makeEditMenuItem(final JComponent edit) {
JMenuItem item = new JMenuItem("Edit") {
@Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.width += edit.getPreferredSize().width;
d.height = Math.max(edit.getPreferredSize().height, d.height);
return d;
}
};
item.setEnabled(false);
item.setLayout(new GridBagLayout());
item.add(edit);
return item;
}
private static JComponent makeEditButtonBar(List<AbstractButton> list) {
JPanel p = new JPanel(new GridLayout(1, list.size(), 0, 0));
for (AbstractButton b : list) {
p.add(b);
}
p.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
p.setOpaque(false);
return p;
}
private static AbstractButton makeButton(Action action, String title) {
JButton b = new JButton(action);
b.setText(title);
return b;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setJMenuBar(makeManuBar());
f.setJMenuBar(makeMenuBar());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
警告
GodClass
PMD 5.1.2
-
修正済みPMD 5.1.2
で以下のようなコードが、Avoid unused private methods such as 'makePanel(String,JComponent)'.
と警告されてしまう?- PMD / Issues / #1228 UnusedPrivateMethod returns false positives
-
C:\pmd-bin-5.1.2\bin\pmd.bat -dir /root/directory/for/sources -rulesets java-unusedcode
#spanadd
import java.awt.*;
#spanend
#spanadd
import javax.swing.*;
#spanend
#spanadd
#spanend
#spanadd
public class Foo {
#spanend
public JComponent makeUI() {
Box box = Box.createVerticalBox();
JTextField field = new JTextField();
box.add(makePanel("aaa", field));
return box;
}
private static JPanel makePanel(String title, JComponent c) {
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createTitledBorder(title));
p.add(c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Foo().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#spanadd
}
#spanend
#spanadd
PMD 5.0.0
-
RuleSet short names now require a language prefix, 'basic' is now 'java-basic', and 'rulesets/basic.xml' is now 'rulesets/java/basic.xml'
- 上記のサンプルで、
rulesetfiles
属性のbasic
などを、java-basic
に変更
- 上記のサンプルで、
-
Removed -targetjdk use -version {name} {version} instead
- 上記のサンプルから、
targetjdk="${compile.source}"
を削除
- 上記のサンプルから、
警告
GodClass
- GodClass - Design | PMD Source Code Analyzer
#spanend /** * Very high threshold for WMC (Weighted Method Count). See: Lanza. * Object-Oriented Metrics in Practice. Page 16. */ private static final int WMC_VERY_HIGH = 47; /** * Few means between 2 and 5. See: Lanza. Object-Oriented Metrics in * Practice. Page 18. */ private static final int FEW_THRESHOLD = 5; /** * One third is a low value. See: Lanza. Object-Oriented Metrics in * Practice. Page 17. */ private static final double ONE_THIRD_THRESHOLD = 1.0 / 3.0; //0.33333 #spandel //... #spanend #spanadd // ... #spanend if (wmcCounter >= WMC_VERY_HIGH && atfdCounter > FEW_THRESHOLD && tcc < ONE_THIRD_THRESHOLD) { #spandel //... #spanend #spanadd // ... #spanend
SimplifiedTernary
SimplifiedTernary
- SimplifiedTernary - Design | PMD Source Code Analyzer
public class SimplifiedTernaryTest { public static void main(String... args) { //boolean condition = true; boolean b1 = condition ? true : something(); // can be as simple as condition || something(); boolean b2 = condition ? false : something(); // can be as simple as !condition && something(); boolean b3 = condition ? something() : true; // can be as simple as !condition || something(); boolean b4 = condition ? something() : false; // can be as simple as condition && something(); } private static boolean something() { return true; } }
- 例:
model2 = new BasicDirectoryModel(getFileChooser()) { @Override public boolean renameFile(File oldFile, File newFile) { //return oldFile.canWrite() ? super.renameFile(oldFile, newFile) : false; return oldFile.canWrite() && super.renameFile(oldFile, newFile); } };
PMD 5.4.0
-
PMD 5.4.0
では、以下のような三項演算子も修正可と誤認識される -
PMD 5.4.0
では、以下のような三項演算子も修正可と誤認識される5.4.2
で修正済repaintRect = prevRollover ? r.union(table.getCellRect(prevRow, prevCol, false)) : r;
- PMD Issues #1424 False negative with ternary operator
-
参考リンク
Jenkins
- Jenkinsに移動