diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6d529e9e99..a1c9a7bd37 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,10 +1,19 @@
-
+## Describe the PR
-Before submitting a PR, please check that:
- - [ ] The PR is submitted against `master`. The PMD team will merge back to support branches as needed.
- - [ ] `./mvnw clean verify` passes. This will [build](https://github.com/pmd/pmd/blob/master/BUILDING.md) and test PMD, execute PMD and checkstyle rules. [Check this for more info](https://github.com/pmd/pmd/blob/master/CONTRIBUTING.md#code-style)
+
-**PR Description:**
+## Related issues
+
+
+
+- Fixes #
+
+## Ready?
+
+
+
+- [ ] Added unit tests for fixed bug/feature
+- [ ] Passing all unit tests
+- [ ] Complete build `./mvnw clean verify` passes (checked automatically by travis)
+- [ ] Added (in-code) documentation (if needed)
diff --git a/.travis.yml b/.travis.yml
index bcfbfccb5c..2276f63309 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,7 +49,7 @@ jobs:
env: BUILD=publish
before_install:
- - bash .travis/before_install.sh "11.0.6+10"
+ - bash .travis/before_install.sh "11.0.7+10"
- source ${HOME}/java.env
install: true
before_script: true
diff --git a/do-release.sh b/do-release.sh
index 614c8f12c4..f276238301 100755
--- a/do-release.sh
+++ b/do-release.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -e
# Make sure, everything is English...
@@ -15,7 +15,7 @@ if [ ! -f pom.xml -o ! -d ../pmd.github.io ]; then
exit 1
fi
-
+LAST_VERSION=
RELEASE_VERSION=
DEVELOPMENT_VERSION=
CURRENT_BRANCH=
@@ -33,11 +33,16 @@ PATCH=$(echo $RELEASE_VERSION | cut -d . -f 3)
if [ "$PATCH" == "0" ]; then
NEXT_MINOR=$(expr ${MINOR} + 1)
NEXT_PATCH="0"
+ LAST_MINOR=$(expr ${MINOR} - 1)
+ LAST_PATCH="0"
else
# this is a bugfixing release
NEXT_MINOR="${MINOR}"
NEXT_PATCH=$(expr ${PATCH} + 1)
+ LAST_MINOR="${MINOR}"
+ LAST_PATCH=$(expr ${PATCH} - 1)
fi
+LAST_VERSION="$MAJOR.$LAST_MINOR.$LAST_PATCH"
DEVELOPMENT_VERSION="$MAJOR.$NEXT_MINOR.$NEXT_PATCH"
DEVELOPMENT_VERSION="${DEVELOPMENT_VERSION}-SNAPSHOT"
@@ -52,17 +57,18 @@ CURRENT_BRANCH=$(git symbolic-ref -q HEAD)
CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/}
CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD}
-echo "RELEASE_VERSION: ${RELEASE_VERSION}"
-echo "DEVELOPMENT_VERSION: ${DEVELOPMENT_VERSION}"
+echo "LAST_VERSION: ${LAST_VERSION}"
+echo "RELEASE_VERSION: ${RELEASE_VERSION} (this release)"
+echo "DEVELOPMENT_VERSION: ${DEVELOPMENT_VERSION} (the next version after the release)"
echo "CURRENT_BRANCH: ${CURRENT_BRANCH}"
echo
echo "Is this correct?"
echo
-echo "Press enter to continue..."
+echo "Press enter to continue... (or CTRL+C to cancel)"
read
-
+export LAST_VERSION
export RELEASE_VERSION
export DEVELOPMENT_VERSION
export CURRENT_BRANCH
@@ -89,6 +95,26 @@ echo
echo "Press enter to continue..."
read
+
+# calculating stats for release notes
+
+STATS=$(
+echo "### Stats"
+echo "* $(git log pmd_releases/${LAST_VERSION}..HEAD --oneline --no-merges |wc -l) commits"
+echo "* $(curl -s https://api.github.com/repos/pmd/pmd/milestones|jq ".[] | select(.title == \"$RELEASE_VERSION\") | .closed_issues") closed tickets & PRs"
+echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/${LAST_VERSION}) ) / 86400))"
+)
+
+TEMP_RELEASE_NOTES=$(cat docs/pages/release_notes.md)
+TEMP_RELEASE_NOTES=${TEMP_RELEASE_NOTES/\{\% endtocmaker \%\}/$STATS$'\n'$'\n'\{\% endtocmaker \%\}$'\n'}
+echo "${TEMP_RELEASE_NOTES}" > docs/pages/release_notes.md
+
+echo
+echo "Updated stats in release notes:"
+echo "$STATS"
+echo
+echo
+
# install bundles needed for rendering release notes
bundle install --with=release_notes_preprocessing --path vendor/bundle
diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml
index 0d6145cd6c..6c84019247 100644
--- a/docs/_data/sidebars/pmd_sidebar.yml
+++ b/docs/_data/sidebars/pmd_sidebar.yml
@@ -55,6 +55,21 @@ entries:
- title: PMD CLI reference
url: /pmd_userdocs_cli_reference.html
output: web, pdf
+ - title: PMD Report formats
+ url: /pmd_userdocs_report_formats.html
+ output: web, pdf
+ - title: null
+ output: web, pdf
+ subfolders:
+ - title: CPD reference
+ output: web, pdf
+ subfolderitems:
+ - title: Copy-paste detection
+ url: /pmd_userdocs_cpd.html
+ output: web, pdf
+ - title: CPD Report formats
+ url: /pmd_userdocs_cpd_report_formats.html
+ output: web, pdf
- title: null
output: web, pdf
subfolders:
@@ -88,9 +103,6 @@ entries:
- title: Testing your rules
url: /pmd_userdocs_extending_testing.html
output: web, pdf
- - title: Copy-paste detection
- url: /pmd_userdocs_cpd.html
- output: web, pdf
- title: null
output: web, pdf
subfolders:
diff --git a/docs/pages/pmd/projectdocs/committers/releasing.md b/docs/pages/pmd/projectdocs/committers/releasing.md
index 0951ac7e16..f8966a7730 100644
--- a/docs/pages/pmd/projectdocs/committers/releasing.md
+++ b/docs/pages/pmd/projectdocs/committers/releasing.md
@@ -53,6 +53,25 @@ The designer lives at [pmd/pmd-designer](https://github.com/pmd/pmd-designer).
Update property `pmd-designer.version` in **pom.xml** to reference the latest pmd-designer release.
See for the available releases.
+Starting with PMD 6.23.0 we'll provide small statistics for every release. This needs to be added
+to the release notes as the last section. To count the closed issues and pull requests, the milestone
+on github with the title of the new release is searched. Make sure, there is a milestone
+on . The following snippet will
+create the numbers, that can be attached to the release notes as a last section:
+
+```shell
+LAST_VERSION=6.22.0
+NEW_VERSION=6.23.0
+NEW_VERSION_COMMITISH=HEAD
+
+echo "### Stats"
+echo "* $(git log pmd_releases/${LAST_VERSION}..${NEW_VERSION_COMMITISH} --oneline --no-merges |wc -l) commits"
+echo "* $(curl -s https://api.github.com/repos/pmd/pmd/milestones|jq ".[] | select(.title == \"$NEW_VERSION\") | .closed_issues") closed tickets & PRs"
+echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/${LAST_VERSION}) ) / 86400))"
+```
+
+Note: this part is also integrated into `do-release.sh`.
+
Check in all (version) changes to branch master or any other branch, from which the release takes place:
$ git commit -a -m "Prepare pmd release "
diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md
index ca45b101de..a43c809081 100644
--- a/docs/pages/pmd/userdocs/cli_reference.md
+++ b/docs/pages/pmd/userdocs/cli_reference.md
@@ -198,76 +198,5 @@ Example:
## Available Report Formats
PMD comes with many different renderers.
-The mnemonics in bold are used to select them on the command line, as
-arguments to the `-format` option. Some formats accept *properties*,
-which can be specified with the `-property` option on the command-line.
+All formats are described at [PMD Report formats](pmd_userdocs_report_formats.html)
-* **codeclimate**: Renderer for Code Climate JSON format.
-
-* **csv**: Comma-separated values tabular format.
-
- Properties:
-
- * problem: Include problem column. Default: true.
- * package: Include package column. Default: true.
- * file: Include file column. Default: true.
- * priority: Include priority column. Default: true.
- * line: Include line column. Default: true.
- * desc: Include description column. Default: true.
- * ruleSet: Include Rule set column. Default: true.
- * rule: Include Rule column. Default: true.
-
-* **emacs**: GNU Emacs integration.
-
-* **html**: HTML format.
-
- Properties:
-
- * linePrefix: Prefix for line number anchor in the source file.
- * linkPrefix: Path to HTML source.
-
-* **ideaj**: IntelliJ IDEA integration.
-
- Properties:
-
- * classAndMethodName: Class and method name, pass `.method` when processing a directory.
- * sourcePath:
- * fileName:
-
-* **summaryhtml**: Summary HTML format.
-
- Properties:
-
- * linePrefix: Prefix for line number anchor in the source file.
- * linkPrefix: Path to HTML source.
-
-* **text**: Text format.
-
-* **textcolor**: Text format, with color support (requires ANSI console support, e.g. xterm, rxvt, etc.).
-
- Properties:
-
- * color: Enables colors with anything other than `false` or `0`. Default: yes.
-
-* **textpad**: TextPad integration.
-
-* **vbhtml**: Vladimir Bossicard HTML format.
-
-* **xml**: XML format.
-
- Properties:
-
- * encoding: XML encoding format, defaults to UTF-8.
-
-* **xslt**: XML with a XSL transformation applied.
-
- Properties:
-
- * encoding: XML encoding format, defaults to UTF-8.
- * xsltFilename: The XSLT file name.
-
-* **yahtml**: Yet Another HTML format.
-
- Properties:
-
- * outputDir: Output directory.
diff --git a/docs/pages/pmd/userdocs/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md
similarity index 99%
rename from docs/pages/pmd/userdocs/cpd.md
rename to docs/pages/pmd/userdocs/cpd/cpd.md
index e9843661fe..bb341e8e2a 100644
--- a/docs/pages/pmd/userdocs/cpd.md
+++ b/docs/pages/pmd/userdocs/cpd/cpd.md
@@ -239,6 +239,7 @@ This behavior has been introduced to ease CPD integration into scripts or hooks,
* csv_with_linecount_per_file
* vs
+For details, see [CPD Report Formats](pmd_userdocs_cpd_report_formats.html).
## Ant task
diff --git a/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md b/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md
new file mode 100644
index 0000000000..d224b28ee7
--- /dev/null
+++ b/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md
@@ -0,0 +1,220 @@
+---
+title: Report formats for CPD
+tags: [cpd, userdocs]
+keywords: [formats, renderers]
+summary: "Overview of the built-in report formats for CPD"
+permalink: pmd_userdocs_cpd_report_formats.html
+author: Andreas Dangel
+---
+
+## Overview
+
+CPD collects occurrences of found duplications and provides them to the selected report format.
+Each found code duplication appears in one or more other files, so that each code duplication can
+have multiple locations. Not all report formats display all locations.
+
+The following examples always describe the same duplications:
+
+1. a code block of 239 tokens spanning 33 lines in RuleReferenceTest. This is a duplication within the same file.
+2. a code block of 110 tokens spanning 16 lines in JaxenXPathRuleQueryTest. This is a duplication that appears
+ 3 times within the same file.
+
+
+## text
+
+This is the default format.
+
+All duplications are reported one after another. For each duplication, the complete code snippet is output.
+Each duplication is separated by `======`.
+
+Example:
+
+```
+Found a 33 line (239 tokens) duplication in the following files:
+Starting at line 32 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java
+Starting at line 68 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java
+
+ public void testOverride() {
+ final StringProperty PROPERTY1_DESCRIPTOR = new StringProperty("property1", "Test property", null, 0f);
+ MockRule rule = new MockRule();
+ rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR);
+ rule.setLanguage(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME));
+ rule.setName("name1");
+ rule.setProperty(PROPERTY1_DESCRIPTOR, "value1");
+ rule.setMessage("message1");
+ rule.setDescription("description1");
+ rule.addExample("example1");
+ rule.setExternalInfoUrl("externalInfoUrl1");
+ rule.setPriority(RulePriority.HIGH);
+
+ final StringProperty PROPERTY2_DESCRIPTOR = new StringProperty("property2", "Test property", null, 0f);
+ RuleReference ruleReference = new RuleReference();
+ ruleReference.setRule(rule);
+ ruleReference.definePropertyDescriptor(PROPERTY2_DESCRIPTOR);
+ ruleReference.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME));
+ ruleReference
+ .setMinimumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"));
+ ruleReference
+ .setMaximumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"));
+ ruleReference.setDeprecated(true);
+ ruleReference.setName("name2");
+ ruleReference.setProperty(PROPERTY1_DESCRIPTOR, "value2");
+ ruleReference.setProperty(PROPERTY2_DESCRIPTOR, "value3");
+ ruleReference.setMessage("message2");
+ ruleReference.setDescription("description2");
+ ruleReference.addExample("example2");
+ ruleReference.setExternalInfoUrl("externalInfoUrl2");
+ ruleReference.setPriority(RulePriority.MEDIUM_HIGH);
+
+ validateOverridenValues(PROPERTY1_DESCRIPTOR, PROPERTY2_DESCRIPTOR, ruleReference);
+=====================================================================
+Found a 16 line (110 tokens) duplication in the following files:
+Starting at line 66 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java
+Starting at line 88 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java
+Starting at line 110 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java
+
+ JaxenXPathRuleQuery query = createQuery(xpath);
+ List ruleChainVisits = query.getRuleChainVisits();
+ Assert.assertEquals(2, ruleChainVisits.size());
+ Assert.assertTrue(ruleChainVisits.contains("dummyNode"));
+ // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't
+ // match a real node name.
+ Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT));
+
+ DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1);
+ RuleContext data = new RuleContext();
+ data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion());
+
+ query.evaluate(dummy, data);
+ // note: the actual xpath queries are only available after evaluating
+ Assert.assertEquals(2, query.nodeNameToXPaths.size());
+ Assert.assertEquals("self::node()[(attribute::Test1 = \"false\")][(attribute::Test2 = \"true\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString());
+```
+
+
+## xml
+
+This format uses XML to output the duplications in a more structured format.
+
+Example:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+ ruleChainVisits = query.getRuleChainVisits();
+ Assert.assertEquals(2, ruleChainVisits.size());
+ Assert.assertTrue(ruleChainVisits.contains("dummyNode"));
+ // Note: Having AST_ROOT in the rule chain visits is probably a mistake. But it doesn't hurt, it shouldn't
+ // match a real node name.
+ Assert.assertTrue(ruleChainVisits.contains(JaxenXPathRuleQuery.AST_ROOT));
+
+ DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(1);
+ RuleContext data = new RuleContext();
+ data.setLanguageVersion(LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion());
+
+ query.evaluate(dummy, data);
+ // note: the actual xpath queries are only available after evaluating
+ Assert.assertEquals(2, query.nodeNameToXPaths.size());
+ Assert.assertEquals("self::node()[(attribute::Test1 = \"false\")][(attribute::Test2 = \"true\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString());]]>
+
+
+```
+
+
+## csv
+
+This outputs the duplication as comma separated values. It only reports the duplication size (number
+of lines and tokens) and the number of occurrences. After that, the begin lines and filenames are reported on
+after another.
+
+Example:
+
+```
+lines,tokens,occurrences
+33,239,2,32,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java,68,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java
+16,110,3,66,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java,88,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java,110,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java
+```
+
+
+## csv_with_linecount_per_file
+
+This format is similar to "csv", but it has one difference: The duplication size in number of lines is reported
+for each occurrence separately. While the tokens are the same, due to formatting or comments, the code blocks might be
+different. Whitespace and comments are usually ignored when finding duplicated code.
+
+In each line, the duplication size in tokens is reported, then the number of occurrences. And after that, for each
+file, the begin line, the number of duplicated lines and the filename.
+
+Example:
+
+```
+tokens,occurrences
+239,2,32,33,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java,68,33,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenc
+eTest.java
+110,3,66,16,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java,88,16,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java,110,16,/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java
+```
+
+
+## vs
+
+This outputs the duplication in a format, that Visual Studio. CPD can be added as a external tool and the output
+is shown in the console. You can then click on the filenames to jump to the source where the duplication is located.
+
+Each occurrence of a duplication is reported in a separate line, that's why in this example, we have 5 lines.
+
+Example:
+
+```
+/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java(32): Between lines 32 and 65
+/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java(68): Between lines 68 and 101
+/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(66): Between lines 66 and 82
+/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(88): Between lines 88 and 104
+/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java(110): Between lines 110 and 126
+```
diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md
new file mode 100644
index 0000000000..ff7fd639e7
--- /dev/null
+++ b/docs/pages/pmd/userdocs/pmd_report_formats.md
@@ -0,0 +1,339 @@
+---
+title: Report formats for PMD
+tags: [pmd, userdocs]
+keywords: [formats, renderers]
+summary: "Overview of the built-in report formats for CPD"
+permalink: pmd_userdocs_report_formats.html
+author: Andreas Dangel
+---
+
+## Overview
+
+PMD can report the found rule violations in various formats. Some formats can
+be customized further via properties. Violations might also be suppressed and there might
+be processing errors or configuration errors. Not all report formats display all information.
+
+The header of the sections below are used to select the format on the command line, as
+arguments to the `-format` option. When a format accepts *properties*,
+those can be specified with the `-property` / `-P` option on the command-line.
+
+{% include note.html content="Suppressed violations are only reported, if the CLI parameter `-showsuppressed` is set." %}
+
+## codeclimate
+
+Renderer for Code Climate JSON format.
+
+This format is used when running PMD within [Code Climate](https://codeclimate.com/).
+The renderer will stream JSON objects, each object is a own issue (a PMD rule violation). Each issue
+is separated by the null character (`\0`).
+
+The format is specified here: .
+
+The code climate format doesn't support suppressed violations. It also doesn't report any errors. But it contains
+the full rule details for each reported rule violation.
+
+Example:
+
+```
+{"type":"issue","check_name":"GuardLogStatement","description":"Logger calls should be surrounded by log level guards.","content":{"body":"## GuardLogStatement\n\nSince: PMD 5.1.0\n\nPriority: Medium High\n\n[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\n\n[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\n\nWhenever using a log level, one should check if the loglevel is actually enabled, or otherwise skip the associate String creation and manipulation.\n\n### Example:\n\n```java\n\n\n // Add this for performance\n if (log.isDebugEnabled() { ...\n log.debug('log something' + ' and ' + 'concat strings');\n\n \n``` \n\n### [PMD properties](https://pmd.github.io/pmd-6.22.0/pmd_devdocs_working_with_properties.html)\n\nName | Value | Description\n--- | --- | ---\nviolationSuppressRegex | | Suppress violations with messages matching a regular expression\nviolationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\nlogLevels | trace,debug,info,warn,error,log,finest,finer,fine,info,warning,severe | LogLevels to guard\nguardsMethods | isTraceEnabled,isDebugEnabled,isInfoEnabled,isWarnEnabled,isErrorEnabled,isLoggable | Method use to guard the log statement\n"},"categories":["Style"],"location":{"path":"/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java","lines":{"begin":124,"end":125}},"severity":"normal","remediation_points":50000}
+{"type":"issue","check_name":"ForLoopCanBeForeach","description":"This for loop can be replaced by a foreach loop","content":{"body":"## ForLoopCanBeForeach\n\nSince: PMD 6.0.0\n\nPriority: Medium\n\n[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\n\n[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\n\nReports loops that can be safely replaced with the foreach syntax. The rule considers loops over lists, arrays and iterators. A loop is safe to replace if it only uses the index variable to access an element of the list or array, only has one update statement, and loops through *every* element of the list or array left to right.\n\n### Example:\n\n```java\n\n\npublic class MyClass {\n void loop(List l) {\n for (int i = 0; i < l.size(); i++) { // pre Java 1.5\n System.out.println(l.get(i));\n }\n\n for (String s : l) { // post Java 1.5\n System.out.println(s);\n }\n }\n}\n\n \n``` \n\n### [PMD properties](https://pmd.github.io/pmd-6.22.0/pmd_devdocs_working_with_properties.html)\n\nName | Value | Description\n--- | --- | ---\nviolationSuppressRegex | | Suppress violations with messages matching a regular expression\nviolationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\n"},"categories":["Style"],"location":{"path":"/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java","lines":{"begin":58,"end":62}},"severity":"normal","remediation_points":50000}
+```
+
+## csv
+
+Comma-separated values tabular format.
+
+This format only renders rule violations. Suppressed violations or errors are ignored.
+
+Example:
+
+```
+"Problem","Package","File","Priority","Line","Description","Rule set","Rule"
+"1","net.sourceforge.pmd","/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java","2","124","Logger calls should be surrounded by log level guards.","Best Practices","GuardLogStatement"
+"1","net.sourceforge.pmd.benchmark","/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java","3","58","This for loop can be replaced by a foreach loop","Best Practices","ForLoopCanBeForeach"
+```
+
+This format can be configured to display only certain columns. In order to not show the problem counter and package
+columns, use these CLI parameters additionally: `-property problem=false -property package=false`
+
+**Properties:**
+
+* problem: Include problem column. Default: true.
+* package: Include package column. Default: true.
+* file: Include file column. Default: true.
+* priority: Include priority column. Default: true.
+* line: Include line column. Default: true.
+* desc: Include description column. Default: true.
+* ruleSet: Include Rule set column. Default: true.
+* rule: Include Rule column. Default: true.
+
+## emacs
+
+GNU Emacs integration.
+
+Example:
+
+```
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: Logger calls should be surrounded by log level guards.
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: This for loop can be replaced by a foreach loop
+```
+
+## html
+
+HTML format.
+
+This renderer provides two properties. If these are provided, then a link to the source where the violations
+have been found is rendered. The following example has been created with `-property linkPrefix=https://github.com/pmd/pmd/blob/master/ -property linePrefix=L -shortnames -d pmd`.
+
+When using [Maven JXR Plugin](https://maven.apache.org/jxr/maven-jxr-plugin/index.html) to generate a html view
+of the project's sources, then the property "htmlExtension" needs to be set to "true". This will then replace the
+normal source file extensions (e.g. ".java") with ".html", so that the generated html pages are referenced.
+
+[Example](report-examples/pmd-report-html.html)
+
+**Properties:**
+
+* linePrefix: Prefix for line number anchor in the source file.
+* linkPrefix: Path to HTML source.
+* htmlExtension: Replace file extension with .html for the links (default: false)
+
+## ideaj
+
+IntelliJ IDEA integration.
+
+{% include warning.html content="This format can only be used as described in [Tools: IDEA](pmd_userdocs_tools.html#idea)." %}
+
+It has two ways of calling:
+
+1. For a single file: then all three properties need to be provided
+
+`run.sh pmd -d src/Foo.java -R rulesets/java/quickstart.xml -f ideaj -P fileName=src/Foo.java -P sourcePath=/home/pmd/src -P classAndMethodName=Foo`
+
+2. For a directory: then the fileName property can be omitted
+
+`run.sh pmd -d src -R rulesets/java/quickstart.xml -f ideaj -P sourcePath=/home/pmd/src -P classAndMethodName=.method`
+
+Example:
+
+```
+Logger calls should be surrounded by log level guards.
+ at Foo(:124)
+This for loop can be replaced by a foreach loop
+ at Foo(:58)
+```
+
+**Properties:**
+
+* classAndMethodName: Class and method name, pass `.method` when processing a directory.
+* sourcePath:
+* fileName:
+
+## summaryhtml
+
+Summary HTML format.
+
+This is the [html renderer](#html) but with an extra section, the summarizes the violations per rule.
+
+[Example](report-examples/pmd-report-summaryhtml.html)
+
+**Properties:**
+
+* linePrefix: Prefix for line number anchor in the source file.
+* linkPrefix: Path to HTML source.
+* htmlExtension: Replace file extension with .html for the links (default: false)
+
+## text (default)
+
+This is the default format.
+
+This format outputs one line per violation. At the end, processing errors, suppressed violations
+and configuration errors are reported.
+
+Example:
+
+```
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: Logger calls should be surrounded by log level guards.
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: This for loop can be replaced by a foreach loop
+/home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java - PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+CloseResource rule violation suppressed by Annotation in /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
+LoosePackageCoupling - No packages or classes specified
+```
+
+## textcolor
+
+Text format, with color support (requires ANSI console support, e.g. xterm, rxvt, etc.).
+
+Example:
+
+
+* file: ./pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java
+ src: RuleContext.java:124:125
+ rule: GuardLogStatement
+ msg: Logger calls should be surrounded by log level guards.
+ code: LOG.warning("The method RuleContext::setSourceCodeFilename(String) has been deprecated and will be removed."
+
+* file: ./pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java
+ src: Benchmarker.java:58:62
+ rule: ForLoopCanBeForeach
+ msg: This for loop can be replaced by a foreach loop
+ code: for (int i = 0; i < args.length; i++) {
+
+
+
+Summary:
+
+net.sourceforge.pmd.RuleContext : 1
+net.sourceforge.pmd.benchmark.Benchmarker : 1
+* file: ./pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+ err: PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+net.sourceforge.pmd.PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:110)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:89)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:51)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:78)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:24)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
+ at java.base/java.lang.Thread.run(Thread.java:832)
+Caused by: net.sourceforge.pmd.lang.java.ast.ParseException: Encountered " "-" "- "" at line 6, column 30.
+Was expecting one of:
+ "extends" ...
+ "implements" ...
+ "{" ...
+ "<" ...
+
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.generateParseException(JavaParser.java:12713)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.jj_consume_token(JavaParser.java:12597)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceBody(JavaParser.java:1554)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceDeclaration(JavaParser.java:732)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.TypeDeclaration(JavaParser.java:639)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.CompilationUnit(JavaParser.java:373)
+ at net.sourceforge.pmd.lang.java.AbstractJavaParser.parse(AbstractJavaParser.java:62)
+ at net.sourceforge.pmd.SourceCodeProcessor.parse(SourceCodeProcessor.java:121)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:185)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:107)
+ ... 10 more
+
+
+* rule: LoosePackageCoupling
+ err: No packages or classes specified
+
+* errors: 2
+* warnings: 2
+
+
+**Properties:**
+
+* color: Enables colors with anything other than `false` or `0`. Default: yes.
+
+## textpad
+
+TextPad integration.
+
+Example:
+
+```
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java(124, GuardLogStatement): Logger calls should be surrounded by log level guards.
+/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java(58, ForLoopCanBeForeach): This for loop can be replaced by a foreach loop
+```
+
+## vbhtml
+
+Vladimir Bossicard HTML format.
+
+
+## xml
+
+XML format.
+
+This format is a XML document, that can be validated by a XSD schema. The schema is [report_2_0_0.xsd](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/resources/report_2_0_0.xsd).
+
+Example:
+
+```xml
+
+
+
+
+Logger calls should be surrounded by log level guards.
+
+
+
+
+This for loop can be replaced by a foreach loop
+
+
+
+
+
+
+
+
+```
+
+**Properties:**
+
+* encoding: XML encoding format, defaults to UTF-8.
+
+## xslt
+
+XML with a XSL transformation applied.
+
+PMD provides one built-in stylesheet, that is used by default, if no other
+stylesheet with the property "xsltFilename" is specified. It is called [pmd-nicerhtml.xsl](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/resources/pmd-nicerhtml.xsl) and can be used for customization.
+
+[Example with pmd-nicerhtml.xsl](report-examples/pmd-report-pmd-nicerhtml.html)
+
+**Properties:**
+
+* encoding: XML encoding format, defaults to UTF-8.
+* xsltFilename: The XSLT file name.
+
+## yahtml
+
+Yet Another HTML format.
+
+This renderer creates an html file per analyzed source file, hence you need to specify a output directory.
+The output directory must exist. If not specified, the html files are created in the current directory.
+
+[Example](report-examples/pmd-report-yahtml/index.html)
+
+**Properties:**
+
+* outputDir: Output directory.
diff --git a/docs/pages/pmd/userdocs/tools/tools.md b/docs/pages/pmd/userdocs/tools/tools.md
index b8427311fe..b3e8d550c9 100644
--- a/docs/pages/pmd/userdocs/tools/tools.md
+++ b/docs/pages/pmd/userdocs/tools/tools.md
@@ -243,11 +243,11 @@ Here's how to set it up as an "External Tool":
* Name: PMD
* Description: PMD, good for what ails you.
* Menu: Select the "Main menu", "Project views", "Editor menu", and "Search results" checkboxes.
- * Program: $JDKPath$\bin\java.exe
+ * Program: `c:\pmd\bin\pmd.bat`
* For the next parameter you'll need to plug in the location of your PMD installation
and the rulesets you want to use
* Parameters:
- `-cp %CLASSPATH%;c:\pmd\lib\pmd-{{pmd.site.version}}.jar;c:\pmd\lib\asm-3.2.jar;c:\pmd\lib\jaxen-1.1.1.jar net.sourceforge.pmd.PMD "$FilePath$" ideaj unusedcode,imports "$Sourcepath$" $FileClass$.method $FileName$`
+ `-d "$FilePath$" -f ideaj -R rulesets/java/quickstart.xml -P sourcePath="$Sourcepath$" -P classAndMethodName=$FileClass$.method -P fileName=$FileName$`
That's pretty much it. Now you can right click on a source directory and select PMD,
it'll run recursively on the source files, and the results should
diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index b20e4ec7ff..ab8bffd0a5 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -46,12 +46,18 @@ Note that XPath 1.0 support, the default XPath version, is deprecated since PMD
* [#2210](https://github.com/pmd/pmd/issues/2210): \[apex] ApexCRUDViolation: Support WITH SECURITY_ENFORCED
* [#2399](https://github.com/pmd/pmd/issues/2399): \[apex] ApexCRUDViolation: false positive with security enforced with line break
* core
+ * [#2019](https://github.com/pmd/pmd/issues/2019): \[core] Insufficient deprecation warnings for XPath attributes
+* doc
* [#2355](https://github.com/pmd/pmd/issues/2355): \[doc] Improve documentation about incremental analysis
* [#2356](https://github.com/pmd/pmd/issues/2356): \[doc] Add missing doc about pmd.github.io
* [#2412](https://github.com/pmd/pmd/issues/2412): \[core] HTMLRenderer doesn't render links to source files
+ * [#2413](https://github.com/pmd/pmd/issues/2413): \[doc] Improve documentation about the available renderers (PMD/CPD)
* java
* [#2378](https://github.com/pmd/pmd/issues/2378): \[java] AbstractJUnitRule has bad performance on large code bases
+* java-bestpractices
+ * [#2398](https://github.com/pmd/pmd/issues/2398): \[java] AbstractClassWithoutAbstractMethod false negative with inner abstract classes
* java-codestyle
+ * [#1164](https://github.com/pmd/pmd/issues/1164): \[java] ClassNamingConventions suggests to add Util for class containing only static constants
* [#1723](https://github.com/pmd/pmd/issues/1723): \[java] UseDiamondOperator false-positive inside lambda
* java-design
* [#2390](https://github.com/pmd/pmd/issues/2390): \[java] AbstractClassWithoutAnyMethod: missing violation for nested classes
@@ -59,6 +65,8 @@ Note that XPath 1.0 support, the default XPath version, is deprecated since PMD
* [#2402](https://github.com/pmd/pmd/issues/2402): \[java] CloseResource possible false positive with Primitive Streams
* java-multithreading
* [#2313](https://github.com/pmd/pmd/issues/2313): \[java] Documenation for DoNotUseThreads is outdated
+* javascript-errorprone
+ * [#384](https://github.com/pmd/pmd/issues/384): \[javascript] Trailing commas not detected on French default locale
### API Changes
@@ -69,6 +77,11 @@ Note that XPath 1.0 support, the default XPath version, is deprecated since PMD
Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0.
You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning.
+* {% jdoc core::lang.rule.xpath.AbstractXPathRuleQuery %}
+* {% jdoc core::lang.rule.xpath.JaxenXPathRuleQuery %}
+* {% jdoc core::lang.rule.xpath.SaxonXPathRuleQuery %}
+* {% jdoc core::lang.rule.xpath.XPathRuleQuery %}
+
##### In ASTs
As part of the changes we'd like to do to AST classes for 7.0.0, we would like to
@@ -118,6 +131,12 @@ implementations, and their corresponding Parser if it exists (in the same packag
* {% jdoc matlab::lang.matlab.MatlabTokenManager %}
* {% jdoc objectivec::lang.objectivec.ObjectiveCTokenManager %}
+In the **Java AST** the following attributes are deprecated and will issue a warning when used in XPath rules:
+
+* {% jdoc !!java::lang.java.ast.ASTAdditiveExpression#getImage() %} - use `getOperator()` instead
+* {% jdoc !!java::lang.java.ast.ASTVariableDeclaratorId#getImage() %} - use `getName()` instead
+* {% jdoc !!java::lang.java.ast.ASTVariableDeclaratorId#getVariableName() %} - use `getName()` instead
+
##### For removal
* {% jdoc !!core::lang.Parser#getTokenManager(java.lang.String,java.io.Reader) %}
@@ -125,6 +144,10 @@ implementations, and their corresponding Parser if it exists (in the same packag
* {% jdoc !!core::lang.ast.AbstractTokenManager#setFileName(java.lang.String) %}
* {% jdoc !!core::lang.ast.AbstractTokenManager#getFileName(java.lang.String) %}
* {% jdoc !!core::cpd.token.AntlrToken#getType() %} - use `getKind()` instead.
+* {% jdoc core::lang.rule.ImmutableLanguage %}
+* {% jdoc core::lang.rule.MockRule %}
+* {% jdoc !!java::lang.java.ast.ASTRecordDeclaration#getComponentList() %}
+* Multiple fields, constructors and methods in {% jdoc core::lang.rule.XPathRule %}. See javadoc for details.
### External Contributions
@@ -137,6 +160,9 @@ implementations, and their corresponding Parser if it exists (in the same packag
* [#2397](https://github.com/pmd/pmd/pull/2397): \[apex] fixed WITH SECURITY_ENFORCED regex to recognise line break characters - [Kieran Black](https://github.com/kieranlblack)
* [#2401](https://github.com/pmd/pmd/pull/2401): \[doc] Update DoNotUseThreads rule documentation - [Saikat Sengupta](https://github.com/s4ik4t)
* [#2403](https://github.com/pmd/pmd/pull/2403): \[java] #2402 fix false-positives on Primitive Streams - [Bernd Farka](https://github.com/BerndFarkaDyna)
+* [#2409](https://github.com/pmd/pmd/pull/2409): \[java] ClassNamingConventions suggests to add Util for class containing only static constants, fixes #1164 - [Binu R J](https://github.com/binu-r)
+* [#2411](https://github.com/pmd/pmd/pull/2411): \[java] Fix UseAssertEqualsInsteadOfAssertTrue Example - [Moritz Scheve](https://github.com/Blightbuster)
+* [#2423](https://github.com/pmd/pmd/pull/2423): \[core] Fix Checkstyle OperatorWrap in AbstractTokenizer - [Harsh Kukreja](https://github.com/harsh-kukreja)
{% endtocmaker %}
diff --git a/docs/report-examples/pmd-report-html.html b/docs/report-examples/pmd-report-html.html
new file mode 100644
index 0000000000..2c1c41342d
--- /dev/null
+++ b/docs/report-examples/pmd-report-html.html
@@ -0,0 +1,67 @@
+PMD
+PMD report
Problems found
Processing errors
+File | Problem |
+
+pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java |
+net.sourceforge.pmd.PMDException: Error while parsing pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:110)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:89)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:51)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:78)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:24)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
+ at java.base/java.lang.Thread.run(Thread.java:832)
+Caused by: net.sourceforge.pmd.lang.java.ast.ParseException: Encountered " "-" "- "" at line 6, column 30.
+Was expecting one of:
+ "extends" ...
+ "implements" ...
+ "{" ...
+ "<" ...
+
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.generateParseException(JavaParser.java:12713)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.jj_consume_token(JavaParser.java:12597)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceBody(JavaParser.java:1554)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceDeclaration(JavaParser.java:732)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.TypeDeclaration(JavaParser.java:639)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.CompilationUnit(JavaParser.java:373)
+ at net.sourceforge.pmd.lang.java.AbstractJavaParser.parse(AbstractJavaParser.java:62)
+ at net.sourceforge.pmd.SourceCodeProcessor.parse(SourceCodeProcessor.java:121)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:185)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:107)
+ ... 10 more
+ |
+
+
Suppressed warnings
Configuration errors
\ No newline at end of file
diff --git a/docs/report-examples/pmd-report-pmd-nicerhtml.html b/docs/report-examples/pmd-report-pmd-nicerhtml.html
new file mode 100644
index 0000000000..702d8b1e8f
--- /dev/null
+++ b/docs/report-examples/pmd-report-pmd-nicerhtml.html
@@ -0,0 +1,223 @@
+
+
+
+
+ PMD 6.22.0 Report
+
+
+
+
+
+
+ |
+
+ PMD 6.22.0 Report. Generated on 2020-04-11 - 19:23:45
+ |
+
+
+
+ Summary
+
+
+ Files |
+ Total |
+
+ Priority 1
+ |
+
+ Priority 2
+ |
+
+ Priority 3
+ |
+
+ Priority 4
+ |
+
+ Priority 5
+ |
+
+
+ 2 |
+ 2 |
+ 0 |
+ 1 |
+ 1 |
+ 0 |
+ 0 |
+
+
+
+ Rules
+
+
+ Rule |
+ Violations |
+ Severity |
+
+
+
+ [Best Practices] GuardLogStatement |
+ 1 |
+
+ 2
+ |
+
+
+
+ [Best Practices] ForLoopCanBeForeach |
+ 1 |
+
+ 3
+ |
+
+
+
+ Files
+
+
+
+ File /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java
+
+ Back to top
+
+ File /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java
+
+ Back to top
+
+
+
\ No newline at end of file
diff --git a/docs/report-examples/pmd-report-summaryhtml.html b/docs/report-examples/pmd-report-summaryhtml.html
new file mode 100644
index 0000000000..92dc3e1f72
--- /dev/null
+++ b/docs/report-examples/pmd-report-summaryhtml.html
@@ -0,0 +1,74 @@
+PMD
+Summary
+
+Rule name | Number of violations |
+GuardLogStatement | 1 |
+ForLoopCanBeForeach | 1 |
+
+Detail
+PMD report
Problems found
Processing errors
+File | Problem |
+
+pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java |
+net.sourceforge.pmd.PMDException: Error while parsing pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:110)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:89)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:51)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:78)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:1)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
+ at java.base/java.lang.Thread.run(Thread.java:834)
+Caused by: net.sourceforge.pmd.lang.java.ast.ParseException: Encountered " "-" "- "" at line 6, column 30.
+Was expecting one of:
+ "extends" ...
+ "implements" ...
+ "{" ...
+ "<" ...
+
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.generateParseException(JavaParser.java:12731)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.jj_consume_token(JavaParser.java:12615)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceBody(JavaParser.java:1574)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceDeclaration(JavaParser.java:779)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.TypeDeclaration(JavaParser.java:686)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.CompilationUnit(JavaParser.java:420)
+ at net.sourceforge.pmd.lang.java.AbstractJavaParser.parse(AbstractJavaParser.java:62)
+ at net.sourceforge.pmd.SourceCodeProcessor.parse(SourceCodeProcessor.java:121)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:185)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:107)
+ ... 10 more
+ |
+
+
Suppressed warnings
Configuration errors
\ No newline at end of file
diff --git a/docs/report-examples/pmd-report-vbhtml.html b/docs/report-examples/pmd-report-vbhtml.html
new file mode 100644
index 0000000000..f6ce69c610
--- /dev/null
+++ b/docs/report-examples/pmd-report-vbhtml.html
@@ -0,0 +1,42 @@
+PMD
+124 | Logger calls should be surrounded by log level guards. |
+
+58 | This for loop can be replaced by a foreach loop |
+
/home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java | net.sourceforge.pmd.PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:110)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:89)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:51)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:78)
+ at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:24)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
+ at java.base/java.lang.Thread.run(Thread.java:832)
+Caused by: net.sourceforge.pmd.lang.java.ast.ParseException: Encountered " "-" "- "" at line 6, column 30.
+Was expecting one of:
+ "extends" ...
+ "implements" ...
+ "{" ...
+ "<" ...
+
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.generateParseException(JavaParser.java:12713)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.jj_consume_token(JavaParser.java:12597)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceBody(JavaParser.java:1554)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.ClassOrInterfaceDeclaration(JavaParser.java:732)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.TypeDeclaration(JavaParser.java:639)
+ at net.sourceforge.pmd.lang.java.ast.JavaParser.CompilationUnit(JavaParser.java:373)
+ at net.sourceforge.pmd.lang.java.AbstractJavaParser.parse(AbstractJavaParser.java:62)
+ at net.sourceforge.pmd.SourceCodeProcessor.parse(SourceCodeProcessor.java:121)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:185)
+ at net.sourceforge.pmd.SourceCodeProcessor.processSourceCodeWithoutCache(SourceCodeProcessor.java:107)
+ ... 10 more
+ |
LoosePackageCoupling | No packages or classes specified |
diff --git a/docs/report-examples/pmd-report-yahtml/Benchmarker.html b/docs/report-examples/pmd-report-yahtml/Benchmarker.html
new file mode 100644
index 0000000000..2e0da807d1
--- /dev/null
+++ b/docs/report-examples/pmd-report-yahtml/Benchmarker.html
@@ -0,0 +1,15 @@
+
+
+
+
+ PMD - Benchmarker
+
+
+ Class View
+ Class: Benchmarker
+
+ Method | Violation |
+ findBooleanSwitch | Rule: | ForLoopCanBeForeach | Description: | This for loop can be replaced by a foreach loop | Line: | 58 and 62 |
|
+
+
+
diff --git a/docs/report-examples/pmd-report-yahtml/RuleContext.html b/docs/report-examples/pmd-report-yahtml/RuleContext.html
new file mode 100644
index 0000000000..1f401103f4
--- /dev/null
+++ b/docs/report-examples/pmd-report-yahtml/RuleContext.html
@@ -0,0 +1,15 @@
+
+
+
+
+ PMD - RuleContext
+
+
+ Class View
+ Class: RuleContext
+
+ Method | Violation |
+ setSourceCodeFilename | Rule: | GuardLogStatement | Description: | Logger calls should be surrounded by log level guards. | Line: | 124 and 125 |
|
+
+
+
diff --git a/docs/report-examples/pmd-report-yahtml/index.html b/docs/report-examples/pmd-report-yahtml/index.html
new file mode 100644
index 0000000000..c8a696b998
--- /dev/null
+++ b/docs/report-examples/pmd-report-yahtml/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+ PMD
+
+
+ Package View
+
+ Package | Class | # |
+ Aggregate | - | 2 |
+ net | - | 2 |
+ net.sourceforge | - | 2 |
+ net.sourceforge.pmd | - | 2 |
+ net.sourceforge.pmd | RuleContext | 1 |
+ net.sourceforge.pmd.benchmark | - | 1 |
+ net.sourceforge.pmd.benchmark | Benchmarker | 1 |
+
+
+
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java
index 561ec95e63..00bfdff2d9 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java
@@ -12,7 +12,6 @@ import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ParserOptions;
import net.sourceforge.pmd.lang.VisitorStarter;
-import net.sourceforge.pmd.lang.XPathHandler;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
@@ -22,7 +21,6 @@ import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey;
import net.sourceforge.pmd.lang.apex.multifile.ApexMultifileVisitorFacade;
import net.sourceforge.pmd.lang.apex.rule.ApexRuleViolationFactory;
-import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
@@ -38,12 +36,6 @@ public class ApexHandler extends AbstractLanguageVersionHandler {
return rootNode -> new ApexMultifileVisitorFacade().initializeWith((ApexNode>) rootNode);
}
-
- @Override
- public XPathHandler getXPathHandler() {
- return new DefaultASTXPathHandler();
- }
-
@Override
public RuleViolationFactory getRuleViolationFactory() {
return ApexRuleViolationFactory.INSTANCE;
diff --git a/pmd-apex/src/main/resources/category/apex/codestyle.xml b/pmd-apex/src/main/resources/category/apex/codestyle.xml
index f04a7580f8..0c0ea51a4a 100644
--- a/pmd-apex/src/main/resources/category/apex/codestyle.xml
+++ b/pmd-apex/src/main/resources/category/apex/codestyle.xml
@@ -45,12 +45,13 @@ from the rest.
3
+
0]
+//IfBlockStatement/BlockStatement[@CurlyBrace= false()][count(child::*) > 0]
|
-//IfElseBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0]
+//IfElseBlockStatement/BlockStatement[@CurlyBrace= false()][count(child::*) > 0]
]]>
@@ -82,10 +83,11 @@ controlled from the rest.
3
+
@@ -163,12 +165,13 @@ from the rest.
3
+
@@ -272,6 +275,7 @@ can lead to quite messy code. This rule looks for several declarations on the sa
1
+
3
+
diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml
index fb4b39c19b..ecea64c472 100644
--- a/pmd-apex/src/main/resources/category/apex/errorprone.xml
+++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml
@@ -59,6 +59,7 @@ Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Tr
3
+
3
+
3
+
3
+
@@ -221,6 +225,7 @@ Avoid empty try or finally blocks - what's the point?
3
+
3
+
0) {
// We are done, we found the end of the string...
done = true;
- } else if (tok == '\\') { // Found an escaped char
- escaped = true;
- } else { // Adding char...
- escaped = false;
+ } else {
+ // Found an escaped char?
+ escaped = tok == '\\';
}
// Adding char to String:" + token.toString());
token.append(tok);
loc++;
}
// Handling multiple lines string
- if (!done && // ... we didn't find the end of the string
- loc >= currentLine.length() && // ... we have reach the end of
- // the line ( the String is
- // incomplete, for the moment at
- // least)
- spanMultipleLinesString && // ... the language allow multiple
- // line span Strings
- lineNumber < code.size() - 1 // ... there is still more lines to
- // parse
+ if (!done // ... we didn't find the end of the string (but the end of the line)
+ && spanMultipleLinesString // ... the language allow multiple line span Strings
+ && lineNumber < code.size() - 1 // ... there is still more lines to parse
) {
// removes last character, if it is the line continuation (e.g.
// backslash) character
- if (spanMultipleLinesLineContinuationCharacter != null && token.length() > 0
- && token.charAt(token.length() - 1) == spanMultipleLinesLineContinuationCharacter.charValue()) {
+ if (spanMultipleLinesLineContinuationCharacter != null
+ && token.length() > 0
+ && token.charAt(token.length() - 1) == spanMultipleLinesLineContinuationCharacter) {
token.deleteCharAt(token.length() - 1);
}
// parsing new line
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java
index 9fb7fb820f..634e00fe24 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java
@@ -18,6 +18,7 @@ import net.sourceforge.pmd.util.designerbindings.DesignerBindings;
*/
public abstract class AbstractLanguageVersionHandler implements LanguageVersionHandler {
+
@Override
public DataFlowHandler getDataFlowHandler() {
return DataFlowHandler.DUMMY;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java
index 95728185d7..ebdf801d80 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/XPathHandler.java
@@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang;
import org.jaxen.Navigator;
import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler;
import net.sourceforge.pmd.lang.xpath.Initializer;
import net.sf.saxon.sxpath.IndependentContext;
@@ -19,22 +20,7 @@ import net.sf.saxon.sxpath.IndependentContext;
@Deprecated
public interface XPathHandler {
- XPathHandler DUMMY = new XPathHandler() {
- @Override
- public void initialize() {
- // empty handler - does nothing
- }
-
- @Override
- public void initialize(IndependentContext context) {
- // empty handler - does nothing
- }
-
- @Override
- public Navigator getNavigator() {
- return null;
- }
- };
+ XPathHandler DUMMY = new DefaultASTXPathHandler();
/**
* Initialize. This is intended to be called by {@link Initializer} to
@@ -52,7 +38,8 @@ public interface XPathHandler {
* Get a Jaxen Navigator for this Language. May return null
if
* there is no Jaxen Navigation for this language.
*
- * @deprecated Support for Jaxen will be removed come 7.0.0
+ * @deprecated Support for Jaxen will be removed come 7.0.0. This isn't used
+ * anymore
*/
@Deprecated
Navigator getNavigator();
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java
index c14ede1d12..2f8d3eb78c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java
@@ -25,6 +25,8 @@ import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator;
import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
+import net.sourceforge.pmd.lang.ast.xpath.internal.ContextualizedNavigator;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.dfa.DataFlowNode;
import net.sourceforge.pmd.util.DataMap;
import net.sourceforge.pmd.util.DataMap.DataKey;
@@ -513,7 +515,8 @@ public abstract class AbstractNode implements Node {
@Override
@SuppressWarnings("unchecked")
public List findChildNodesWithXPath(final String xpathString) throws JaxenException {
- return new BaseXPath(xpathString, new DocumentNavigator()).selectNodes(this);
+ return new BaseXPath(xpathString, new ContextualizedNavigator(DeprecatedAttrLogger.createAdHocLogger()))
+ .selectNodes(this);
}
@Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java
index f4c1e8ddbe..eb470c94bd 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AbstractASTXPathHandler.java
@@ -25,4 +25,14 @@ public abstract class AbstractASTXPathHandler implements XPathHandler {
public void initialize(IndependentContext context, Language language, Class> functionsClass) {
context.declareNamespace("pmd-" + language.getTerseName(), "java:" + functionsClass.getName());
}
+
+ @Override
+ public void initialize() {
+ // override if needed
+ }
+
+ @Override
+ public void initialize(IndependentContext context) {
+ // override if needed
+ }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java
index cee0734a9c..7f4573ce97 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java
@@ -9,12 +9,9 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import net.sourceforge.pmd.annotation.Experimental;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute;
@@ -29,11 +26,6 @@ import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute;
* @author daniels
*/
public class Attribute {
-
-
- private static final Logger LOG = Logger.getLogger(Attribute.class.getName());
- static final ConcurrentMap DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>();
-
private static final Object[] EMPTY_OBJ_ARRAY = new Object[0];
private final Node parent;
@@ -73,9 +65,22 @@ public class Attribute {
return method == null ? String.class : method.getReturnType();
}
- private boolean isAttributeDeprecated() {
- return method != null && (method.isAnnotationPresent(Deprecated.class)
- || method.isAnnotationPresent(DeprecatedAttribute.class));
+ /**
+ * Returns null for "not deprecated", empty string for "deprecated without replacement",
+ * otherwise name of replacement attribute.
+ */
+ @InternalApi
+ public String replacementIfDeprecated() {
+ if (method == null) {
+ return null;
+ } else {
+ DeprecatedAttribute annot = method.getAnnotation(DeprecatedAttribute.class);
+ return annot != null
+ ? annot.replaceWith()
+ : method.isAnnotationPresent(Deprecated.class)
+ ? DeprecatedAttribute.NO_REPLACEMENT
+ : null;
+ }
}
public Object getValue() {
@@ -83,12 +88,6 @@ public class Attribute {
return value.get(0);
}
- if (LOG.isLoggable(Level.WARNING) && isAttributeDeprecated()
- && DETECTED_DEPRECATED_ATTRIBUTES.putIfAbsent(getLoggableAttributeName(), Boolean.TRUE) == null) {
- // this message needs to be kept in sync with PMDCoverageTest / BinaryDistributionIT
- LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in XPath query");
- }
-
// this lazy loading reduces calls to Method.invoke() by about 90%
try {
value = Collections.singletonList(method.invoke(parent, EMPTY_OBJ_ARRAY));
@@ -129,11 +128,6 @@ public class Attribute {
return Objects.hash(parent, name);
}
-
- private String getLoggableAttributeName() {
- return parent.getXPathNodeName() + "/@" + name;
- }
-
@Override
public String toString() {
return name + ':' + getValue() + ':' + parent.getXPathNodeName();
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java
index 974d426e80..3fa844f28a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/DefaultASTXPathHandler.java
@@ -12,6 +12,7 @@ import net.sf.saxon.sxpath.IndependentContext;
@Deprecated
@InternalApi
public class DefaultASTXPathHandler extends AbstractASTXPathHandler {
+
@Override
public void initialize() {
// override if needed
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/ContextualizedNavigator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/ContextualizedNavigator.java
new file mode 100644
index 0000000000..358d7d7ce7
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/ContextualizedNavigator.java
@@ -0,0 +1,27 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.ast.xpath.internal;
+
+import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
+
+/**
+ * Navigator that records attribute usages.
+ */
+public class ContextualizedNavigator extends DocumentNavigator {
+
+ private final DeprecatedAttrLogger ctx;
+
+ public ContextualizedNavigator(DeprecatedAttrLogger ctx) {
+ this.ctx = ctx;
+ }
+
+ @Override
+ public String getAttributeStringValue(Object arg0) {
+ Attribute attr = (Attribute) arg0;
+ ctx.recordUsageOf(attr);
+ return attr.getStringValue();
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttrLogger.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttrLogger.java
new file mode 100644
index 0000000000..e6c2358a06
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttrLogger.java
@@ -0,0 +1,117 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.ast.xpath.internal;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.rule.XPathRule;
+
+/**
+ * Records usages of deprecated attributes in XPath rules. This needs
+ * to be threadsafe, XPath rules have one each (and share it).
+ */
+public abstract class DeprecatedAttrLogger {
+
+ private static final Logger LOG = Logger.getLogger(Attribute.class.getName());
+
+
+ public abstract void recordUsageOf(Attribute attribute);
+
+ /**
+ * Create a new context for the given rule, returns a noop implementation
+ * if the warnings would be ignored anyway.
+ */
+ public static DeprecatedAttrLogger create(XPathRule rule) {
+ if (LOG.isLoggable(Level.WARNING)) {
+ return new AttrLoggerImpl(rule);
+ } else {
+ return noop();
+ }
+ }
+
+ public static DeprecatedAttrLogger createAdHocLogger() {
+ if (LOG.isLoggable(Level.WARNING)) {
+ return new AdhocLoggerImpl();
+ } else {
+ return noop();
+ }
+ }
+
+ public static Noop noop() {
+ return Noop.INSTANCE;
+ }
+
+ private static String getLoggableAttributeName(Attribute attr) {
+ return attr.getParent().getXPathNodeName() + "/@" + attr.getName();
+ }
+
+ private static class Noop extends DeprecatedAttrLogger {
+
+ static final Noop INSTANCE = new Noop();
+
+ @Override
+ public void recordUsageOf(Attribute attribute) {
+ // do nothing
+ }
+ }
+
+ private static class AttrLoggerImpl extends DeprecatedAttrLogger {
+
+ private final ConcurrentMap deprecated = new ConcurrentHashMap<>();
+ private final XPathRule rule;
+
+ private AttrLoggerImpl(XPathRule rule) {
+ this.rule = rule;
+ }
+
+ @Override
+ public void recordUsageOf(Attribute attribute) {
+ String replacement = attribute.replacementIfDeprecated();
+ if (replacement != null) {
+ String name = getLoggableAttributeName(attribute);
+ Boolean b = deprecated.putIfAbsent(name, Boolean.TRUE);
+ if (b == null) {
+ // this message needs to be kept in sync with PMDCoverageTest / BinaryDistributionIT
+ String msg = "Use of deprecated attribute '" + name + "' by XPath rule " + ruleToString();
+ if (!replacement.isEmpty()) {
+ msg += ", please use " + replacement + " instead";
+ }
+ LOG.warning(msg);
+ }
+ }
+ }
+
+ public String ruleToString() {
+ // we can't compute that beforehand because the name is set
+ // outside of the rule constructor
+ String name = "'" + rule.getName() + "'";
+ if (rule.getRuleSetName() != null) {
+ name += " (in ruleset '" + rule.getRuleSetName() + "')";
+ }
+ return name;
+ }
+ }
+
+ private static class AdhocLoggerImpl extends DeprecatedAttrLogger {
+ @Override
+ public void recordUsageOf(Attribute attribute) {
+ String replacement = attribute.replacementIfDeprecated();
+ if (replacement != null) {
+ String name = getLoggableAttributeName(attribute);
+ // this message needs to be kept in sync with PMDCoverageTest / BinaryDistributionIT
+ String msg = "Use of deprecated attribute '" + name + "' in a findChildNodesWithXPath navigation";
+ if (!replacement.isEmpty()) {
+ msg += ", please use " + replacement + " instead";
+ }
+ // log with execption stack trace to help figure out where exactly the xpath is used.
+ LOG.log(Level.WARNING, msg, new RuntimeException(msg));
+ }
+ }
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java
index 75e80d4f5f..430cf5dd73 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java
@@ -22,4 +22,13 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DeprecatedAttribute {
+
+ String NO_REPLACEMENT = "";
+
+
+ /**
+ * The simple name of the attribute to use for replacement (with '@' prefix).
+ * If empty, then the attribute is deprecated for removal.
+ */
+ String replaceWith() default NO_REPLACEMENT;
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java
index 1e7c8e0825..822bb9d797 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AbstractNodeInfo.java
@@ -260,11 +260,11 @@ public class AbstractNodeInfo implements VirtualNode, SiblingCountingNode {
*/
@Override
public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
- AxisIterator axisIterator = iterateAxis(axisNumber);
- if (nodeTest != null) {
- axisIterator = new AxisFilter(axisIterator, nodeTest);
- }
- return axisIterator;
+ return filter(iterateAxis(axisNumber), nodeTest);
+ }
+
+ protected static AxisIterator filter(AxisIterator axisIterator, NodeTest nodeTest) {
+ return nodeTest != null ? new AxisFilter(axisIterator, nodeTest) : axisIterator;
}
/**
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java
index 9a0e23baf1..d22adb33ab 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeAxisIterator.java
@@ -42,7 +42,7 @@ public class AttributeAxisIterator extends Navigator.BaseEnumeration {
public void advance() {
if (this.iterator.hasNext()) {
Attribute attribute = this.iterator.next();
- super.current = new AttributeNode(attribute, super.position());
+ super.current = new AttributeNode(startNodeInfo, attribute, super.position());
} else {
super.current = null;
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java
index 528b4fc578..7a919967ef 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/AttributeNode.java
@@ -8,6 +8,7 @@ import java.util.List;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
import net.sf.saxon.om.NodeInfo;
@@ -23,7 +24,8 @@ import net.sf.saxon.value.Value;
*/
@Deprecated
@InternalApi
-public class AttributeNode extends AbstractNodeInfo {
+public class AttributeNode extends BaseNodeInfo {
+
protected final Attribute attribute;
protected final int id;
protected Value value;
@@ -32,30 +34,28 @@ public class AttributeNode extends AbstractNodeInfo {
/**
* Creates a new AttributeNode from a PMD Attribute.
*
- * @param id The index within the attribute order
+ * @param parent Parent elemtn
+ * @param id The index within the attribute order
*/
- public AttributeNode(Attribute attribute, int id) {
+ public AttributeNode(ElementNode parent, Attribute attribute, int id) {
+ super(Type.ATTRIBUTE, parent.getNamePool(), attribute.getName(), parent);
this.attribute = attribute;
this.id = id;
}
- @Override
- public int getNodeKind() {
- return Type.ATTRIBUTE;
- }
-
@Override
public String getLocalPart() {
return attribute.getName();
}
- @Override
- public String getURI() {
- return "";
+ private DeprecatedAttrLogger getAttrCtx() {
+ return parent == null ? DeprecatedAttrLogger.noop()
+ : parent.document.getAttrCtx();
}
@Override
public Value atomize() {
+ getAttrCtx().recordUsageOf(attribute);
if (value == null) {
Object data = attribute.getValue();
if (data instanceof List) {
@@ -79,6 +79,7 @@ public class AttributeNode extends AbstractNodeInfo {
@Override
public int compareOrder(NodeInfo other) {
+
return Integer.signum(this.id - ((AttributeNode) other).id);
}
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java
new file mode 100644
index 0000000000..ea6e4477ac
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/BaseNodeInfo.java
@@ -0,0 +1,76 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.ast.xpath.saxon;
+
+
+import net.sf.saxon.om.FingerprintedNode;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SiblingCountingNode;
+import net.sf.saxon.om.VirtualNode;
+
+abstract class BaseNodeInfo extends AbstractNodeInfo implements VirtualNode, SiblingCountingNode, FingerprintedNode {
+
+ // It's important that all our NodeInfo implementations share the
+ // same getNodeKind implementation, otherwise NameTest spends a lot
+ // of time in virtual dispatch
+ private final int nodeKind;
+ private final NamePool namePool;
+ private final int fingerprint;
+
+ protected final ElementNode parent;
+
+ BaseNodeInfo(int nodeKind, NamePool namePool, String localName, ElementNode parent) {
+ this.nodeKind = nodeKind;
+ this.namePool = namePool;
+ this.fingerprint = namePool.allocate("", "", localName) & NamePool.FP_MASK;
+ this.parent = parent;
+ }
+
+ @Override
+ public final String getURI() {
+ return "";
+ }
+
+ @Override
+ public final String getBaseURI() {
+ return "";
+ }
+
+ @Override
+ public String getPrefix() {
+ return "";
+ }
+
+ @Override
+ public final NodeInfo getParent() {
+ return parent;
+ }
+
+ @Override
+ public int getNameCode() {
+ // note: the nameCode is only equal to the fingerprint because
+ // this implementation does not use namespace prefixes
+ // if we change that (eg for embedded language support) then
+ // we'll need to worry about this. See NamePool.FP_MASK
+ return fingerprint;
+ }
+
+ @Override
+ public final int getFingerprint() {
+ return fingerprint;
+ }
+
+ @Override
+ public final NamePool getNamePool() {
+ return namePool;
+ }
+
+ @Override
+ public final int getNodeKind() {
+ return nodeKind;
+ }
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java
index f91718c816..d91f1828a6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/DocumentNode.java
@@ -10,10 +10,13 @@ import java.util.Map;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
+import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
import net.sf.saxon.om.Axis;
import net.sf.saxon.om.AxisIterator;
import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.Navigator;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.SingleNodeIterator;
@@ -24,7 +27,7 @@ import net.sf.saxon.type.Type;
*/
@Deprecated
@InternalApi
-public class DocumentNode extends AbstractNodeInfo implements DocumentInfo {
+public class DocumentNode extends BaseNodeInfo implements DocumentInfo {
/**
* The root ElementNode of the DocumentNode.
@@ -36,17 +39,25 @@ public class DocumentNode extends AbstractNodeInfo implements DocumentInfo {
*/
public final Map nodeToElementNode = new HashMap<>();
+ private DeprecatedAttrLogger attrCtx;
+
/**
* Construct a DocumentNode, with the given AST Node serving as the root
* ElementNode.
*
- * @param node
- * The root AST Node.
+ * @param node The root AST Node.
+ * @param namePool Pool to share names
*
* @see ElementNode
*/
+ public DocumentNode(Node node, NamePool namePool) {
+ super(Type.DOCUMENT, namePool, "", null);
+ this.rootNode = new ElementNode(this, new IdGenerator(), null, node, -1, namePool);
+ }
+
+ @Deprecated
public DocumentNode(Node node) {
- this.rootNode = new ElementNode(this, new IdGenerator(), null, node, -1);
+ this(node, SaxonXPathRuleQuery.getNamePool());
}
@Override
@@ -64,11 +75,6 @@ public class DocumentNode extends AbstractNodeInfo implements DocumentInfo {
throw createUnsupportedOperationException("DocumentInfo.selectID(String)");
}
- @Override
- public int getNodeKind() {
- return Type.DOCUMENT;
- }
-
@Override
public DocumentInfo getDocumentRoot() {
return this;
@@ -92,4 +98,12 @@ public class DocumentNode extends AbstractNodeInfo implements DocumentInfo {
return super.iterateAxis(axisNumber);
}
}
+
+ public DeprecatedAttrLogger getAttrCtx() {
+ return attrCtx == null ? DeprecatedAttrLogger.noop() : attrCtx;
+ }
+
+ public void setAttrCtx(DeprecatedAttrLogger attrCtx) {
+ this.attrCtx = attrCtx;
+ }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java
index 4663e7f1bf..6db157eac4 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/saxon/ElementNode.java
@@ -4,25 +4,41 @@
package net.sourceforge.pmd.lang.ast.xpath.saxon;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
import net.sf.saxon.om.Axis;
import net.sf.saxon.om.AxisIterator;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.om.EmptyIterator;
+import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.Navigator;
+import net.sf.saxon.om.Navigator.BaseEnumeration;
import net.sf.saxon.om.NodeArrayIterator;
import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.SingleNodeIterator;
+import net.sf.saxon.om.SingletonIterator;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+import net.sf.saxon.value.Value;
/**
* A Saxon OM Element type node for an AST Node.
*/
@Deprecated
@InternalApi
-public class ElementNode extends AbstractNodeInfo {
+public class ElementNode extends BaseNodeInfo {
protected final DocumentNode document;
protected final ElementNode parent;
@@ -31,17 +47,31 @@ public class ElementNode extends AbstractNodeInfo {
protected final int siblingPosition;
protected final NodeInfo[] children;
- public ElementNode(DocumentNode document, IdGenerator idGenerator, ElementNode parent, Node node,
- int siblingPosition) {
+ private Map attributes;
+
+ @Deprecated
+ public ElementNode(DocumentNode document, IdGenerator idGenerator, ElementNode parent, Node node, int siblingPosition) {
+ this(document, idGenerator, parent, node, siblingPosition, SaxonXPathRuleQuery.getNamePool());
+ }
+
+ public ElementNode(DocumentNode document,
+ IdGenerator idGenerator,
+ ElementNode parent,
+ Node node,
+ int siblingPosition,
+ NamePool namePool) {
+ super(Type.ELEMENT, namePool, node.getXPathNodeName(), parent);
+
this.document = document;
this.parent = parent;
this.node = node;
this.id = idGenerator.getNextId();
this.siblingPosition = siblingPosition;
+
if (node.getNumChildren() > 0) {
this.children = new NodeInfo[node.getNumChildren()];
for (int i = 0; i < children.length; i++) {
- children[i] = new ElementNode(document, idGenerator, this, node.getChild(i), i);
+ children[i] = new ElementNode(document, idGenerator, this, node.getChild(i), i, namePool);
}
} else {
this.children = null;
@@ -49,6 +79,20 @@ public class ElementNode extends AbstractNodeInfo {
document.nodeToElementNode.put(node, this);
}
+ private Map getAttributes() {
+ if (attributes == null) {
+ attributes = new HashMap<>();
+ Iterator iter = node.getXPathAttributesIterator();
+ int idx = 0;
+ while (iter.hasNext()) {
+ Attribute next = iter.next();
+ AttributeNode attrNode = new AttributeNode(this, next, idx++);
+ attributes.put(attrNode.getFingerprint(), attrNode);
+ }
+ }
+ return attributes;
+ }
+
@Override
public Object getUnderlyingNode() {
return node;
@@ -74,11 +118,6 @@ public class ElementNode extends AbstractNodeInfo {
return children != null;
}
- @Override
- public int getNodeKind() {
- return Type.ELEMENT;
- }
-
@Override
public DocumentInfo getDocumentRoot() {
return document;
@@ -89,14 +128,26 @@ public class ElementNode extends AbstractNodeInfo {
return node.getXPathNodeName();
}
+
@Override
- public String getURI() {
- return "";
+ public SequenceIterator getTypedValue() {
+ return SingletonIterator.makeIterator((AtomicValue) atomize());
}
@Override
- public NodeInfo getParent() {
- return parent;
+ public Value atomize() {
+ switch (getNodeKind()) {
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return new StringValue(getStringValueCS());
+ default:
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+ }
+
+ @Override
+ public CharSequence getStringValueCS() {
+ return "";
}
@Override
@@ -122,16 +173,40 @@ public class ElementNode extends AbstractNodeInfo {
return result;
}
+
+ @Override
+ public String getDisplayName() {
+ return getLocalPart();
+ }
+
+
+ @Override
+ public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
+ if (axisNumber == Axis.ATTRIBUTE) {
+ if (nodeTest instanceof NameTest) {
+ if ((nodeTest.getNodeKindMask() & (1 << Type.ATTRIBUTE)) == 0) {
+ return EmptyIterator.getInstance();
+ } else {
+ int fp = nodeTest.getFingerprint();
+ if (fp != -1) {
+ return SingleNodeIterator.makeIterator(getAttributes().get(fp));
+ }
+ }
+ }
+ }
+ return super.iterateAxis(axisNumber, nodeTest);
+ }
+
@SuppressWarnings("PMD.MissingBreakInSwitch")
@Override
- public AxisIterator iterateAxis(byte axisNumber) {
+ public AxisIterator iterateAxis(final byte axisNumber) {
switch (axisNumber) {
case Axis.ANCESTOR:
return new Navigator.AncestorEnumeration(this, false);
case Axis.ANCESTOR_OR_SELF:
return new Navigator.AncestorEnumeration(this, true);
case Axis.ATTRIBUTE:
- return new AttributeAxisIterator(this);
+ return new AttributeEnumeration();
case Axis.CHILD:
if (children == null) {
return EmptyIterator.getInstance();
@@ -171,4 +246,22 @@ public class ElementNode extends AbstractNodeInfo {
}
}
+ private class AttributeEnumeration extends BaseEnumeration {
+
+ private final Iterator iter = getAttributes().values().iterator();
+
+ @Override
+ public void advance() {
+ if (iter.hasNext()) {
+ current = iter.next();
+ } else {
+ current = null;
+ }
+ }
+
+ @Override
+ public SequenceIterator getAnother() {
+ return new AttributeEnumeration();
+ }
+ }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/ImmutableLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/ImmutableLanguage.java
index b71e01d24d..73aeeb4265 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/ImmutableLanguage.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/ImmutableLanguage.java
@@ -8,6 +8,10 @@ package net.sourceforge.pmd.lang.rule;
* This is a tag interface to indicate that a Rule implementation class does not
* support changes to it's Language. The Language is integral to the proper
* functioning of the Rule.
+ *
+ * @deprecated No rule supports a change to their language. This will
+ * be made the default behaviour with PMD 7.0.0.
*/
+@Deprecated
public interface ImmutableLanguage {
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
index 0cfad63c71..5318030a02 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
@@ -20,7 +20,12 @@ import net.sourceforge.pmd.properties.PropertyFactory;
* functional Rule is not needed. For example, during unit testing, or as an
* editable surrogate used by IDE plugins. The Language of this Rule defaults to
* Java.
+ *
+ * @deprecated This is not a supported API. You need the pmd-test module
+ * on your classpath, or pmd-core's test sources. This will be removed
+ * in 7.0.0
*/
+@Deprecated
public class MockRule extends AbstractRule {
public MockRule() {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
index 773d64a5c0..0f91715bcc 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
@@ -12,25 +12,30 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
+import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.rule.xpath.JaxenXPathRuleQuery;
import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
+import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
import net.sourceforge.pmd.properties.EnumeratedProperty;
import net.sourceforge.pmd.properties.StringProperty;
/**
* Rule that tries to match an XPath expression against a DOM view of an AST.
- *
- * This rule needs a "xpath" property value in order to function.
*/
public class XPathRule extends AbstractRule {
- // TODO 7.0.0 use PropertyDescriptor
+ /**
+ * @deprecated Use {@link #XPathRule(XPathVersion, String)}
+ */
+ @Deprecated
public static final StringProperty XPATH_DESCRIPTOR = StringProperty.named("xpath")
.desc("XPath expression")
.defaultValue("")
@@ -47,7 +52,11 @@ public class XPathRule extends AbstractRule {
XPATH_VERSIONS = Collections.unmodifiableMap(tmp);
}
- // published, can't be converted
+
+ /**
+ * @deprecated Use {@link #XPathRule(XPathVersion, String)}
+ */
+ @Deprecated
public static final EnumeratedProperty VERSION_DESCRIPTOR = EnumeratedProperty.named("version")
.desc("XPath specification version")
.mappings(XPATH_VERSIONS)
@@ -61,8 +70,13 @@ public class XPathRule extends AbstractRule {
*/
private XPathRuleQuery xpathRuleQuery;
+ // this is shared with rules forked by deepCopy, used by the XPathRuleQuery
+ private DeprecatedAttrLogger attrLogger = DeprecatedAttrLogger.create(this);
+
/**
* Creates a new XPathRule without the corresponding XPath query.
+ *
+ * @deprecated Use {@link #XPathRule(XPathVersion, String)}
*/
public XPathRule() {
definePropertyDescriptor(XPATH_DESCRIPTOR);
@@ -73,6 +87,8 @@ public class XPathRule extends AbstractRule {
/**
* Creates a new XPathRule and associates the XPath query.
+ *
+ * @deprecated Use {@link #XPathRule(XPathVersion, String)}
*/
public XPathRule(final String xPath) {
this();
@@ -80,21 +96,62 @@ public class XPathRule extends AbstractRule {
}
/**
- * Sets the XPath to query against the desired nodes in {@link #apply(List, RuleContext)}.
+ * Make a new XPath rule with the given version + expression
*
- * @param xPath the XPath query
+ * @param version Version of the XPath language
+ * @param expression XPath expression
+ *
+ * @throws NullPointerException If any of the arguments is null
*/
+ public XPathRule(XPathVersion version, String expression) {
+ this();
+ Objects.requireNonNull(version, "XPath version is null");
+ Objects.requireNonNull(expression, "XPath expression is null");
+ setXPath(expression);
+ setVersion(version.getXmlName());
+ }
+
+
+ @Override
+ public Rule deepCopy() {
+ XPathRule rule = (XPathRule) super.deepCopy();
+ rule.attrLogger = this.attrLogger;
+ return rule;
+ }
+
+ /**
+ * Returns the version for this rule. Returns null if this is not
+ * set or invalid.
+ */
+ public XPathVersion getVersion() {
+ return XPathVersion.ofId(getProperty(VERSION_DESCRIPTOR));
+ }
+
+ /**
+ * Returns the XPath expression that implements this rule.
+ */
+ public String getXPathExpression() {
+ return getProperty(XPATH_DESCRIPTOR);
+ }
+
+ /**
+ * @deprecated Use the constructor {@link #XPathRule(XPathVersion, String)},
+ * don't set the expression after the fact.
+ */
+ @Deprecated
public void setXPath(final String xPath) {
setProperty(XPathRule.XPATH_DESCRIPTOR, xPath);
}
+ /**
+ * @deprecated Use the constructor {@link #XPathRule(XPathVersion, String)},
+ * don't set the version after the fact.
+ */
+ @Deprecated
public void setVersion(final String version) {
setProperty(XPathRule.VERSION_DESCRIPTOR, version);
}
- /**
- * Apply the rule to all nodes.
- */
@Override
public void apply(List extends Node> nodes, RuleContext ctx) {
for (Node node : nodes) {
@@ -107,7 +164,10 @@ public class XPathRule extends AbstractRule {
*
* @param node The Node that to be checked.
* @param data The RuleContext.
+ *
+ * @deprecated Use {@link #apply(List, RuleContext)}
*/
+ @Deprecated
public void evaluate(final Node node, final RuleContext data) {
if (xPathRuleQueryNeedsInitialization()) {
initXPathRuleQuery();
@@ -124,13 +184,21 @@ public class XPathRule extends AbstractRule {
* engine in which the query will be run it looks at the XPath version.
*/
private void initXPathRuleQuery() {
- String xpath = getProperty(XPATH_DESCRIPTOR);
- String version = getProperty(VERSION_DESCRIPTOR);
+ String xpath = getXPathExpression();
+ XPathVersion version = getVersion();
- initRuleQueryBasedOnVersion(version);
+ if (version == null) {
+ throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason");
+ }
+
+ if (version == XPathVersion.XPATH_1_0) {
+ xpathRuleQuery = new JaxenXPathRuleQuery(attrLogger);
+ } else {
+ xpathRuleQuery = new SaxonXPathRuleQuery(attrLogger);
+ }
xpathRuleQuery.setXPath(xpath);
- xpathRuleQuery.setVersion(version);
+ xpathRuleQuery.setVersion(version.getXmlName());
xpathRuleQuery.setProperties(getPropertiesByPropertyDescriptor());
}
@@ -143,10 +211,6 @@ public class XPathRule extends AbstractRule {
return xpathRuleQuery == null;
}
- private void initRuleQueryBasedOnVersion(final String version) {
- xpathRuleQuery = XPATH_1_0.equals(version) ? new JaxenXPathRuleQuery() : new SaxonXPathRuleQuery();
- }
-
@Override
public List getRuleChainVisits() {
if (xPathRuleQueryNeedsInitialization()) {
@@ -161,10 +225,11 @@ public class XPathRule extends AbstractRule {
@Override
public String dysfunctionReason() {
- return hasXPathExpression() ? null : "Missing xPath expression";
- }
-
- private boolean hasXPathExpression() {
- return StringUtils.isNotBlank(getProperty(XPATH_DESCRIPTOR));
+ if (getVersion() == null) {
+ return "Invalid XPath version '" + getProperty(VERSION_DESCRIPTOR) + "'";
+ } else if (StringUtils.isBlank(getXPathExpression())) {
+ return "Missing XPath expression";
+ }
+ return null;
}
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java
index 69c6d0afdf..5647fb9ec8 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/AbstractXPathRuleQuery.java
@@ -9,12 +9,17 @@ import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.properties.PropertyDescriptor;
/**
* This implementation of XPathRuleQuery provides support for RuleChain visits.
+ *
+ * @deprecated Internal API
*/
+@Deprecated
+@InternalApi
public abstract class AbstractXPathRuleQuery implements XPathRuleQuery {
/**
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java
index 2ecd213c9a..4ef992f19d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQuery.java
@@ -31,25 +31,38 @@ import org.jaxen.expr.XPathFactory;
import org.jaxen.saxpath.Axis;
import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.internal.ContextualizedNavigator;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.properties.PropertyDescriptor;
/**
* This is a Jaxen based XPathRule query.
+ *
+ * @deprecated Internal API
*/
+@Deprecated
+@InternalApi
public class JaxenXPathRuleQuery extends AbstractXPathRuleQuery {
private static final Logger LOG = Logger.getLogger(JaxenXPathRuleQuery.class.getName());
- private enum InitializationStatus {
- NONE, PARTIAL, FULL
- }
+ static final String AST_ROOT = "_AST_ROOT_";
private InitializationStatus initializationStatus = InitializationStatus.NONE;
// Mapping from Node name to applicable XPath queries
Map> nodeNameToXPaths;
- static final String AST_ROOT = "_AST_ROOT_";
+ private final DeprecatedAttrLogger attrCtx;
+
+ public JaxenXPathRuleQuery() {
+ this(DeprecatedAttrLogger.noop());
+ }
+
+ public JaxenXPathRuleQuery(DeprecatedAttrLogger attrCtx) {
+ this.attrCtx = attrCtx;
+ }
@Override
public boolean isSupportedVersion(String version) {
@@ -61,7 +74,7 @@ public class JaxenXPathRuleQuery extends AbstractXPathRuleQuery {
final List results = new ArrayList<>();
try {
- initializeExpressionIfStatusIsNoneOrPartial(data.getLanguageVersion().getLanguageVersionHandler().getXPathHandler().getNavigator());
+ initializeExpressionIfStatusIsNoneOrPartial(new ContextualizedNavigator(attrCtx));
List xPaths = getXPathsForNodeOrDefault(node.getXPathNodeName());
for (XPath xpath : xPaths) {
@@ -261,4 +274,9 @@ public class JaxenXPathRuleQuery extends AbstractXPathRuleQuery {
}
return xpath;
}
+
+
+ private enum InitializationStatus {
+ NONE, PARTIAL, FULL
+ }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
index 3f4178a754..4bcb8f7423 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
@@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.rule.xpath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -16,15 +15,21 @@ import java.util.logging.Logger;
import java.util.regex.Pattern;
import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode;
import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode;
import net.sourceforge.pmd.lang.rule.xpath.internal.RuleChainAnalyzer;
import net.sourceforge.pmd.lang.xpath.Initializer;
import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.util.DataMap;
+import net.sourceforge.pmd.util.DataMap.DataKey;
+import net.sourceforge.pmd.util.DataMap.SimpleDataKey;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.NamespaceConstant;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.ValueRepresentation;
@@ -50,8 +55,13 @@ import net.sf.saxon.value.Value;
/**
* This is a Saxon based XPathRule query.
+ *
+ * @deprecated Internal API
*/
+@Deprecated
+@InternalApi
public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
+
/**
* Special nodeName that references the root expression.
*/
@@ -59,15 +69,10 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
private static final Logger LOG = Logger.getLogger(SaxonXPathRuleQuery.class.getName());
- private static final int MAX_CACHE_SIZE = 20;
- private static final Map CACHE = new LinkedHashMap(MAX_CACHE_SIZE) {
- private static final long serialVersionUID = -7653916493967142443L;
+ private static final NamePool NAME_POOL = new NamePool();
- @Override
- protected boolean removeEldestEntry(final Map.Entry eldest) {
- return size() > MAX_CACHE_SIZE;
- }
- };
+ /** Cache key for the wrapped tree for saxon. */
+ private static final SimpleDataKey SAXON_TREE_CACHE_KEY = DataMap.simpleDataKey("saxon.tree");
/**
* Contains for each nodeName a sub expression, used for implementing rule chain.
@@ -86,21 +91,33 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
*/
private List xpathVariables;
+ private final DeprecatedAttrLogger attrCtx;
+
+ @Deprecated
+ public SaxonXPathRuleQuery() {
+ this(DeprecatedAttrLogger.noop());
+ }
+
+ public SaxonXPathRuleQuery(DeprecatedAttrLogger attrCtx) {
+ this.attrCtx = attrCtx;
+ }
+
@Override
public boolean isSupportedVersion(String version) {
return XPATH_1_0_COMPATIBILITY.equals(version) || XPATH_2_0.equals(version);
}
@Override
- @SuppressWarnings("unchecked")
public List evaluate(final Node node, final RuleContext data) {
initializeXPathExpression();
try {
final DocumentNode documentNode = getDocumentNodeForRootNode(node);
+ documentNode.setAttrCtx(attrCtx); //
// Map AST Node -> Saxon Node
final ElementNode rootElementNode = documentNode.nodeToElementNode.get(node);
+ assert rootElementNode != null : "Cannot find " + node;
final XPathDynamicContext xpathDynamicContext = createDynamicContext(rootElementNode);
final List nodes = new LinkedList<>();
@@ -182,15 +199,13 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
private DocumentNode getDocumentNodeForRootNode(final Node node) {
final Node root = getRootNode(node);
- DocumentNode documentNode;
- synchronized (CACHE) {
- documentNode = CACHE.get(root);
- if (documentNode == null) {
- documentNode = new DocumentNode(root);
- CACHE.put(root, documentNode);
- }
+ DataMap> userMap = root.getUserMap();
+ DocumentNode docNode = userMap.get(SAXON_TREE_CACHE_KEY);
+ if (docNode == null) {
+ docNode = new DocumentNode(root, getNamePool());
+ userMap.set(SAXON_TREE_CACHE_KEY, docNode);
}
- return documentNode;
+ return docNode;
}
/**
@@ -224,13 +239,14 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
try {
final XPathEvaluator xpathEvaluator = new XPathEvaluator();
final XPathStaticContext xpathStaticContext = xpathEvaluator.getStaticContext();
+ xpathStaticContext.getConfiguration().setNamePool(getNamePool());
// Enable XPath 1.0 compatibility
if (XPATH_1_0_COMPATIBILITY.equals(version)) {
((AbstractStaticContext) xpathStaticContext).setBackwardsCompatibilityMode(true);
}
- ((IndependentContext) xpathEvaluator.getStaticContext()).declareNamespace("fn", NamespaceConstant.FN);
+ ((IndependentContext) xpathStaticContext).declareNamespace("fn", NamespaceConstant.FN);
// Register PMD functions
Initializer.initialize((IndependentContext) xpathStaticContext);
@@ -349,4 +365,8 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
initializeXPathExpression();
return super.getRuleChainVisits();
}
+
+ public static NamePool getNamePool() {
+ return NAME_POOL;
+ }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRuleQuery.java
index dedd90deab..cd1d36c32f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRuleQuery.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRuleQuery.java
@@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@@ -22,24 +23,38 @@ import net.sourceforge.pmd.properties.PropertyDescriptor;
* are recommended to manage internal state that is invariant over AST Nodes in
* a fashion which facilities high performance (e.g. caching).
*
+ *
+ * @deprecated This will be internalized in 7.0.0.
*/
+@InternalApi
+@Deprecated
public interface XPathRuleQuery {
/**
* XPath 1.0 version.
+ *
+ * @deprecated Use {@link XPathVersion}
*/
+ @Deprecated
String XPATH_1_0 = "1.0";
/**
* XPath 1.0 compatibility version.
+ *
+ * @deprecated Use {@link XPathVersion}
*/
+ @Deprecated
String XPATH_1_0_COMPATIBILITY = "1.0 compatibility";
/**
* XPath 2.0 version.
+ *
+ * @deprecated Use {@link XPathVersion}
*/
+ @Deprecated
String XPATH_2_0 = "2.0";
+
/**
* Set the XPath query string to be used.
*
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathVersion.java
new file mode 100644
index 0000000000..7177afa443
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathVersion.java
@@ -0,0 +1,70 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.rule.xpath;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Constants for XPath language version used in XPath queries.
+ */
+public enum XPathVersion {
+ /**
+ * XPath 1.0.
+ *
+ * @deprecated Will become unsupported in 7.0.0
+ */
+ @Deprecated
+ XPATH_1_0(XPathRuleQuery.XPATH_1_0),
+
+ /**
+ * XPath 1.0 compatibility mode.
+ *
+ * @deprecated Will become unsupported in 7.0.0
+ */
+ @Deprecated
+ XPATH_1_0_COMPATIBILITY(XPathRuleQuery.XPATH_1_0_COMPATIBILITY),
+
+ /** XPath 2.0. */
+ XPATH_2_0(XPathRuleQuery.XPATH_2_0);
+
+ private static final Map BY_NAME = new HashMap<>();
+ private final String version;
+
+
+ static {
+ for (XPathVersion value : values()) {
+ BY_NAME.put(value.getXmlName(), value);
+ }
+ }
+
+
+ XPathVersion(String version) {
+ this.version = version;
+ }
+
+
+ /**
+ * Returns the string used to represent the version in the XML.
+ *
+ * @return A string representation
+ */
+ public String getXmlName() {
+ return version;
+ }
+
+
+ /**
+ * Gets an XPath version from the string used to represent
+ * it in the XML.
+ *
+ * @param version A version string
+ *
+ * @return An XPath version, or null if the argument is not a valid version
+ */
+ public static XPathVersion ofId(String version) {
+ return BY_NAME.get(version);
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java
index 4cc5645b92..0463368e8a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java
@@ -33,7 +33,7 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
public static final int REMEDIATION_POINTS_DEFAULT = 50000;
public static final String[] CODECLIMATE_DEFAULT_CATEGORIES = new String[] {"Style"};
- // Note: required by https://github.com/codeclimate/spec/blob/master/SPEC.md
+ // Note: required by https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md
protected static final String NULL_CHARACTER = "\u0000";
protected static final List INTERNAL_DEV_PROPERTIES = Arrays.asList("version", "xpath");
private static final String PMD_PROPERTIES_URL = getPmdPropertiesURL();
@@ -146,9 +146,9 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
private String getBody() {
String result = "## " + rule.getName() + "\\n\\n" + "Since: PMD " + rule.getSince() + "\\n\\n" + "Priority: "
+ rule.getPriority() + "\\n\\n"
- + "[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): "
+ + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): "
+ Arrays.toString(getCategories()).replaceAll("[\\[\\]]", "") + "\\n\\n"
- + "[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): "
+ + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): "
+ getRemediationPoints() + "\\n\\n" + cleaned(rule.getDescription());
if (!rule.getExamples().isEmpty()) {
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java
index b5ece64c46..d23db81a28 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java
@@ -1,4 +1,4 @@
-/**
+/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
@@ -228,7 +228,6 @@ public class AbstractNodeTest {
assertEquals(0, grandChild.getNumChildren());
}
-
@Test
public void testDeprecatedAttributeXPathQuery() throws JaxenException {
class MyRootNode extends DummyNode implements RootNode {
@@ -238,7 +237,8 @@ public class AbstractNodeTest {
}
}
- addChild(new MyRootNode(nextId()), new DummyNodeWithDeprecatedAttribute(2)).findChildNodesWithXPath("//dummyNode[@Size=1]");
+ Node root = addChild(new MyRootNode(nextId()), new DummyNodeWithDeprecatedAttribute(2));
+ root.findChildNodesWithXPath("//dummyNode[@Size=1]");
String log = loggingRule.getLog();
@@ -247,5 +247,4 @@ public class AbstractNodeTest {
assertTrue(log.contains("dummyNode/@Size"));
}
-
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java
index 8cfa55925e..10ddb52a1d 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java
@@ -26,6 +26,21 @@ public class DummyNode extends AbstractNode {
this.xpathName = xpathName;
}
+ public void setBeginColumn(int i) {
+ beginColumn = i;
+ }
+
+ public void setBeginLine(int i) {
+ beginLine = i;
+ }
+
+ public void setCoords(int bline, int bcol, int eline, int ecol) {
+ beginLine = bline;
+ beginColumn = bcol;
+ endLine = eline;
+ endColumn = ecol;
+ }
+
@Override
public String toString() {
return xpathName;
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java
index 7deca9984b..0e5dd20606 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java
@@ -25,7 +25,7 @@ public class DummyNodeWithDeprecatedAttribute extends DummyNode {
// this is a attribute that is deprecated for xpath, because it will be removed.
// it should still be available via Java.
- @DeprecatedAttribute
+ @DeprecatedAttribute(replaceWith = "@Image")
public String getName() {
return "foo";
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java
index 1d9e274921..a1dda2cb58 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java
@@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.ast.xpath;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -16,15 +15,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.hamcrest.Matchers;
-import org.hamcrest.collection.IsMapContaining;
import org.junit.Assert;
-import org.junit.Rule;
import org.junit.Test;
-import net.sourceforge.pmd.junit.JavaUtilLoggingRule;
import net.sourceforge.pmd.lang.ast.DummyNode;
-import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute;
import net.sourceforge.pmd.lang.ast.Node;
@@ -33,32 +27,6 @@ import net.sourceforge.pmd.lang.ast.Node;
*/
public class AttributeAxisIteratorTest {
- @Rule
- public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(Attribute.class.getName());
-
- /**
- * Verifies that attributes are returned, even if they are deprecated.
- * Deprecated attributes are still accessible, but a warning is logged, when
- * the value is used.
- */
- @Test
- public void testAttributeDeprecation() {
- // make sure, we log
- Attribute.DETECTED_DEPRECATED_ATTRIBUTES.clear();
-
- Node dummy = new DummyNodeWithDeprecatedAttribute(2);
- Map attributes = toMap(new AttributeAxisIterator(dummy));
- assertThat(attributes, IsMapContaining.hasKey("Size"));
- assertThat(attributes, IsMapContaining.hasKey("Name"));
-
- assertThat(attributes.get("Size").getStringValue(), Matchers.is("2"));
- assertThat(attributes.get("Name").getStringValue(), Matchers.is("foo"));
-
- String log = loggingRule.getLog();
- assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Size' in XPath query"));
- assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Name' in XPath query"));
- }
-
/**
* Test hasNext and next.
*/
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java
index e9bc85a985..07605202d5 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java
@@ -4,18 +4,37 @@
package net.sourceforge.pmd.lang.rule;
+import static java.util.Collections.singletonList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import org.hamcrest.Matchers;
import org.junit.Assert;
+import org.junit.Rule;
import org.junit.Test;
+import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.junit.JavaUtilLoggingRule;
+import net.sourceforge.pmd.lang.DummyLanguageModule;
+import net.sourceforge.pmd.lang.LanguageRegistry;
+import net.sourceforge.pmd.lang.ast.DummyNode;
+import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute;
+import net.sourceforge.pmd.lang.ast.xpath.Attribute;
+import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
+
public class XPathRuleTest {
+ @Rule
+ public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(Attribute.class.getName());
+
/**
* It's easy to forget the attribute "typeResolution=true" when
* defining XPath rules in xml. Therefore we by default enable
* typeresolution. For Java rules, type resolution was enabled by
* default long time ago.
*
- * @see #2048 [core] Enable type resolution by default for XPath rules
+ * @see #2048 [core] Enable type resolution by default for XPath
+ * rules
*/
@Test
public void typeResolutionShouldBeEnabledByDefault() {
@@ -26,4 +45,75 @@ public class XPathRuleTest {
Assert.assertTrue(rule2.isTypeResolution());
}
+
+ @Test
+ public void testAttributeDeprecation10() {
+ testDeprecation(XPathVersion.XPATH_1_0);
+ }
+
+ @Test
+ public void testAttributeDeprecation20() {
+ testDeprecation(XPathVersion.XPATH_2_0);
+ }
+
+ public void testDeprecation(XPathVersion version) {
+ XPathRule xpr = makeRule(version, "SomeRule");
+
+ loggingRule.clear();
+
+ RuleContext ctx = new RuleContext();
+ ctx.setLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getDefaultVersion());
+ DummyNode firstNode = newNode();
+ eval(ctx, xpr, firstNode);
+ assertEquals(1, ctx.getReport().size());
+
+ String log = loggingRule.getLog();
+ assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Size' by XPath rule 'SomeRule'"));
+ assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Name' by XPath rule 'SomeRule', please use @Image instead"));
+
+
+ loggingRule.clear();
+
+ eval(ctx, xpr, newNode()); // with another node
+ assertEquals(2, ctx.getReport().size());
+
+ assertEquals("", loggingRule.getLog()); // no additional warnings
+
+
+ // with another rule forked from the same one (in multithreaded processor)
+ eval(ctx, xpr.deepCopy(), newNode());
+ assertEquals(3, ctx.getReport().size());
+
+ assertEquals("", loggingRule.getLog()); // no additional warnings
+
+ // with another rule on the same node, new warnings
+ XPathRule otherRule = makeRule(version, "OtherRule");
+ otherRule.setRuleSetName("rset.xml");
+ eval(ctx, otherRule, firstNode);
+ assertEquals(4, ctx.getReport().size());
+
+ log = loggingRule.getLog();
+ assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Size' by XPath rule 'OtherRule' (in ruleset 'rset.xml')"));
+ assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Name' by XPath rule 'OtherRule' (in ruleset 'rset.xml'), please use @Image instead"));
+
+ }
+
+ public XPathRule makeRule(XPathVersion version, String name) {
+ XPathRule xpr = new XPathRule(version, "//dummyNode[@Size >= 2 and @Name='foo']");
+ xpr.setName(name);
+ xpr.setMessage("gotcha");
+ return xpr;
+ }
+
+ public void eval(RuleContext ctx, net.sourceforge.pmd.Rule rule, DummyNode node) {
+ rule.apply(singletonList(node), ctx);
+ }
+
+ public DummyNode newNode() {
+ DummyNode dummy = new DummyNodeWithDeprecatedAttribute(2);
+ dummy.setCoords(1, 1, 1, 2);
+ return dummy;
+ }
+
+
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
index 28ca250b06..c81b425668 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
@@ -30,8 +30,8 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
public String getExpected() {
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n"
- + "[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n"
- + "[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\n"
+ + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n"
+ + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n"
+ "desc\\n\\n"
+ "### [PMD properties](https://pmd.github.io/latest/pmd_devdocs_working_with_properties.html)\\n\\n"
+ "Name | Value | Description\\n" + "--- | --- | ---\\n"
@@ -45,8 +45,8 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
public String getExpectedWithProperties() {
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n"
- + "[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n"
- + "[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\n"
+ + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n"
+ + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n"
+ "desc\\n\\n"
+ "### [PMD properties](https://pmd.github.io/latest/pmd_devdocs_working_with_properties.html)\\n\\n"
+ "Name | Value | Description\\n" + "--- | --- | ---\\n"
@@ -67,8 +67,8 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
public String getExpectedMultiple() {
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n"
- + "[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n"
- + "[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\n"
+ + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n"
+ + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n"
+ "desc\\n\\n"
+ "### [PMD properties](https://pmd.github.io/latest/pmd_devdocs_working_with_properties.html)\\n\\n"
+ "Name | Value | Description\\n" + "--- | --- | ---\\n"
@@ -77,8 +77,8 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
+ "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}"
+ "\u0000" + PMD.EOL + "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n"
- + "[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n"
- + "[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\n"
+ + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n"
+ + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n"
+ "desc\\n\\n"
+ "### [PMD properties](https://pmd.github.io/latest/pmd_devdocs_working_with_properties.html)\\n\\n"
+ "Name | Value | Description\\n" + "--- | --- | ---\\n"
diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/xml/j2ee.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/xml/j2ee.xml
index f158927f95..17443c62ab 100644
--- a/pmd-core/src/test/resources/net/sourceforge/pmd/xml/j2ee.xml
+++ b/pmd-core/src/test/resources/net/sourceforge/pmd/xml/j2ee.xml
@@ -20,6 +20,7 @@
3
+
4
+
4
+
4
+
4
+
4
+
3
+
3
+
@@ -355,6 +363,7 @@ public class SomeEJB extends EJBObject implements EJBLocalHome {
3
+
3
+
3
+
-
\ No newline at end of file
+
diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java
index 1ab25d40a6..66d8c45a7e 100644
--- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java
+++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java
@@ -177,6 +177,12 @@ public class DeadLinksChecker {
} else {
linkOk = linkTarget.isEmpty() || htmlPages.contains(linkTarget);
}
+
+ // maybe a local file
+ if (!linkOk) {
+ Path localResource = docsDirectory.resolve(linkTarget);
+ linkOk = Files.exists(localResource);
+ }
}
if (!linkOk) {
diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
index 4d1ffa2dee..e0e6c2511f 100644
--- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
+++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
@@ -405,10 +405,11 @@ public class RuleDocGenerator {
lines.addAll(EscapeUtils.escapeLines(toLines(stripIndentation(rule.getDescription()))));
lines.add("");
- if (rule instanceof XPathRule || rule instanceof RuleReference && ((RuleReference) rule).getRule() instanceof XPathRule) {
+ XPathRule xpathRule = asXPathRule(rule);
+ if (xpathRule != null) {
lines.add("**This rule is defined by the following XPath expression:**");
lines.add("``` xpath");
- lines.addAll(toLines(StringUtils.stripToEmpty(rule.getProperty(XPathRule.XPATH_DESCRIPTOR))));
+ lines.addAll(toLines(StringUtils.stripToEmpty(xpathRule.getXPathExpression())));
lines.add("```");
} else {
lines.add("**This rule is defined by the following Java class:** "
@@ -502,6 +503,15 @@ public class RuleDocGenerator {
}
}
+ private XPathRule asXPathRule(Rule rule) {
+ if (rule instanceof XPathRule) {
+ return (XPathRule) rule;
+ } else if (rule instanceof RuleReference && ((RuleReference) rule).getRule() instanceof XPathRule) {
+ return (XPathRule) ((RuleReference) rule).getRule();
+ }
+ return null;
+ }
+
private static boolean isDeprecated(PropertyDescriptor> propertyDescriptor) {
return propertyDescriptor.description() != null
&& propertyDescriptor.description().toLowerCase(Locale.ROOT).startsWith(DEPRECATED_RULE_PROPERTY_MARKER);
diff --git a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml
index 44e2bb76fb..78591da860 100644
--- a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml
+++ b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml
@@ -57,6 +57,7 @@ Here might be <script>alert('XSS');</script> as well. And "quotes".
3
+
3
+
3
+
3
+
0;
}
+ /**
+ * @deprecated Use {@link #getName()}
+ * @return
+ */
+ @Override
+ @DeprecatedAttribute(replaceWith = "@Name")
+ @Deprecated
+ public String getImage() {
+ return getName();
+ }
+
+ /** Returns the name of the variable. */
+ public String getName() {
+ return super.getImage();
+ }
/**
* Returns true if the declared variable has an array type.
@@ -161,9 +177,13 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements Dim
/**
* Returns the name of the variable.
+ *
+ * @deprecated Use {@link #getName()}
*/
+ @Deprecated
+ @DeprecatedAttribute(replaceWith = "@Name")
public String getVariableName() {
- return getImage();
+ return getName();
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java
index fbdacd93bc..ed9076c669 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java
@@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute;
import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName;
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
@@ -37,10 +38,14 @@ public abstract class AbstractAnyTypeDeclaration extends AbstractJavaAccessTypeN
|| getParent() instanceof ASTRecordBody;
}
- @Override
+ /**
+ * @deprecated Use {@link #getSimpleName()}
+ */
@Deprecated
+ @DeprecatedAttribute(replaceWith = "@SimpleName")
+ @Override
public String getImage() {
- return super.getImage();
+ return getSimpleName();
}
@Override
@@ -50,7 +55,17 @@ public abstract class AbstractAnyTypeDeclaration extends AbstractJavaAccessTypeN
@Override
public String getSimpleName() {
- return getImage();
+ return super.getImage();
+ }
+
+
+ /**
+ * Returns the record component list, or null if this is not a record
+ * declaration.
+ */
+ // @Nullable // TODO pull up to ASTAnyTypeDecl on 7.0.x
+ public ASTRecordComponentList getRecordComponents() {
+ return getFirstChildOfType(ASTRecordComponentList.class);
}
/**
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractClassWithoutAbstractMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractClassWithoutAbstractMethodRule.java
new file mode 100644
index 0000000000..3f20b559d5
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractClassWithoutAbstractMethodRule.java
@@ -0,0 +1,50 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.rule.bestpractices;
+
+import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration.DeclarationKind;
+import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTExtendsList;
+import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
+
+public class AbstractClassWithoutAbstractMethodRule extends AbstractJavaRule {
+
+ public AbstractClassWithoutAbstractMethodRule() {
+ addRuleChainVisit(ASTClassOrInterfaceDeclaration.class);
+ }
+
+ @Override
+ public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
+ if (!node.isAbstract() || doesExtend(node) || doesImplement(node)) {
+ return data;
+ }
+
+ int countOfAbstractMethods = 0;
+ for (ASTAnyTypeBodyDeclaration decl : node.getDeclarations()) {
+ if (decl.getKind() == DeclarationKind.METHOD) {
+ ASTMethodDeclaration methodDecl = (ASTMethodDeclaration) decl.getDeclarationNode();
+ if (methodDecl.isAbstract()) {
+ countOfAbstractMethods++;
+ }
+ }
+ }
+ if (countOfAbstractMethods == 0) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+
+ private boolean doesExtend(ASTClassOrInterfaceDeclaration node) {
+ return node.getFirstChildOfType(ASTExtendsList.class) != null;
+ }
+
+ private boolean doesImplement(ASTClassOrInterfaceDeclaration node) {
+ return node.getFirstChildOfType(ASTImplementsList.class) != null;
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java
new file mode 100644
index 0000000000..3b09462725
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java
@@ -0,0 +1,119 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.rule.bestpractices;
+
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
+import net.sourceforge.pmd.lang.java.ast.ASTArguments;
+import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
+import net.sourceforge.pmd.lang.java.ast.ASTName;
+import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
+import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
+import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
+import net.sourceforge.pmd.lang.java.ast.JavaNode;
+import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
+
+class AbstractPositionLiteralsFirstInComparisons extends AbstractJavaRule {
+
+ private final String equalsImage;
+
+ AbstractPositionLiteralsFirstInComparisons(String equalsImage) {
+ addRuleChainVisit(ASTPrimaryExpression.class);
+ this.equalsImage = equalsImage;
+ }
+
+ @Override
+ public Object visit(ASTPrimaryExpression node, Object data) {
+ ASTPrimaryPrefix primaryPrefix = node.getFirstChildOfType(ASTPrimaryPrefix.class);
+ ASTPrimarySuffix primarySuffix = node.getFirstChildOfType(ASTPrimarySuffix.class);
+ if (primaryPrefix != null && primarySuffix != null) {
+ ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class);
+ if (name == null || !name.getImage().endsWith(equalsImage)) {
+ return data;
+ }
+ if (!isSingleStringLiteralArgument(primarySuffix)) {
+ return data;
+ }
+ if (isWithinNullComparison(node)) {
+ return data;
+ }
+ addViolation(data, node);
+ }
+ return node;
+ }
+
+ private boolean isWithinNullComparison(ASTPrimaryExpression node) {
+ for (ASTExpression parentExpr : node.getParentsOfType(ASTExpression.class)) {
+ if (isComparisonWithNull(parentExpr, "==", ASTConditionalOrExpression.class)
+ || isComparisonWithNull(parentExpr, "!=", ASTConditionalAndExpression.class)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Expression/ConditionalAndExpression//EqualityExpression(@Image='!=']//NullLiteral
+ * Expression/ConditionalOrExpression//EqualityExpression(@Image='==']//NullLiteral
+ */
+ private boolean isComparisonWithNull(ASTExpression parentExpr, String equalOperator, Class extends JavaNode> condition) {
+ Node condExpr = null;
+ ASTEqualityExpression eqExpr = null;
+ if (parentExpr != null) {
+ condExpr = parentExpr.getFirstChildOfType(condition);
+ }
+ if (condExpr != null) {
+ eqExpr = condExpr.getFirstDescendantOfType(ASTEqualityExpression.class);
+ }
+ if (eqExpr != null) {
+ return eqExpr.hasImageEqualTo(equalOperator) && eqExpr.hasDescendantOfType(ASTNullLiteral.class);
+ }
+ return false;
+ }
+
+ /*
+ * This corresponds to the following XPath expression:
+ * (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()])
+ * and
+ * ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 )
+ */
+ private boolean isSingleStringLiteralArgument(ASTPrimarySuffix primarySuffix) {
+ if (!primarySuffix.isArguments() || primarySuffix.getArgumentCount() != 1) {
+ return false;
+ }
+ Node node = primarySuffix;
+ node = node.getFirstChildOfType(ASTArguments.class);
+ if (node != null) {
+ node = node.getFirstChildOfType(ASTArgumentList.class);
+ if (node.getNumChildren() != 1) {
+ return false;
+ }
+ }
+ if (node != null) {
+ node = node.getFirstChildOfType(ASTExpression.class);
+ }
+ if (node != null) {
+ node = node.getFirstChildOfType(ASTPrimaryExpression.class);
+ }
+ if (node != null) {
+ node = node.getFirstChildOfType(ASTPrimaryPrefix.class);
+ }
+ if (node != null) {
+ node = node.getFirstChildOfType(ASTLiteral.class);
+ }
+ if (node != null) {
+ ASTLiteral literal = (ASTLiteral) node;
+ if (literal.isStringLiteral()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopCanBeForeachRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopCanBeForeachRule.java
index fccfd94132..fb7873371d 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopCanBeForeachRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopCanBeForeachRule.java
@@ -229,7 +229,7 @@ public class ForLoopCanBeForeachRule extends AbstractJavaRule {
+ "/Name[matches(@Image,'\\w+\\.(size|length)')]"
+ "|"
+ "./RelationalExpression[@Image='<=']/AdditiveExpression[count(*)=2 and "
- + "@Image='-' and PrimaryExpression/PrimaryPrefix/Literal[@Image='1']]"
+ + "@Operator='-' and PrimaryExpression/PrimaryPrefix/Literal[@Image='1']]"
+ "/PrimaryExpression/PrimaryPrefix/Name[matches(@Image,'\\w+\\.(size|length)')]");
if (left.isEmpty()) {
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java
new file mode 100644
index 0000000000..1c84769c01
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java
@@ -0,0 +1,13 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.rule.bestpractices;
+
+public class PositionLiteralsFirstInCaseInsensitiveComparisonsRule extends AbstractPositionLiteralsFirstInComparisons {
+
+ public PositionLiteralsFirstInCaseInsensitiveComparisonsRule() {
+ super(".equalsIgnoreCase");
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java
new file mode 100644
index 0000000000..2b9edd12c4
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java
@@ -0,0 +1,13 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.rule.bestpractices;
+
+public class PositionLiteralsFirstInComparisonsRule extends AbstractPositionLiteralsFirstInComparisons {
+
+ public PositionLiteralsFirstInComparisonsRule() {
+ super(".equals");
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java
index b90c1804aa..efd4a782c5 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java
@@ -30,7 +30,7 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule interfaceRegex = defaultProp("interface").build();
private final PropertyDescriptor enumerationRegex = defaultProp("enum").build();
private final PropertyDescriptor annotationRegex = defaultProp("annotation").build();
- private final PropertyDescriptor utilityClassRegex = defaultProp("utility class").defaultValue("[A-Z][a-zA-Z0-9]+(Utils?|Helper)").build();
+ private final PropertyDescriptor utilityClassRegex = defaultProp("utility class").defaultValue("[A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants)").build();
public ClassNamingConventionsRule() {
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java
new file mode 100644
index 0000000000..8fd2249133
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java
@@ -0,0 +1,28 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.rule.errorprone;
+
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule;
+
+public class JUnitSpellingRule extends AbstractJUnitRule {
+
+ @Override
+ public Object visit(ASTMethodDeclaration node, Object data) {
+ if (node.getArity() != 0) {
+ return super.visit(node, data);
+ }
+
+ String name = node.getName();
+ if (!"setUp".equals(name) && "setup".equalsIgnoreCase(name)) {
+ addViolation(data, node);
+ }
+ if (!"tearDown".equals(name) && "teardown".equalsIgnoreCase(name)) {
+ addViolation(data, node);
+ }
+ return super.visit(node, data);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java
new file mode 100644
index 0000000000..de0f71ada6
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java
@@ -0,0 +1,24 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.rule.errorprone;
+
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule;
+
+public class JUnitStaticSuiteRule extends AbstractJUnitRule {
+
+ @Override
+ public Object visit(ASTMethodDeclaration node, Object data) {
+ if (node.getArity() != 0) {
+ return super.visit(node, data);
+ }
+ String name = node.getName();
+ if ("suite".equals(name) && (!node.isStatic() || !node.isPublic())) {
+ addViolation(data, node);
+ }
+ return super.visit(node, data);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java
new file mode 100644
index 0000000000..19262a5535
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java
@@ -0,0 +1,52 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.rule.errorprone;
+
+import java.util.List;
+
+import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTBlock;
+import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
+
+public class ProperCloneImplementationRule extends AbstractJavaRule {
+
+ public ProperCloneImplementationRule() {
+ addRuleChainVisit(ASTMethodDeclaration.class);
+ }
+
+ @Override
+ public Object visit(ASTMethodDeclaration node, Object data) {
+ if (!"clone".equals(node.getName()) || node.getArity() > 0) {
+ return data;
+ }
+
+ ASTBlock block = node.getFirstChildOfType(ASTBlock.class);
+ if (block == null) {
+ return data;
+ }
+
+ String enclosingClassName = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class).getSimpleName();
+ if (blockHasAllocations(block, enclosingClassName)) {
+ addViolation(data, node);
+ }
+
+ return data;
+ }
+
+ private boolean blockHasAllocations(ASTBlock block, String enclosingClassName) {
+ List allocations = block.findDescendantsOfType(ASTAllocationExpression.class);
+ for (ASTAllocationExpression alloc : allocations) {
+ ASTClassOrInterfaceType type = alloc.getFirstChildOfType(ASTClassOrInterfaceType.class);
+ if (type.hasImageEqualTo(enclosingClassName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml
index d15c60e627..3264cd40a3 100644
--- a/pmd-java/src/main/resources/category/java/bestpractices.xml
+++ b/pmd-java/src/main/resources/category/java/bestpractices.xml
@@ -13,7 +13,7 @@ Rules which enforce generally accepted best practices.
language="java"
since="3.0"
message="This abstract class does not have any abstract methods"
- class="net.sourceforge.pmd.lang.rule.XPathRule"
+ class="net.sourceforge.pmd.lang.java.rule.bestpractices.AbstractClassWithoutAbstractMethodRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#abstractclasswithoutabstractmethod">
The abstract class does not contain any abstract methods. An abstract class suggests
@@ -22,19 +22,6 @@ abstract methods. If the class is intended to be used as a base class only (not
directly) a protected constructor can be provided prevent direct instantiation.
3
-
-
-
-
-
-
-
3
+
3
+
3
+
@@ -422,12 +412,13 @@ By convention, the default label should be the last label in a switch statement.
3
+
@@ -541,6 +532,7 @@ the loop iterates over. By default this rule allows a regular 'for' loop with on
+
//ForInit/LocalVariableDeclaration[count(VariableDeclarator) > $maximumVariables]
@@ -587,12 +579,13 @@ through the @RunWith(Suite.class) annotation.
3
+
@@ -628,16 +621,17 @@ JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after
3
+
@@ -672,16 +666,17 @@ JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods bef
3
+
@@ -716,6 +711,7 @@ In JUnit 5, one of the following annotations should be used for tests: @Test, @R
3
+
3
+
4
+
Position literals first in comparisons, if the second argument is null then NullPointerExceptions
can be avoided, they will just return false.
3
-
-
-
-
-
-
-
Position literals first in comparisons, if the second argument is null then NullPointerExceptions
can be avoided, they will just return false.
3
-
-
-
-
-
-
-
3
+
3
+
//Type/ReferenceType/ClassOrInterfaceType[@Image='Hashtable']
@@ -1206,6 +1166,7 @@ Consider replacing Vector usages with the newer java.util.ArrayList if expensive
3
+
//Type/ReferenceType/ClassOrInterfaceType[@Image='Vector']
@@ -1267,6 +1228,7 @@ will (and by priority) and avoid clogging the Standard out log.
2
+
3
+
@@ -1445,7 +1407,7 @@ public class FooTest extends TestCase {
void testCode() {
Object a, b;
assertTrue(a.equals(b)); // bad usage
- assertEquals(?a should equals b?, a, b); // good usage
+ assertEquals("a should equals b", a, b); // good usage
}
}
]]>
@@ -1465,17 +1427,11 @@ more specific methods, like assertNull, assertNotNull.
3
+
@@ -1519,17 +1481,11 @@ by more specific methods, like assertSame, assertNotSame.
3
+
@@ -1569,6 +1531,7 @@ When asserting a value is the same as a literal or Boxed boolean, use assertTrue
3
+
3
+
@@ -112,11 +113,12 @@ If the goal is to avoid defining constants in a scope smaller than the class, th
3
+
4
+
@@ -210,12 +213,13 @@ Clarify your intent by using private or package access modifiers instead.
3
+
@@ -244,12 +248,13 @@ visibility cannot be reduced). Clarify your intent by using private or package a
3
+
@@ -276,6 +281,7 @@ and increases the maintenance burden.
2
+
//Name[starts-with(@Image,'System.loadLibrary')]
@@ -313,12 +319,13 @@ prefix for these methods.
4
+
3
+
0 ]
+//ClassOrInterfaceDeclaration[ExtendsList/*]
/ClassOrInterfaceBody
/ClassOrInterfaceBodyDeclaration
- /ConstructorDeclaration[ count (.//ExplicitConstructorInvocation)=0 ]
+ /ConstructorDeclaration[ not(.//ExplicitConstructorInvocation) ]
]]>
@@ -549,16 +557,17 @@ The rule allows methods and fields annotated with Guava's @VisibleForTesting.
3
+
@@ -620,23 +629,24 @@ usage by developers who should be implementing their own versions in the concret
1
+
@@ -666,6 +676,7 @@ public abstract class ShouldBeAbstract {
No need to explicitly extend Object.
4
+
3
+
3
+
//ForStatement[not(Statement/Block)]
@@ -860,13 +873,14 @@ Names for references to generic values should be limited to a single uppercase l
4
+
1
or
- string:upper-case(@Image) != @Image
+ upper-case(@Image) != @Image
]
]]>
@@ -943,11 +957,12 @@ by the rule {% rule java/codestyle/ControlStatementBraces %}.
3
+
@@ -985,6 +1000,7 @@ by the rule {% rule java/codestyle/ControlStatementBraces %}.
3
+
4
+
4
+
3
+
$minimum]
+//VariableDeclaratorId[string-length(@Name) > $minimum]
]]>
@@ -1248,6 +1267,7 @@ The EJB Specification states that any MessageDrivenBean or SessionBean should be
4
+
3
+
@@ -1478,6 +1499,7 @@ Detects when a package definition contains uppercase characters.
3
+
//PackageDeclaration/Name[lower-case(@Image)!=@Image]
@@ -1531,6 +1553,7 @@ Remote Interface of a Session EJB should not have a suffix.
4
+
4
+
4
+
3
+
3
+
@@ -1764,10 +1791,11 @@ which class a static member comes from (Sun 1.5 Language Guide).
+
$maximumStaticImports]
+.[count(ImportDeclaration[@Static = true()]) > $maximumStaticImports]
]]>
@@ -2003,6 +2031,7 @@ List stringsWithDiamond = new ArrayList<>(); // using the diamond operat
Useless parentheses should be removed.
4
+
stringsWithDiamond = new ArrayList<>(); // using the diamond operat
|
//Expression/ConditionalAndExpression/PrimaryExpression/PrimaryPrefix/Expression[
count(*)=1 and
- count(./CastExpression)=0 and
- count(./EqualityExpression/MultiplicativeExpression)=0 and
- count(./ConditionalExpression)=0 and
- count(./ConditionalOrExpression)=0]
+ not(./CastExpression) and
+ not(./EqualityExpression/MultiplicativeExpression) and
+ not(./ConditionalExpression) and
+ not(./ConditionalOrExpression)]
|
//Expression/ConditionalOrExpression/PrimaryExpression/PrimaryPrefix/Expression[
count(*)=1 and
@@ -2034,11 +2063,11 @@ List stringsWithDiamond = new ArrayList<>(); // using the diamond operat
not(./CastExpression) and
not(./EqualityExpression)]
|
-//Expression/AdditiveExpression[not(./PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral='true'])]
+//Expression/AdditiveExpression[not(./PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()])]
/PrimaryExpression[1]/PrimaryPrefix/Expression[
count(*)=1 and
not(./CastExpression) and
- not(./AdditiveExpression[@Image = '-']) and
+ not(./AdditiveExpression[@Operator = '-']) and
not(./ShiftExpression) and
not(./RelationalExpression) and
not(./InstanceOfExpression) and
@@ -2091,14 +2120,15 @@ public class Foo {
3
+
@@ -2150,6 +2180,7 @@ E.g. `int[] x = new int[] { 1, 2, 3 };` can be written as `int[] x = { 1, 2, 3 }
3
+
3
+
//WhileStatement[not(Statement/Block)]
diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml
index 2fb4fe92d8..dba3f14cec 100644
--- a/pmd-java/src/main/resources/category/java/design.xml
+++ b/pmd-java/src/main/resources/category/java/design.xml
@@ -23,12 +23,14 @@ protected constructor in order to prevent instantiation than make the class misl
1
+
@@ -55,6 +57,7 @@ Avoid catching generic exceptions such as NullPointerException, RuntimeException
3
+
3
+
@@ -161,6 +165,7 @@ code size and runtime complexity.
3
+
1
+
1
+
3
+
1
+
= 1 ]
-[count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[(@Public = 'true') or (@Protected = 'true') or (@PackagePrivate = 'true')]) = 0 ]
+//TypeDeclaration[count(../TypeDeclaration) = 1]/ClassOrInterfaceDeclaration
+[@Final = false()]
+[ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[@Private = true()]]
+[not(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[(@Public = true()) or (@Protected = true()) or (@PackagePrivate = true())])]
[not(.//ClassOrInterfaceDeclaration)]
]]>
@@ -367,15 +376,16 @@ Sometimes two consecutive 'if' statements can be consolidated by separating thei
3
+
@@ -532,6 +542,7 @@ Errors are system exceptions. Do not extend them.
3
+
3
+
@@ -845,6 +857,7 @@ Use opposite operator instead of negating the whole expression with a logic comp
3
+
3
+
3
+
@@ -1297,6 +1311,7 @@ Avoid unnecessary comparisons in boolean expressions, they serve no purpose and
3
+
3
+
3
+
/Block
/BlockStatement[last()]
[not(Statement/StatementExpression/PrimaryExpression
- [./PrimaryPrefix[@SuperModifier='true']]
+ [./PrimaryPrefix[@SuperModifier= true()]]
[./PrimarySuffix[@Image='finalize']]
)
]
[not(Statement/TryStatement/FinallyStatement
/Block/BlockStatement/Statement/StatementExpression/PrimaryExpression
- [./PrimaryPrefix[@SuperModifier='true']]
+ [./PrimaryPrefix[@SuperModifier= true()]]
[./PrimarySuffix[@Image='finalize']]
)
]
@@ -1917,6 +1938,7 @@ If the finalize() is implemented, it should do something besides just calling su
3
+
@@ -1954,6 +1976,7 @@ Note that Oracle has declared Object.finalize() as deprecated since JDK 9.
3
+
3
+
@@ -2057,6 +2081,7 @@ Avoid instantiating an object just to call getClass() on it; use the .class publ
4
+
3
+
@@ -2146,37 +2172,12 @@ public class JumbledIncrementerRule1 {
language="java"
since="1.0"
message="You may have misspelled a JUnit framework method (setUp or tearDown)"
- class="net.sourceforge.pmd.lang.rule.XPathRule"
+ class="net.sourceforge.pmd.lang.java.rule.errorprone.JUnitSpellingRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#junitspelling">
Some JUnit framework methods are easy to misspell.
3
-
-
-
-
-
-
-
The suite() method in a JUnit test needs to be both public and static.
3
-
-
-
-
-
-
-
2
+
@@ -2343,7 +2322,7 @@ Either the check is useless (the variable will never be "null") or it is incorre
public class Foo {
void bar() {
if (a.equals(baz) && a != null) {} // a could be null, misplaced null check
-
+
if (a != null && a.equals(baz)) {} // correct null check
}
}
@@ -2354,7 +2333,7 @@ public class Foo {
public class Foo {
void bar() {
if (a.equals(baz) || a == null) {} // a could be null, misplaced null check
-
+
if (a == null || a.equals(baz)) {} // correct null check
}
}
@@ -2374,6 +2353,7 @@ may indicate problematic behaviour. Empty cases are ignored as these indicate an
3
+
@@ -2427,13 +2407,14 @@ chain needs an own serialVersionUID field. See also [Should an abstract class ha
3
+
@@ -2470,14 +2451,13 @@ See the property `annotations`.
3
+
//SwitchStatement//BlockStatement/Statement/LabeledStatement
@@ -2684,29 +2661,12 @@ public class Foo { // perfect, both methods provided
language="java"
since="1.4"
message="Object clone() should be implemented with super.clone()"
- class="net.sourceforge.pmd.lang.rule.XPathRule"
+ class="net.sourceforge.pmd.lang.java.rule.errorprone.ProperCloneImplementationRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#propercloneimplementation">
Object clone() should be implemented with super.clone().
2
-
-
-
- 0
-]
-]]>
-
-
-
@@ -2793,12 +2753,13 @@ NullPointerExceptions.
1
+
3
+
3
+
@@ -3024,6 +2987,7 @@ new StringBuilder("A") // 1 + 16 = 17
4
+
2
+
3
+