Page MenuHomePhabricator

D14632.diff
No OneTemporary

D14632.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -55,6 +55,8 @@
'ArcanistCapabilityNotSupportedException' => 'workflow/exception/ArcanistCapabilityNotSupportedException.php',
'ArcanistCastSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCastSpacingXHPASTLinterRule.php',
'ArcanistCastSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCastSpacingXHPASTLinterRuleTestCase.php',
+ 'ArcanistCheckstyleLinter' => 'lint/linter/ArcanistCheckstyleLinter.php',
+ 'ArcanistCheckstyleLinterTestCase' => 'lint/linter/__tests__/ArcanistCheckstyleLinterTestCase.php',
'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php',
'ArcanistChmodLinter' => 'lint/linter/ArcanistChmodLinter.php',
'ArcanistChmodLinterTestCase' => 'lint/linter/__tests__/ArcanistChmodLinterTestCase.php',
@@ -274,6 +276,8 @@
'ArcanistPHPOpenTagXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPHPOpenTagXHPASTLinterRuleTestCase.php',
'ArcanistPHPShortTagXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPHPShortTagXHPASTLinterRule.php',
'ArcanistPHPShortTagXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPHPShortTagXHPASTLinterRuleTestCase.php',
+ 'ArcanistPMDLinter' => 'lint/linter/ArcanistPMDLinter.php',
+ 'ArcanistPMDLinterTestCase' => 'lint/linter/__tests__/ArcanistPMDLinterTestCase.php',
'ArcanistPaamayimNekudotayimSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPaamayimNekudotayimSpacingXHPASTLinterRule.php',
'ArcanistPaamayimNekudotayimSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPaamayimNekudotayimSpacingXHPASTLinterRuleTestCase.php',
'ArcanistParentMemberReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParentMemberReferenceXHPASTLinterRule.php',
@@ -469,6 +473,8 @@
'ArcanistCapabilityNotSupportedException' => 'Exception',
'ArcanistCastSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistCastSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistCheckstyleLinter' => 'ArcanistExternalLinter',
+ 'ArcanistCheckstyleLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
'ArcanistChmodLinter' => 'ArcanistLinter',
'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase',
@@ -688,6 +694,8 @@
'ArcanistPHPOpenTagXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistPHPShortTagXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPHPShortTagXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistPMDLinter' => 'ArcanistExternalLinter',
+ 'ArcanistPMDLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPaamayimNekudotayimSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPaamayimNekudotayimSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistParentMemberReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
diff --git a/src/lint/linter/ArcanistCheckstyleLinter.php b/src/lint/linter/ArcanistCheckstyleLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistCheckstyleLinter.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * This linter invokes checkstyle for verifying java coding standards.
+ * The checkstyle report is given over stdout as a simple XML document
+ * which maps fairly easily to ArcanistLintMessage.
+ */
+class ArcanistCheckstyleLinter extends ArcanistExternalLinter {
+
+ private $fullyQualifySourcename = false;
+
+ public function getInfoName() {
+ return 'Java checkstyle linter';
+ }
+
+ public function getLinterName() {
+ return 'CHECKSTYLE';
+ }
+
+ public function getInfoURI() {
+ return 'http://checkstyle.sourceforge.net';
+ }
+
+ public function getInfoDescription() {
+ return pht('Use `%s` to perform static analysis on Java code.',
+ 'checkstyle');
+ }
+
+ public function getLinterConfigurationName() {
+ return 'checkstyle';
+ }
+
+ public function getLinterConfigurationOptions() {
+ $options = array(
+ 'checkstyle.fully-qualify-sourcename' => array(
+ 'type' => 'optional bool',
+ 'help' => pht(
+ 'Controls whether errors messages reported by checkstyle use the '.
+ 'fully-qualified or simple classname of the reporting check. The '.
+ 'default setting is `false`, which simplifies the class name: '.
+ '`com.company.pkg.CheckIndentation` => `CheckIndentation`.'),
+ ),
+ );
+
+ return $options + parent::getLinterConfigurationOptions();
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'checkstyle.fully-qualify-sourcename':
+ $this->setFullyQualifySourcename($value);
+ return;
+ }
+
+ return parent::setLinterConfigurationValue($key, $value);
+ }
+
+ public function setFullyQualifySourcename($fully_qualify_sourcename) {
+ $this->fullyQualifySourcename = $fully_qualify_sourcename;
+ return $this;
+ }
+
+ public function getVersion() {
+ list($stdout) = execx('%C -v', $this->getExecutableCommand());
+
+ $matches = array();
+ $regex = '/^Checkstyle version: (?P<version>\d+\.\d+)$/';
+ if (preg_match($regex, $stdout, $matches)) {
+ return $matches['version'];
+ }
+ return false;
+ }
+
+ public function getInstallInstructions() {
+ return pht('Ensure java is configured as interpreter and '.
+ 'the checkstyle jar is configured as the binary. If you need to pass '.
+ 'additional additional JVM arguments include them with the '.
+ '`interpreter.flags` field. Use the `flags` field to configure '.
+ 'checkstyle arguments, including the `-c my_styles.xml` for '.
+ 'the styles to verify.');
+ }
+
+ protected function getMandatoryFlags() {
+ return array('-f', 'xml');
+ }
+
+ public function shouldExpectCommandErrors() {
+ return false;
+ }
+
+ public function shouldUseInterpreter() {
+ return true;
+ }
+
+ public function getDefaultBinary() {
+ return 'checkstyle.jar';
+ }
+
+ public function getDefaultInterpreter() {
+ return 'java';
+ }
+
+ protected function getMandatoryInterpreterFlags() {
+ // since the binary is the checkstyle jar, this flag must
+ // always be passed to the interpreter, and is guaranteed to be provided
+ // as the first flag before the binary jar on the command line
+ return array('-jar');
+ }
+
+ protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+ $dom = new DOMDocument();
+ $ok = @$dom->loadXML($stdout);
+
+ if (!$ok) {
+ return false;
+ }
+
+ $files = $dom->getElementsByTagName('file');
+ $messages = array();
+ foreach ($files as $file) {
+ $errors = $file->getElementsByTagName('error');
+ foreach ($errors as $error) {
+ $message = new ArcanistLintMessage();
+ $message->setPath($file->getAttribute('name'));
+ $message->setLine($error->getAttribute('line'));
+ $message->setCode($this->getLinterName());
+
+ // source is the module's fully-qualified classname
+ // attempt to simplify it for readability
+ $source = $error->getAttribute('source');
+ if ($this->fullyQualifySourcename == false) {
+ $source = idx(array_slice(explode('.', $source), -1), 0);
+ }
+ $message->setName($source);
+
+ // checkstyle's XMLLogger escapes these five characters
+ $description = $error->getAttribute('message');
+ $description = str_replace(
+ ['&lt;', '&gt;', '&apos;', '&quot;', '&amp;'],
+ ['<', '>', '\'', '"', '&'],
+ $description);
+ $message->setDescription($description);
+
+ $column = $error->getAttribute('column');
+ if ($column) {
+ $message->setChar($column);
+ }
+
+ $severity = $error->getAttribute('severity');
+ switch ($severity) {
+ case 'error':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
+ break;
+ case 'warning':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+ case 'info':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
+ break;
+ case 'ignore':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_DISABLED);
+ break;
+
+ // The above four are the only valid checkstyle severities,
+ // this is for completion as well as preparing for future severities
+ default:
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+ }
+
+ $messages[] = $message;
+ }
+ }
+
+ return $messages;
+ }
+}
diff --git a/src/lint/linter/ArcanistPMDLinter.php b/src/lint/linter/ArcanistPMDLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistPMDLinter.php
@@ -0,0 +1,326 @@
+<?php
+
+/**
+ * This linter invokes PMD for linting java code.
+ * The PMD report is given over stdout as a simple XML document
+ * which maps fairly easily to ArcanistLintMessage.
+ *
+ * The manner of executing PMD is a little round-about in order
+ * to work around the internals of ArcanistExternalLinter.
+ *
+ * The binary is passed as a classpath argument to `java` then
+ * the binary arguments override it with a full classpath.
+ * This also enables the binary being used to build the full
+ * classpath since the PMD distribution uses multiple jars.
+ *
+ * This linter can be used to invoke either PMD or CPD subcommands.
+ */
+class ArcanistPMDLinter extends ArcanistExternalLinter {
+
+ private $subCommand = 'pmd';
+
+ public function getInfoName() {
+ return 'Java PMD linter';
+ }
+
+ public function getLinterName() {
+ return 'PMD';
+ }
+
+ public function getInfoURI() {
+ return 'https://pmd.github.io/';
+ }
+
+ public function getInfoDescription() {
+ return pht('Use `%s` to perform static analysis on Java code.',
+ 'PMD');
+ }
+
+ public function getLinterConfigurationName() {
+ return 'pmd';
+ }
+
+ public function getLinterConfigurationOptions() {
+ $options = array(
+ 'pmd.command' => array(
+ 'type' => 'optional string',
+ 'help' => pht(
+ 'Specify the subcommand to run, defaults to `pmd`. '.
+ 'Available subcommands are `pmd` or `cpd`. See the PMD '.
+ 'documentation for more details.'),
+ ),
+ );
+
+ return $options + parent::getLinterConfigurationOptions();
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'pmd.command':
+ $this->setSubCommand($value);
+ return;
+ }
+
+ return parent::setLinterConfigurationValue($key, $value);
+ }
+
+ public function setSubCommand($sub_command) {
+ $this->subCommand = $sub_command;
+ return $this;
+ }
+
+ public function getVersion() {
+ // The pmd executable doesn't appear to actually be able to print a version
+ // However if you run the binary, the help output gives examples that
+ // appear to display the version number in paths...
+ $interpreter = $this->getInterpreter();
+ $classpath = $this->buildClasspath();
+ $mainclass = $this->getSubCommandMainClass();
+
+ // having the help printout for PMD returns with error code 4.
+ list($code, $stdout, $stderr) = exec_manual('%s -cp %s %s',
+ $interpreter, $classpath, $mainclass);
+
+ $matches = array();
+ $regex = '/^\$ pmd-bin-(?P<version>\d+\.\d+\.\d+)/m';
+ if (preg_match($regex, $stdout, $matches)) {
+ return $matches['version'];
+ }
+ return false;
+ }
+
+ public function getInstallInstructions() {
+ return pht('Ensure java is configured as interpreter and '.
+ 'the pmd jar is configured as the binary. If you need to pass '.
+ 'additional additional JVM arguments include them with the '.
+ '`interpreter.flags` field. Use the `flags` field to configure pmd '.
+ 'arguments, including the `-rulesets my_rulesets.xml`. '.
+ 'You must pass the `-dir` or `--files` argument as the last flag.');
+ }
+
+ protected function getMandatoryFlags() {
+ $classpath = $this->buildClasspath();
+ $subcommand_mainclass = $this->getSubCommandMainClass();
+ $format_option = '-format';
+ if ($this->subCommand == 'cpd') {
+ $format_option = '--format';
+ }
+
+ return array(
+ '-cp',
+ $classpath,
+ $subcommand_mainclass,
+ '-failOnViolation',
+ 'false',
+ $format_option,
+ 'xml',
+ );
+ }
+
+ public function shouldExpectCommandErrors() {
+ return false;
+ }
+
+ public function shouldUseInterpreter() {
+ return true;
+ }
+
+ public function getDefaultBinary() {
+ return 'pmd-core.jar';
+ }
+
+ public function getDefaultInterpreter() {
+ return 'java';
+ }
+
+ protected function getMandatoryInterpreterFlags() {
+ // this causes the jar binary to be interpreted as classpath
+ // later to be overwritten with a full classpath during execution
+ return array('-cp');
+ }
+
+ protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+ $messages = array();
+
+ if (strlen(trim($stdout)) == 0) {
+ return $messages;
+ }
+
+ $dom = new DOMDocument();
+ $ok = @$dom->loadXML($stdout);
+
+ if (!$ok) {
+ return false;
+ }
+
+ $pmd = $dom->getElementsByTagName('pmd');
+ if ($pmd) {
+ foreach ($pmd as $pmd_node) {
+ $messages = array_merge($messages,
+ $this->parsePmdNodeToLintMessages($pmd_node));
+ }
+ }
+
+ $cpd = $dom->getElementsByTagName('pmd-cpd');
+ if ($cpd) {
+ foreach ($cpd as $cpd_node) {
+ $messages = array_merge($messages,
+ $this->parseCpdNodeToLintMessages($cpd_node, $messages));
+ }
+ }
+
+ return $messages;
+ }
+
+ private function parsePmdNodeToLintMessages($pmd) {
+ $messages = array();
+
+ $files = $pmd->getElementsByTagName('file');
+ foreach ($files as $file) {
+ $violations = $file->getElementsByTagName('violation');
+ foreach ($violations as $violation) {
+ $message = new ArcanistLintMessage();
+ $message->setPath($file->getAttribute('name'));
+ $message->setLine($violation->getAttribute('beginline'));
+ $message->setCode('PMD');
+
+ // include the ruleset and the rule
+ $message->setName($violation->getAttribute('ruleset').
+ ': '.$violation->getAttribute('rule'));
+
+ $description = '';
+ if (property_exists($violation, 'firstChild')) {
+ $first_child = $violation->firstChild;
+ if (property_exists($first_child, 'wholeText')) {
+ $description = $first_child->wholeText;
+ }
+ }
+
+ // unescape the XML written out by pmd's XMLRenderer
+ if ($description) {
+ // these 4 characters use specific XML-escape codes
+ $description = str_replace(
+ ['&amp;', '&quot;', '&lt;', '&gt;'],
+ ['&', '"', '<', '>'],
+ $description);
+
+ // everything else is hex-code escaped
+ $escaped_chars = array();
+ preg_replace_callback(
+ '/&#x(?P<hexcode>[a-f|A-F|0-9]+);/',
+ array($this, 'callbackReplaceMatchesWithHexcode'),
+ $description);
+
+ $message->setDescription($description);
+ }
+
+ $column = $violation->getAttribute('begincolumn');
+ if ($column) {
+ $message->setChar($column);
+ }
+
+ $severity = $violation->getAttribute('priority');
+ switch ($severity) {
+ case '1':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
+ break;
+ case '2':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+ case '3':
+ case '4':
+ case '5':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
+ break;
+
+ // The above five are the only valid PMD priorities,
+ // this is for completion as well as preparing for future severities
+ default:
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+ }
+
+ $messages[] = $message;
+ }
+ }
+
+ return $messages;
+ }
+
+ private function parseCpdNodeToLintMessages($cpd_node, array $messages) {
+ $dups = $cpd_node->getElementsByTagName('duplication');
+ foreach ($dups as $dup) {
+ $files = $dup->getElementsByTagName('file');
+ $code_nodes = $dup->getElementsByTagName('codefragment');
+
+ $description = pht('Duplicated code locations: ');
+ foreach ($files as $file) {
+ $description .=
+ $file->getAttribute('path').':'.$file->getAttribute('line').', ';
+ }
+
+ reset($files);
+ foreach ($files as $file) {
+ $message = new ArcanistLintMessage();
+ $message->setPath($file->getAttribute('path'));
+ $message->setLine($file->getAttribute('line'));
+ $message->setCode('CPD');
+ $message->setName('Copy/Paste Detector');
+ $message->setDescription($description);
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+
+ $messages[] = $message;
+ }
+ }
+
+ return $messages;
+ }
+
+ private function buildClasspath() {
+ $jar_files = array();
+ $lib_path = Filesystem::resolvePath($this->getBinary());
+ $lib_path = substr($lib_path, 0, strrpos($lib_path, DIRECTORY_SEPARATOR));
+
+ $lib_path_contents = Filesystem::listDirectory($lib_path);
+ foreach ($lib_path_contents as $lib_path_file) {
+ if (preg_match('/\.jar$/', $lib_path_file)) {
+ $jar_files[] = $lib_path.DIRECTORY_SEPARATOR.$lib_path_file;
+ }
+ }
+ return implode(PATH_SEPARATOR, $jar_files);
+ }
+
+ private function getSubCommandMainClass() {
+ $command_mainclass = '';
+ switch ($this->subCommand) {
+ case 'pmd':
+ $command_mainclass = 'net.sourceforge.pmd.PMD';
+ break;
+ case 'cpd':
+ $command_mainclass = 'net.sourceforge.pmd.cpd.CPD';
+ break;
+ }
+ return $command_mainclass;
+ }
+
+ private function callbackReplaceMatchesWithHexcode($matches) {
+ return $this->convertHexToBin($matches['hexcode']);
+ }
+
+ /**
+ * This is a replacement for hex2bin() which is only available in PHP 5.4+.
+ * Returns the ascii interpretation of a given hexadecimal string.
+ *
+ * @param $str string The hexadecimal string to interpret
+ *
+ * @return string The string of characters represented by the given hex codes
+ */
+ private function convertHexToBin($str) {
+ $sbin = '';
+ $len = strlen($str);
+ for ($i = 0; $i < $len; $i += 2) {
+ $sbin .= pack('H*', substr($str, $i, 2));
+ }
+ return $sbin;
+ }
+}
diff --git a/src/lint/linter/__tests__/ArcanistCheckstyleLinterTestCase.php b/src/lint/linter/__tests__/ArcanistCheckstyleLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistCheckstyleLinterTestCase.php
@@ -0,0 +1,17 @@
+<?php
+
+final class ArcanistCheckstyleLinterTestCase
+ extends ArcanistExternalLinterTestCase {
+
+ protected function getLinter() {
+ $linter = parent::getLinter();
+ $linter->setBinary('/usr/local/bin/checkstyle-6.13-all.jar');
+ $linter->setFlags(
+ array('-c', dirname(__FILE__).'/checkstyle/google_checks.xml'));
+ return $linter;
+ }
+
+ public function testLinter() {
+ $this->executeTestsInDirectory(dirname(__FILE__).'/checkstyle/');
+ }
+}
diff --git a/src/lint/linter/__tests__/ArcanistLinterTestCase.php b/src/lint/linter/__tests__/ArcanistLinterTestCase.php
--- a/src/lint/linter/__tests__/ArcanistLinterTestCase.php
+++ b/src/lint/linter/__tests__/ArcanistLinterTestCase.php
@@ -60,9 +60,9 @@
'~~~~~~~~~~'));
}
- list($data, $expect, $xform, $config) = array_merge(
+ list($data, $expect, $xform, $config, $tmp_filename) = array_merge(
$contents,
- array(null, null));
+ array(null, null, null));
$basename = basename($file);
@@ -87,7 +87,10 @@
$caught_exception = false;
try {
- $tmp = new TempFile($basename);
+ if (!$tmp_filename) {
+ $tmp_filename = $basename;
+ }
+ $tmp = new TempFile($tmp_filename);
Filesystem::writeFile($tmp, $data);
$full_path = (string)$tmp;
diff --git a/src/lint/linter/__tests__/ArcanistPMDLinterTestCase.php b/src/lint/linter/__tests__/ArcanistPMDLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistPMDLinterTestCase.php
@@ -0,0 +1,16 @@
+<?php
+
+final class ArcanistPMDLinterTestCase
+ extends ArcanistExternalLinterTestCase {
+
+ protected function getLinter() {
+ $linter = parent::getLinter();
+ $linter->setBinary('/usr/local/pmd/pmd-bin-5.4.1/lib/pmd-core-5.4.1.jar');
+ return $linter;
+ }
+
+ public function testLinter() {
+ $this->executeTestsInDirectory(dirname(__FILE__).'/pmd/');
+ }
+
+}
diff --git a/src/lint/linter/__tests__/checkstyle/google_checks.lint-test b/src/lint/linter/__tests__/checkstyle/google_checks.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/checkstyle/google_checks.lint-test
@@ -0,0 +1,28 @@
+public class TestingCheckstyle {
+ public static void main(String[] args)
+ {
+ System.out.println()
+ }
+
+ private class A{
+
+ @Override public String m() { return null; }
+ }
+}
+~~~~~~~~~~
+warning:1:
+warning:3:3
+warning:4:
+warning:7:18
+warning:9:
+warning:9:31
+warning:9:46
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "checkstyle.fully-qualify-sourcename": false
+ }
+}
+~~~~~~~~~~
+google_checks.lint-test.java
diff --git a/src/lint/linter/__tests__/checkstyle/google_checks.xml b/src/lint/linter/__tests__/checkstyle/google_checks.xml
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/checkstyle/google_checks.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+
+ Checkstyle configuration that checks the Google coding conventions from:
+
+ - Google Java Style
+ https://google-styleguide.googlecode.com/svn-history/r130/trunk/javaguide.html
+
+ Checkstyle is very configurable. Be sure to read the documentation at
+ http://checkstyle.sf.net (or in your downloaded distribution).
+
+ Most Checks are configurable, be sure to consult the documentation.
+
+ To completely disable a check, just comment it out or delete it from the file.
+
+ Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
+
+ -->
+
+<module name = "Checker">
+ <property name="charset" value="UTF-8"/>
+
+ <property name="severity" value="warning"/>
+
+ <property name="fileExtensions" value="java, properties, xml"/>
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="FileTabCharacter">
+ <property name="eachLine" value="true"/>
+ </module>
+
+ <module name="TreeWalker">
+ <module name="OuterTypeFilename"/>
+ <module name="IllegalTokenText">
+ <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
+ <property name="format" value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
+ <property name="message" value="Avoid using corresponding octal or Unicode escape."/>
+ </module>
+ <module name="AvoidEscapedUnicodeCharacters">
+ <property name="allowEscapesForControlCharacters" value="true"/>
+ <property name="allowByTailComment" value="true"/>
+ <property name="allowNonPrintableEscapes" value="true"/>
+ </module>
+ <module name="LineLength">
+ <property name="max" value="100"/>
+ <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
+ </module>
+ <module name="AvoidStarImport"/>
+ <module name="OneTopLevelClass"/>
+ <module name="NoLineWrap"/>
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
+ <module name="NeedBraces"/>
+ <module name="LeftCurly">
+ <property name="maxLineLength" value="100"/>
+ </module>
+ <module name="RightCurly"/>
+ <module name="RightCurly">
+ <property name="option" value="alone"/>
+ <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
+ </module>
+ <module name="WhitespaceAround">
+ <property name="allowEmptyConstructors" value="true"/>
+ <property name="allowEmptyMethods" value="true"/>
+ <property name="allowEmptyTypes" value="true"/>
+ <property name="allowEmptyLoops" value="true"/>
+ <message key="ws.notFollowed"
+ value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
+ <message key="ws.notPreceded"
+ value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="OneStatementPerLine"/>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="ArrayTypeStyle"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="FallThrough"/>
+ <module name="UpperEll"/>
+ <module name="ModifierOrder"/>
+ <module name="EmptyLineSeparator">
+ <property name="allowNoEmptyLineBetweenFields" value="true"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="tokens" value="DOT"/>
+ <property name="option" value="nl"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="tokens" value="COMMA"/>
+ <property name="option" value="EOL"/>
+ </module>
+ <module name="PackageName">
+ <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
+ <message key="name.invalidPattern"
+ value="Package name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="TypeName">
+ <message key="name.invalidPattern"
+ value="Type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MemberName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern"
+ value="Member name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ParameterName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern"
+ value="Parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="LocalVariableName">
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <property name="allowOneCharVarInForLoop" value="true"/>
+ <message key="name.invalidPattern"
+ value="Local variable name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ClassTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Class type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MethodTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Method type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="NoFinalizer"/>
+ <module name="GenericWhitespace">
+ <message key="ws.followed"
+ value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.preceded"
+ value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.illegalFollow"
+ value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.notPreceded"
+ value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="Indentation">
+ <property name="basicOffset" value="2"/>
+ <property name="braceAdjustment" value="0"/>
+ <property name="caseIndent" value="2"/>
+ <property name="throwsIndent" value="4"/>
+ <property name="lineWrappingIndentation" value="4"/>
+ <property name="arrayInitIndent" value="2"/>
+ </module>
+ <module name="AbbreviationAsWordInName">
+ <property name="ignoreFinal" value="false"/>
+ <property name="allowedAbbreviationLength" value="1"/>
+ </module>
+ <module name="OverloadMethodsDeclarationOrder"/>
+ <module name="VariableDeclarationUsageDistance"/>
+ <module name="CustomImportOrder">
+ <property name="specialImportsRegExp" value="com.google"/>
+ <property name="sortImportsInGroupAlphabetically" value="true"/>
+ <property name="customImportOrderRules" value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
+ </module>
+ <module name="MethodParamPad"/>
+ <module name="OperatorWrap">
+ <property name="option" value="NL"/>
+ <property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="allowSamelineMultipleAnnotations" value="true"/>
+ </module>
+ <module name="NonEmptyAtclauseDescription"/>
+ <module name="JavadocTagContinuationIndentation"/>
+ <module name="SummaryJavadocCheck">
+ <property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
+ </module>
+ <module name="JavadocParagraph"/>
+ <module name="AtclauseOrder">
+ <property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
+ <property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
+ </module>
+ <module name="JavadocMethod">
+ <property name="scope" value="public"/>
+ <property name="allowMissingParamTags" value="true"/>
+ <property name="allowMissingThrowsTags" value="true"/>
+ <property name="allowMissingReturnTag" value="true"/>
+ <property name="minLineCount" value="2"/>
+ <property name="allowedAnnotations" value="Override, Test"/>
+ <property name="allowThrowsTagsForSubclasses" value="true"/>
+ </module>
+ <module name="MethodName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
+ <message key="name.invalidPattern"
+ value="Method name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="SingleLineJavadoc">
+ <property name="ignoreInlineTags" value="false"/>
+ </module>
+ <module name="EmptyCatchBlock">
+ <property name="exceptionVariableName" value="expected"/>
+ </module>
+ <module name="CommentsIndentation"/>
+ </module>
+</module>
diff --git a/src/lint/linter/__tests__/pmd/cpd.lint-test b/src/lint/linter/__tests__/pmd/cpd.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/pmd/cpd.lint-test
@@ -0,0 +1,60 @@
+public class TestingCPD {
+ public static void main(String[] args)
+ {
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+
+ boolean b = true;
+ if (b && b || !b && !b) {
+ System.out.println();
+ }
+ }
+}
+~~~~~~~~~~
+warning:4:
+warning:24:
+warning:4:
+warning:29:
+warning:4:
+warning:34:
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "pmd.command": "cpd",
+ "flags": [
+ "--minimum-tokens",
+ "25",
+ "--files"
+ ]
+ }
+}
+~~~~~~~~~~
+cpd.lint-test.java
\ No newline at end of file
diff --git a/src/lint/linter/__tests__/pmd/pmd.lint-test b/src/lint/linter/__tests__/pmd/pmd.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/pmd/pmd.lint-test
@@ -0,0 +1,30 @@
+public class TestingPMD {
+ public static void main(String[] args)
+ {
+ System.out.println();
+ }
+
+ private class A{
+
+ @Override public String m() { return null; }
+ }
+}
+~~~~~~~~~~
+advice:1:8
+advice:7:11
+advice:7:11
+advice:9:27
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "pmd.command": "pmd",
+ "flags": [
+ "-rulesets",
+ "rulesets/java/naming.xml",
+ "-dir"
+ ]
+ }
+}
+~~~~~~~~~~
+pmd.lint-test.java
\ No newline at end of file

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 21, 12:37 AM (2 d, 5 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/ak/ls/kvak5nkhui52fpht
Default Alt Text
D14632.diff (33 KB)

Event Timeline