Merge branch 'pmd/7.0.x' into pmd7-junit5-part3

This commit is contained in:
Andreas Dangel 2023-01-27 10:02:58 +01:00
commit 0d2acf6000
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
152 changed files with 3694 additions and 2621 deletions

View File

@ -7070,6 +7070,24 @@
"contributions": [
"code"
]
},
{
"login": "liamsharp",
"name": "Liam Sharp",
"avatar_url": "https://avatars.githubusercontent.com/u/6429288?v=4",
"profile": "https://github.com/liamsharp",
"contributions": [
"bug"
]
},
{
"login": "hassanalamibmx",
"name": "Hassan ALAMI",
"avatar_url": "https://avatars.githubusercontent.com/u/67870478?v=4",
"profile": "https://github.com/hassanalamibmx",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,

View File

@ -265,7 +265,7 @@ function pmd_ci_dogfood() {
sed -i 's/<version>[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}.*<\/version>\( *<!-- pmd.dogfood.version -->\)/<version>'"${PMD_CI_MAVEN_PROJECT_VERSION}"'<\/version>\1/' pom.xml
if [ "${PMD_CI_MAVEN_PROJECT_VERSION}" = "7.0.0-SNAPSHOT" ]; then
sed -i 's/pmd-dogfood-config\.xml/pmd-dogfood-config7.xml/' pom.xml
mpmdVersion=(-Denforcer.skip=true -Dpmd.plugin.version=3.18.0-pmd7-SNAPSHOT)
mpmdVersion=(-Denforcer.skip=true -Dpmd.plugin.version=3.20.1-pmd-7-SNAPSHOT)
fi
./mvnw verify --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" \
"${mpmdVersion[@]}" \

View File

@ -134,6 +134,25 @@ index 6021fa574d..15d29ed699 100644
EOF
) | patch --strip=1
# Patch 4: Add https://maven.repository.redhat.com/ga/ as repository in order to resolve
# dependency com.ibm.websphere/uow/6.0.2.17
# See https://spring.io/blog/2020/10/29/notice-of-permissions-changes-to-repo-spring-io-fall-and-winter-2020
(cat <<EOF
diff --git a/build.gradle b/build.gradle
index 6021fa57..8319ff76 100644
--- a/build.gradle
+++ b/build.gradle
@@ -291,6 +291,7 @@ configure(allprojects) { project ->
}
repositories {
mavenCentral()
+ maven { url "https://maven.repository.redhat.com/ga/" }
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
}
EOF
) | patch --strip=1
./gradlew --console=plain --build-cache --no-daemon --max-workers=4 build testClasses -x test -x javadoc -x api -x asciidoctor -x asciidoctorPdf
./gradlew --console=plain --build-cache --no-daemon --max-workers=4 createSquishClasspath -q > classpath.txt
]]></build-command>

View File

@ -55,7 +55,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/19/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/20/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -25,7 +25,7 @@ jobs:
shell: bash
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/19/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/20/scripts" >> $GITHUB_ENV
- name: Sync
run: .ci/git-repo-sync.sh
shell: bash

View File

@ -36,7 +36,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/19/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/20/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -9,7 +9,7 @@ GEM
nap
open4 (~> 1.3)
colored2 (3.1.2)
concurrent-ruby (1.1.10)
concurrent-ruby (1.2.0)
cork (0.3.0)
colored2 (~> 3.1)
danger (9.1.0)
@ -28,7 +28,7 @@ GEM
differ (0.1.2)
et-orbi (1.2.7)
tzinfo
faraday (1.10.2)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@ -53,10 +53,10 @@ GEM
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
fugit (1.8.0)
fugit (1.8.1)
et-orbi (~> 1, >= 1.2.7)
raabro (~> 1.4)
git (1.13.0)
git (1.13.1)
addressable (~> 2.8)
rchardet (~> 1.8)
kramdown (2.4.0)
@ -69,7 +69,7 @@ GEM
multipart-post (2.2.3)
nap (1.1.0)
no_proxy_fix (0.1.2)
nokogiri (1.13.10)
nokogiri (1.14.0)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (5.6.1)
@ -114,4 +114,4 @@ DEPENDENCIES
safe_yaml
BUNDLED WITH
2.1.4
2.4.5

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.6)
activesupport (6.0.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@ -14,8 +14,8 @@ GEM
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.23.6)
concurrent-ruby (1.1.10)
commonmarker (0.23.7)
concurrent-ruby (1.2.0)
dnsruby (1.61.9)
simpleidn (~> 0.1)
em-websocket (0.5.3)
@ -25,7 +25,7 @@ GEM
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (2.7.2)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
@ -263,4 +263,4 @@ DEPENDENCIES
webrick
BUNDLED WITH
2.1.4
2.4.5

File diff suppressed because it is too large Load Diff

View File

@ -122,7 +122,8 @@ The tool comes with a rather extensive help text, simply running with `--help`!
%}
{% include custom/cli_option_row.html options="-language,-l"
option_arg="lang"
description="Specify the language PMD should use. Used together with `-version`. See also [Supported Languages](#supported-languages)."
description="Specify the language PMD should use. Used together with `-version`. See also [Supported Languages](#supported-languages).
<p><span class=\"label label-default\">Deprecated</span> since PMD 6.52.0. Use `--use-version` instead.</p>"
%}
{% include custom/cli_option_row.html options="--minimum-priority"
option_arg="priority"
@ -147,13 +148,17 @@ The tool comes with a rather extensive help text, simply running with `--help`!
description="Specifies a property for the report renderer. The option can be specified several times.
<p>Using `--help` will provide a complete list of supported properties for each report format</p>"
%}
{% include custom/cli_option_row.html options="--relativize-paths-with,-z"
option_arg="path"
description="Path relative to which directories are rendered in the report. This option allows
shortening directories in the report; without it, paths are rendered as mentioned in the source directory (option \"--dir\").
The option can be repeated, in which case the shortest relative path will be used.
If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered as absolute."
%}
{% include custom/cli_option_row.html options="--report-file,-r"
option_arg="path"
description="Path to a file to which report output is written. The file is created if it does not exist. If this option is not specified, the report is rendered to standard output."
%}
{% include custom/cli_option_row.html options="--short-names"
description="Prints shortened filenames in the report."
%}
{% include custom/cli_option_row.html options="--show-suppressed"
description="Causes the suppressed rule violations to be added to the report."
%}

View File

@ -345,9 +345,16 @@ Was expecting one of:
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.
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)
There are many other stylesheets available online: <https://github.com/pmd/pmd/tree/master/pmd-core/etc/xslt>.
Examples:
* [Example with pmd-nicerhtml.xsl](report-examples/pmd-report-pmd-nicerhtml.html)
* [Example with html-report-v2.xslt](report-examples/html-report-v2.html) - includes charts. It requires javascript enabled and uses
[jQuery](https://jquery.com/), [DataTables](https://datatables.net/), and [Vega](https://vega.github.io/vega/) for charting.
**Properties:**

View File

@ -77,11 +77,6 @@ The examples below won't repeat this taskdef element, as this is always required
<td>The rule priority threshold; rules with lower priority than they will not be used</td>
<td>No</td>
</tr>
<tr>
<td>shortFilenames</td>
<td>Places truncated filenames in the report. This can reduce your report file size by 15%-20%.</td>
<td>No</td>
</tr>
<tr>
<td>failuresPropertyName</td>
<td>A property name to plug the number of rule violations into when the task finishes</td>
@ -187,7 +182,7 @@ automatically and the latest language version is used.
<target name="pmd">
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask"/>
<pmd shortFilenames="true">
<pmd>
<ruleset>rulesets/java/quickstart.xml</ruleset>
<ruleset>config/my-ruleset.xml</ruleset>
<fileset dir="/usr/local/j2sdk1.4.1_01/src/">
@ -199,6 +194,11 @@ automatically and the latest language version is used.
`fileset` nested element - specify the actual java source files, that PMD should analyze. You can use multiple
fileset elements. See [FileSet](https://ant.apache.org/manual/Types/fileset.html) for the syntax and usage.
`relativizePathsWith` nested element - configures the paths relative to which directories are rendered in the report.
This option allows shortening directories in the report; without it, paths are rendered as absolute paths.
The option can be repeated, in which case the shortest relative path will be used.
It is a [path-like structure](https://ant.apache.org/manual/using.html#path).
### Language version selection
PMD selects the language automatically using the file extension. If multiple versions of a language are
@ -410,7 +410,7 @@ An HTML report with the "linkPrefix" and "linePrefix" properties:
<target name="pmd">
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask"/>
<pmd rulesetfiles="rulesets/java/quickstart.xml" shortFilenames="true">
<pmd rulesetfiles="rulesets/java/quickstart.xml">
<formatter type="html" toFile="pmd_report.html">
<param name="linkPrefix" value="https://maven.apache.org/plugins/maven-pmd-plugin/xref/"/>
<param name="linePrefix" value="L"/>
@ -418,6 +418,9 @@ An HTML report with the "linkPrefix" and "linePrefix" properties:
<fileset dir="/usr/local/j2sdk1.4.1_01/src/">
<include name="java/lang/*.java"/>
</fileset>
<relativizePathsWith>
<pathelement location="/usr/local/j2sdk1.4.1_01/src/"/>
</relativizePathsWith>
</pmd>
</target>

View File

@ -19,10 +19,35 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
#### New report format html-report-v2.xslt
Thanks to @mohan-chinnappan-n a new PMD report format has been added which features a data table
with charting functions. It uses an XSLT stylesheet to convert PMD's XML format into HTML.
See [the example report](report-examples/html-report-v2.html).
### Fixed Issues
* apex-bestpractices
* [#2669](https://github.com/pmd/pmd/issues/2669): \[apex] UnusedLocalVariable false positive in dynamic SOQL
* core
* [#4026](https://github.com/pmd/pmd/issues/4026): \[cli] Filenames printed as absolute paths in the report despite parameter `--short-names`
* [#4279](https://github.com/pmd/pmd/issues/4279): \[core] Can not set ruleset property value to empty
* [#4329](https://github.com/pmd/pmd/pull/4329): \[core] Refactor usage of snakeyaml
* [#4340](https://github.com/pmd/pmd/issues/4340): \[core] Allow to filter found matches in CPDReport
* java
* [#4364](https://github.com/pmd/pmd/issues/4364): \[java] Parsing error with textblock containing quote followed by two backslashes
* testing
* [#4236](https://github.com/pmd/pmd/issues/4236): \[test] kotest logs look broken
### API Changes
#### PMD CLI
* PMD now supports a new `--relativize-paths-with` flag (or short `-z`), which replaces `--short-names`.
It serves the same purpose: Shortening the pathnames in the reports. However, with the new flag it's possible
to explicitly define one or more pathnames that should be used as the base when creating relative paths.
The old flag `--short-names` is deprecated.
#### Deprecated APIs
##### For removal
@ -31,12 +56,39 @@ This is a {{ site.pmd.release_type }} release.
always `Version.CURRENT`, as the apex compiler integration doesn't use additional information which Apex version
actually is used. Therefore, this method can't be used to determine the Apex version of the project
that is being analyzed.
* {% jdoc !!core::cpd.CPDConfiguration#setEncoding(java.lang.String) %} and
{% jdoc !!core::cpd.CPDConfiguration#getEncoding() %}. Use the methods
{% jdoc core::AbstractConfiguration#getSourceEncoding() %} and
{% jdoc core::AbstractConfiguration#setSourceEncoding(java.lang.String) %} instead. Both are available
for `CPDConfiguration` which extends `AbstractConfiguration`.
* {% jdoc test::cli.BaseCLITest %} and {% jdoc test::cli.BaseCPDCLITest %} have been deprecated for removal without
replacement. CLI tests should be done in pmd-core only (and in PMD7 in pmd-cli). Individual language modules
shouldn't need to test the CLI integration logic again. Instead, the individual language modules should test their
functionality as unit tests.
* {% jdoc core::cpd.CPDConfiguration.LanguageConverter %}
* {% jdoc !!core::lang.document.FileCollector#addZipFile(java.nio.file.Path) %} has been deprecated. It is replaced
by {% jdoc !!core::lang.document.FileCollector#addZipFileWithContent(java.nio.file.Path) %} which directly adds the
content of the zip file for analysis.
* {% jdoc !!core::PMDConfiguration#setReportShortNames(boolean) %} and
{% jdoc !!core::PMDConfiguration#isReportShortNames() %} have been deprecated for removal.
Use {% jdoc !!core::PMDConfiguration#addRelativizeRoot(java.nio.file.Path) %} instead.
##### Internal APIs
* {% jdoc core::renderers.CSVWriter %}
* Some fields in {% jdoc test::ant.AbstractAntTestHelper %}
##### Experimental APIs
* CPDReport has a new method which limited mutation of a given report:
* {%jdoc core::cpd.CPDReport#filterMatches(net.sourceforge.pmd.util.Predicate) %} creates a new CPD report
with some matches removed with a given predicate based filter.
### External Contributions
* [#4110](https://github.com/pmd/pmd/pull/4110): \[apex] Feature/unused variable bind false positive with dynamic SOQL - [Thomas Prouvot](https://github.com/tprouvot) (@tprouvot)
* [#4125](https://github.com/pmd/pmd/pull/4125): \[core] New report format html-report-v2.xslt to provide html with datatable and chart features - [Mohan Chinnappan](https://github.com/mohan-chinnappan-n) - (@mohan-chinnappan-n)
* [#4280](https://github.com/pmd/pmd/pull/4280): \[apex] Deprecate ApexRootNode.getApexVersion - [Aaron Hurst](https://github.com/aaronhurst-google) (@aaronhurst-google)
* [#4285](https://github.com/pmd/pmd/pull/4285): \[java] CommentDefaultAccessModifier - add co.elastic.clients.util.VisibleForTesting as default suppressed annotation - [Matthew Luckam](https://github.com/mluckam) (@mluckam)

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,6 @@ import net.sourceforge.pmd.cpd.ReportException;
import net.sourceforge.pmd.cpd.SimpleRenderer;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.cpd.XMLRenderer;
import net.sourceforge.pmd.cpd.renderer.CPDRendererAdapter;
import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer;
/**
@ -94,7 +93,7 @@ public class CPDTask extends Task {
CPDConfiguration config = new CPDConfiguration();
config.setMinimumTileSize(minimumTokenCount);
config.setLanguage(createLanguage());
config.setEncoding(encoding);
config.setSourceEncoding(encoding);
config.setSkipDuplicates(skipDuplicateFiles);
config.setSkipLexicalErrors(skipLexicalErrors);
@ -190,9 +189,9 @@ public class CPDTask extends Task {
private CPDReportRenderer createRenderer() {
if (TEXT_FORMAT.equals(format)) {
return new CPDRendererAdapter(new SimpleRenderer());
return new SimpleRenderer();
} else if (CSV_FORMAT.equals(format)) {
return new CPDRendererAdapter(new CSVRenderer());
return new CSVRenderer();
}
return new XMLRenderer();
}

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.ant;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@ -14,8 +15,10 @@ import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.Resource;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.ant.internal.PMDTaskImpl;
/**
@ -30,7 +33,7 @@ public class PMDTask extends Task {
private final List<FileSet> filesets = new ArrayList<>();
private boolean failOnError;
private boolean failOnRuleViolation;
private boolean shortFilenames;
private final List<Path> relativizePathsWith = new ArrayList<>();
private String suppressMarker;
private String rulesetFiles;
private boolean noRuleSetCompatibility;
@ -94,10 +97,6 @@ public class PMDTask extends Task {
return sb.toString();
}
public void setShortFilenames(boolean reportShortNames) {
this.shortFilenames = reportShortNames;
}
public void setSuppressMarker(String suppressMarker) {
this.suppressMarker = suppressMarker;
}
@ -207,10 +206,6 @@ public class PMDTask extends Task {
return failOnRuleViolation;
}
public boolean isShortFilenames() {
return shortFilenames;
}
public String getSuppressMarker() {
return suppressMarker;
}
@ -271,4 +266,23 @@ public class PMDTask extends Task {
public void setNoCache(boolean noCache) {
this.noCache = noCache;
}
public void addRelativizePathsWith(Path relativizePathsWith) {
this.relativizePathsWith.add(relativizePathsWith);
}
public List<Path> getRelativizePathsWith() {
return relativizePathsWith;
}
@InternalApi
public List<java.nio.file.Path> getRelativizeRoots() {
List<java.nio.file.Path> paths = new ArrayList<>();
for (Path path : getRelativizePathsWith()) {
for (Resource resource : path) {
paths.add(Paths.get(resource.toString()));
}
}
return paths;
}
}

View File

@ -53,7 +53,7 @@ public class PMDTaskImpl {
private Project project;
public PMDTaskImpl(PMDTask task) {
configuration.setReportShortNames(task.isShortFilenames());
configuration.addRelativizeRoots(task.getRelativizeRoots());
if (task.getSuppressMarker() != null) {
configuration.setSuppressMarker(task.getSuppressMarker());
}
@ -103,7 +103,6 @@ public class PMDTaskImpl {
project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE);
}
List<String> ruleSetPaths = expandRuleSetPaths(configuration.getRuleSetPaths());
// don't let PmdAnalysis.create create rulesets itself.
configuration.setRuleSets(Collections.emptyList());
@ -116,9 +115,6 @@ public class PMDTaskImpl {
for (FileSet fileset : filesets) {
DirectoryScanner ds = fileset.getDirectoryScanner(project);
if (configuration.isReportShortNames()) {
pmd.files().relativizeWith(ds.getBasedir().getPath());
}
for (String srcFile : ds.getIncludedFiles()) {
pmd.files().addFile(ds.getBasedir().toPath().resolve(srcFile));
}

View File

@ -9,6 +9,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@ -69,14 +70,14 @@ class PMDTaskTest extends AbstractAntTest {
}
@Test
void testWithShortFilenames() throws IOException {
executeTarget("testWithShortFilenames");
void testRelativizeWith() throws IOException {
executeTarget("testRelativizeWith");
try (InputStream in = Files.newInputStream(Paths.get("target/pmd-ant-test.txt"))) {
String actual = IOUtil.readToString(in, StandardCharsets.UTF_8);
// remove any trailing newline
actual = actual.trim();
assertThat(actual, containsString("sample.dummy:1:\tSampleXPathRule:\tTest Rule 2"));
actual = actual.replaceAll("\n|\r", "");
assertThat(actual, containsString("src" + File.separator + "sample.dummy:1:\tSampleXPathRule:\tTest Rule 2"));
}
}

View File

@ -25,25 +25,31 @@
<pmd />
</target>
<target name="testWithShortFilenames">
<pmd noCache="true" shortFilenames="true">
<target name="testRelativizeWith">
<pmd noCache="true">
<!-- note: this ruleset is in pmd-core/src/test/resources -->
<ruleset>rulesets/dummy/basic.xml</ruleset>
<formatter type="text" toFile="${pmd.home}/target/pmd-ant-test.txt" />
<fileset dir="${pmd.home}/src/test/resources/net/sourceforge/pmd/ant/src">
<fileset dir="${pmd.home}/src/test/resources/net/sourceforge/pmd/ant/">
<include name="**/*dummy"/>
</fileset>
<relativizePathsWith>
<pathelement location="${pmd.home}/src/test/resources/net/sourceforge/pmd/ant"/>
</relativizePathsWith>
</pmd>
</target>
<target name="testXmlFormatter">
<pmd noCache="true" shortFilenames="true">
<pmd noCache="true">
<!-- note: this ruleset is in pmd-core/src/test/resources -->
<ruleset>rulesets/dummy/basic.xml</ruleset>
<formatter type="xml" toFile="${pmd.home}/target/pmd-ant-xml.xml" />
<fileset dir="${pmd.home}/src/test/resources/net/sourceforge/pmd/ant/src">
<include name="**/*dummy"/>
</fileset>
<relativizePathsWith>
<pathelement location="${pmd.home}/src/test/resources/net/sourceforge/pmd/ant/src"/>
</relativizePathsWith>
</pmd>
</target>
</project>

View File

@ -104,11 +104,15 @@
<artifactId>slf4j-api</artifactId>
<!-- apex jorje actually depends on 1.7.20 (https://github.com/forcedotcom/salesforcedx-vscode/commit/631b8cfb85cff5e989bfea13bca681b6cedcb003) -->
</dependency>
<dependency>
<!-- apex jorje actually depends on 1.17 (https://github.com/forcedotcom/salesforcedx-vscode/commit/631b8cfb85cff5e989bfea13bca681b6cedcb003)
however, it is not really needed, so we don't add it here as a dependency,
so that it doesn't end up in pmd-dist
-->
<!--<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<!-- apex jorje actually depends on 1.17 (https://github.com/forcedotcom/salesforcedx-vscode/commit/631b8cfb85cff5e989bfea13bca681b6cedcb003) -->
</dependency>
-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>

View File

@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.apex.ast;
import java.net.URI;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
@ -60,6 +62,11 @@ public final class ASTApexFile extends AbstractApexNode<AstNode> implements Root
}
public List<Issue> getGlobalIssues() {
return multifileAnalysis.getFileIssues(getAstInfo().getTextDocument().getPathId());
String filename = getAstInfo().getTextDocument().getPathId();
if (filename.length() > 7 && "file://".equalsIgnoreCase(filename.substring(0, 7))) {
URI uri = URI.create(filename);
filename = Paths.get(uri).toString();
}
return multifileAnalysis.getFileIssues(filename);
}
}

View File

@ -5,12 +5,21 @@
package net.sourceforge.pmd.lang.apex.rule.bestpractices;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.apex.ast.ASTBlockStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTLiteralExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration;
import net.sourceforge.pmd.lang.apex.ast.ASTVariableExpression;
@ -19,6 +28,13 @@ import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
public class UnusedLocalVariableRule extends AbstractApexRule {
private static final Set<String> DATABASE_QUERY_METHODS = new HashSet<>(Arrays.asList(
"Database.query".toLowerCase(Locale.ROOT),
"Database.getQueryLocator".toLowerCase(Locale.ROOT),
"Database.countQuery".toLowerCase(Locale.ROOT)
));
private static final Pattern BINDING_VARIABLE = Pattern.compile("(?i):([a-z0-9]+)");
@Override
protected @NonNull RuleTargetSelector buildTargetSelector() {
@ -52,7 +68,56 @@ public class UnusedLocalVariableRule extends AbstractApexRule {
}
}
List<String> soqlBindingVariables = findBindingsInSOQLStringLiterals(variableContext);
if (soqlBindingVariables.contains(variableName.toLowerCase(Locale.ROOT))) {
return data;
}
addViolation(data, node, variableName);
return data;
}
private List<String> findBindingsInSOQLStringLiterals(ASTBlockStatement variableContext) {
List<String> bindingVariables = new ArrayList<>();
List<ASTMethodCallExpression> methodCalls = variableContext.findDescendantsOfType(ASTMethodCallExpression.class)
.stream()
.filter(m -> DATABASE_QUERY_METHODS.contains(m.getFullMethodName().toLowerCase(Locale.ROOT)))
.collect(Collectors.toList());
methodCalls.forEach(databaseMethodCall -> {
List<String> stringLiterals = new ArrayList<>();
stringLiterals.addAll(databaseMethodCall.findDescendantsOfType(ASTLiteralExpression.class)
.stream()
.filter(ASTLiteralExpression::isString)
.map(ASTLiteralExpression::getImage)
.collect(Collectors.toList()));
databaseMethodCall.findDescendantsOfType(ASTVariableExpression.class).forEach(variableUsage -> {
String referencedVariable = variableUsage.getImage();
// Search other usages of the same variable within this code block
variableContext.findDescendantsOfType(ASTVariableExpression.class)
.stream()
.filter(usage -> referencedVariable.equalsIgnoreCase(usage.getImage()))
.forEach(usage -> {
stringLiterals.addAll(usage.getParent()
.findChildrenOfType(ASTLiteralExpression.class)
.stream()
.filter(ASTLiteralExpression::isString)
.map(ASTLiteralExpression::getImage)
.collect(Collectors.toList()));
});
});
stringLiterals.forEach(s -> {
Matcher matcher = BINDING_VARIABLE.matcher(s);
while (matcher.find()) {
bindingVariables.add(matcher.group(1).toLowerCase(Locale.ROOT));
}
});
});
return bindingVariables;
}
}

View File

@ -109,6 +109,53 @@ class Foo {
return bar;
}
}
]]></code>
]]>
</code>
</test-code>
<test-code>
<description>[apex] UnusedLocalVariable - false positive on string query #2669</description>
<expected-problems>0</expected-problems>
<code>
<![CDATA[
class Foo {
public Database.QueryLocator start1(Database.BatchableContext BC) {
String customValue = 'Test';
String query = 'SELECT Id FROM Case ';
query += 'WHERE CustomField__c = :customValue ';
return Database.getQueryLocator(query);
}
public Database.QueryLocator start2(Database.BatchableContext BC) {
String customValue = 'Test';
return Database.getQueryLocator('SELECT Id From Case WHERE CustomField__c = :customValue');
}
public void doQuery1() {
String customValue = 'Test';
String query = 'SELECT Id FROM Case ';
query += 'WHERE CustomField__c = :customValue ';
Database.query(query);
}
public void doQuery2() {
String customValue = 'Test';
Database.query('SELECT Id From Case WHERE CustomField__c = :customValue');
}
public void doCount1() {
String customValue = 'Test';
String query = 'SELECT Id FROM Case ';
query += 'WHERE CustomField__c = :customValue ';
Database.countQuery(query);
}
public void doCount2() {
String customValue = 'Test';
Database.countQuery('SELECT Id From Case WHERE CustomField__c = :customValue');
}
}
]]>
</code>
</test-code>
</test-data>

View File

@ -9,7 +9,7 @@ import java.util.concurrent.Callable;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import net.sourceforge.pmd.cli.internal.ExecutionResult;
import net.sourceforge.pmd.cli.internal.CliExitCode;
import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
import picocli.CommandLine.Model.CommandSpec;
@ -46,7 +46,7 @@ public abstract class AbstractPmdSubcommand implements Callable<Integer> {
// no-op, children may override
}
protected abstract ExecutionResult execute();
protected abstract CliExitCode execute();
private void setupCliLogger() {
// only reconfigure logging, if debug flag was used on command line

View File

@ -17,7 +17,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.cli.commands.typesupport.internal.CpdLanguageTypeSupport;
import net.sourceforge.pmd.cli.internal.ExecutionResult;
import net.sourceforge.pmd.cli.internal.CliExitCode;
import net.sourceforge.pmd.cpd.CPD;
import net.sourceforge.pmd.cpd.CPDConfiguration;
import net.sourceforge.pmd.cpd.CPDReport;
@ -127,7 +127,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand {
}
@Override
protected ExecutionResult execute() {
protected CliExitCode execute() {
final Logger logger = LoggerFactory.getLogger(CpdCommand.class);
// TODO : Create a new CpdAnalysis to match PmdAnalysis
@ -141,15 +141,15 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand {
configuration.getCPDReportRenderer().render(report, IOUtil.createWriter(Charset.defaultCharset(), null));
if (cpd.getMatches().hasNext() && configuration.isFailOnViolation()) {
return ExecutionResult.VIOLATIONS_FOUND;
return CliExitCode.VIOLATIONS_FOUND;
}
} catch (IOException | RuntimeException e) {
logger.debug(e.toString(), e);
logger.error(LogMessages.errorDetectedMessage(1, "cpd"));
return ExecutionResult.ERROR;
return CliExitCode.ERROR;
}
return ExecutionResult.OK;
return CliExitCode.OK;
}
/**

View File

@ -4,7 +4,7 @@
package net.sourceforge.pmd.cli.commands.internal;
import net.sourceforge.pmd.cli.internal.ExecutionResult;
import net.sourceforge.pmd.cli.internal.CliExitCode;
import net.sourceforge.pmd.util.fxdesigner.DesignerStarter;
import net.sourceforge.pmd.util.fxdesigner.DesignerStarter.ExitStatus;
import net.sourceforge.pmd.util.fxdesigner.DesignerVersion;
@ -23,11 +23,11 @@ public class DesignerCommand extends AbstractPmdSubcommand {
private boolean versionRequested;
@Override
protected ExecutionResult execute() {
protected CliExitCode execute() {
final String[] rawArgs = spec.commandLine().getParseResult().expandedArgs().toArray(new String[0]);
final ExitStatus status = DesignerStarter.launchGui(rawArgs);
return status == ExitStatus.OK ? ExecutionResult.OK : ExecutionResult.ERROR;
return status == ExitStatus.OK ? CliExitCode.OK : CliExitCode.ERROR;
}
}

View File

@ -7,6 +7,7 @@ package net.sourceforge.pmd.cli.commands.internal;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
@ -26,7 +27,7 @@ import net.sourceforge.pmd.benchmark.TimingReport;
import net.sourceforge.pmd.benchmark.TimingReportRenderer;
import net.sourceforge.pmd.cli.commands.typesupport.internal.PmdLanguageTypeSupport;
import net.sourceforge.pmd.cli.commands.typesupport.internal.PmdLanguageVersionTypeSupport;
import net.sourceforge.pmd.cli.internal.ExecutionResult;
import net.sourceforge.pmd.cli.internal.CliExitCode;
import net.sourceforge.pmd.cli.internal.ProgressBarListener;
import net.sourceforge.pmd.internal.LogMessages;
import net.sourceforge.pmd.lang.Language;
@ -84,7 +85,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
private boolean benchmark;
private boolean shortnames;
private List<Path> relativizeRootPaths;
private boolean showSuppressed;
@ -140,9 +141,21 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
this.benchmark = benchmark;
}
@Option(names = "--short-names", description = "Prints shortened filenames in the report.")
public void setShortnames(final boolean shortnames) {
this.shortnames = shortnames;
@Option(names = { "--relativize-paths-with", "-z"}, description = "Path relative to which directories are rendered in the report. "
+ "This option allows shortening directories in the report; "
+ "without it, paths are rendered as mentioned in the source directory (option \"--dir\"). "
+ "The option can be repeated, in which case the shortest relative path will be used. "
+ "If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered as absolute.",
arity = "1..*", split = ",")
public void setRelativizePathsWith(List<Path> rootPaths) {
this.relativizeRootPaths = rootPaths;
for (Path path : this.relativizeRootPaths) {
if (Files.isRegularFile(path)) {
throw new ParameterException(spec.commandLine(),
"Expected a directory path for option '--relativize-paths-with', found a file: " + path);
}
}
}
@Option(names = "--show-suppressed", description = "Report should show suppressed rule violations.")
@ -272,7 +285,9 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
configuration.setMinimumPriority(minimumPriority);
configuration.setReportFile(reportFile);
configuration.setReportProperties(properties);
configuration.setReportShortNames(shortnames);
if (relativizeRootPaths != null) {
configuration.addRelativizeRoots(relativizeRootPaths);
}
configuration.setRuleSets(rulesets);
configuration.setRuleSetFactoryCompatibilityEnabled(!this.noRuleSetCompatibility);
configuration.setShowSuppressedViolations(showSuppressed);
@ -305,7 +320,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
}
@Override
protected ExecutionResult execute() {
protected CliExitCode execute() {
if (benchmark) {
TimeTracker.startGlobalTracking();
}
@ -320,7 +335,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
pmd = PmdAnalysis.create(configuration);
} catch (final Exception e) {
pmdReporter.errorEx("Could not initialize analysis", e);
return ExecutionResult.ERROR;
return CliExitCode.ERROR;
}
LOG.debug("Current classpath:\n{}", System.getProperty("java.class.path"));
@ -333,15 +348,15 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
pmd.addListener(new ProgressBarListener());
}
}
final ReportStats stats = pmd.runAndReturnStats();
if (pmdReporter.numErrors() > 0) {
// processing errors are ignored
return ExecutionResult.ERROR;
return CliExitCode.ERROR;
} else if (stats.getNumViolations() > 0 && configuration.isFailOnViolation()) {
return ExecutionResult.VIOLATIONS_FOUND;
return CliExitCode.VIOLATIONS_FOUND;
} else {
return ExecutionResult.OK;
return CliExitCode.OK;
}
} finally {
if (pmd != null) {
@ -352,7 +367,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand {
} catch (final Exception e) {
pmdReporter.errorEx("Exception while running PMD.", e);
printErrorDetected(pmdReporter, 1);
return ExecutionResult.ERROR;
return CliExitCode.ERROR;
} finally {
finishBenchmarker(pmdReporter);
}

View File

@ -12,7 +12,7 @@ import picocli.CommandLine.IVersionProvider;
@Command(name = "pmd", mixinStandardHelpOptions = true,
versionProvider = PMDVersionProvider.class,
exitCodeListHeading = "Exit Codes:%n",
exitCodeList = { "0:Succesful analysis, no violations found", "1:An unexpected error occurred during execution",
exitCodeList = { "0:Successful analysis, no violations found", "1:An unexpected error occurred during execution",
"2:Usage error, please refer to the command help", "4:Successful analysis, at least 1 violation found" },
subcommands = { PmdCommand.class, CpdCommand.class, DesignerCommand.class, CpdGuiCommand.class, TreeExportCommand.class })
public class PmdRootCommand {

View File

@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.cli.commands.mixins.internal.EncodingMixin;
import net.sourceforge.pmd.cli.commands.typesupport.internal.PmdLanguageTypeSupport;
import net.sourceforge.pmd.cli.internal.ExecutionResult;
import net.sourceforge.pmd.cli.internal.CliExitCode;
import net.sourceforge.pmd.internal.LogMessages;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@ -106,16 +106,16 @@ public class TreeExportCommand extends AbstractPmdSubcommand {
}
@Override
protected ExecutionResult execute() {
protected CliExitCode execute() {
final TreeExporter exporter = new TreeExporter(toConfiguration());
try {
exporter.export();
return ExecutionResult.OK;
return CliExitCode.OK;
} catch (final IOException e) {
final SimpleMessageReporter reporter = new SimpleMessageReporter(LoggerFactory.getLogger(TreeExportCommand.class));
reporter.error(e, LogMessages.errorDetectedMessage(1, "ast-dump"));
return ExecutionResult.ERROR;
return CliExitCode.ERROR;
}
}

View File

@ -0,0 +1,52 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cli.internal;
import net.sourceforge.pmd.PMDConfiguration;
/**
* The execution result of any given command.
*/
public enum CliExitCode {
/** No errors, no violations. This is exit code {@code 0}. */
OK(0),
/**
* Errors were detected, PMD may have not run to the end.
* This is exit code {@code 1}.
*/
ERROR(1),
/**
* Indicates a problem with the CLI parameters: either a required
* parameter is missing or an invalid parameter was provided.
*/
USAGE_ERROR(2),
/**
* No errors, but PMD found violations. This is exit code {@code 4}.
* This is only returned if {@link PMDConfiguration#isFailOnViolation()}
* is set (CLI flag {@code --failOnViolation}).
*/
VIOLATIONS_FOUND(4);
private final int exitCode;
CliExitCode(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
public static CliExitCode fromInt(int i) {
switch (i) {
case 0: return OK;
case 1: return ERROR;
case 2: return USAGE_ERROR;
case 4: return VIOLATIONS_FOUND;
default:
throw new IllegalArgumentException("Not a known exit code: " + i);
}
}
}

Some files were not shown because too many files have changed in this diff Show More