diff --git a/.all-contributorsrc b/.all-contributorsrc index dcac68bf60..240441581c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -6639,6 +6639,24 @@ "contributions": [ "code" ] + }, + { + "login": "lukelukes", + "name": "lukelukes", + "avatar_url": "https://avatars.githubusercontent.com/u/45536418?v=4", + "profile": "https://github.com/lukelukes", + "contributions": [ + "code" + ] + }, + { + "login": "vibhory2j", + "name": "Vibhor Goyal", + "avatar_url": "https://avatars.githubusercontent.com/u/15845016?v=4", + "profile": "https://github.com/vibhory2j", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/.ci/files/project-list.xml b/.ci/files/project-list.xml index fa804d1d8e..ce357698bc 100644 --- a/.ci/files/project-list.xml +++ b/.ci/files/project-list.xml @@ -1,7 +1,7 @@ + xsi:noNamespaceSchemaLocation="projectlist_1_1_0.xsd"> Standard Projects @@ -12,6 +12,7 @@ xsi:noNamespaceSchemaLocation="projectlist_1_1_0.xsd"> .*/target/test-classes/com/puppycrawl/tools/checkstyle/.* .*/target/generated-sources/.* + .*/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/javaparser/InputJavaParserNoFreezeOnDeeplyNestedLambdas.java .*/build/generated-sources/.* classpath.txt ]]> @@ -150,23 +151,15 @@ fi Schedul-o-matic-9000 git - https://github.com/pmd/Schedul-o-matic-9000 - - pmd-regression-test + 6b1229ba43b38931fbbab5924bc9b9611d19a786 fflib-apex-common git - https://github.com/pmd/fflib-apex-common - - pmd-regression-test + 7e0891efb86d23de62811af56d87d0959082a322 diff --git a/Gemfile.lock b/Gemfile.lock index 6c82745bd6..b41e9645e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,14 +68,14 @@ GEM multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.13.4) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) - pmdtester (1.4.1) + pmdtester (1.5.1) differ (~> 0.1) liquid (~> 5.2) logger-colors (~> 1.0) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 48b6705d7b..af044ad8d5 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.4.8) + activesupport (6.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -232,7 +232,7 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.15.0) multipart-post (2.1.1) - nokogiri (1.13.4) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) diff --git a/docs/pages/pmd/languages/html.md b/docs/pages/pmd/languages/html.md index acb96fc416..9c905571e1 100644 --- a/docs/pages/pmd/languages/html.md +++ b/docs/pages/pmd/languages/html.md @@ -14,5 +14,11 @@ last_updated: April 2022 (6.45.0) The HTML language module uses [jsoup](https://jsoup.org/) for parsing. -XPath rules are supported, but the DOM is not a typical XML/XPath DOM. E.g. -text nodes are normal nodes. This might change in the future. +XPath 2.0 rules are supported, but the DOM is not always a typical XML/XPath DOM. +In the Designer, text nodes appear as nodes with name "#text", but they can +be selected as usual using `text()`. + +XML Namespaces are not supported. The local name of attributes include the prefix, +so that you have to select attributes by e.g. `//*[@*[local-name() = 'if:true']]`. + +Only XPath 1.0 rules are not supported. diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 3f6b6213cf..e243b5c134 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -685,262 +685,264 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Valentin Brandl

πŸ›
Valeria

πŸ›
Vasily Anisimov

πŸ› +
Vibhor Goyal

πŸ›
Vickenty Fesunov

πŸ› -
Victor NoΓ«l

πŸ› +
Victor NoΓ«l

πŸ›
Vincent Galloy

πŸ’»
Vincent HUYNH

πŸ›
Vincent Maurin

πŸ›
Vincent Privat

πŸ›
Vishhwas

πŸ›
Vitaly

πŸ› -
Vitaly Polonetsky

πŸ› +
Vitaly Polonetsky

πŸ›
Vojtech Polivka

πŸ›
Vsevolod Zholobov

πŸ›
Vyom Yadav

πŸ’»
Wang Shidong

πŸ›
Waqas Ahmed

πŸ›
Wayne J. Earl

πŸ› -
Wchenghui

πŸ› +
Wchenghui

πŸ›
Will Winder

πŸ›
William Brockhus

πŸ’» πŸ›
Wilson Kurniawan

πŸ›
Wim Deblauwe

πŸ›
Woongsik Choi

πŸ›
XenoAmess

πŸ’» πŸ› -
Yang

πŸ’» +
Yang

πŸ’»
YaroslavTER

πŸ›
Young Chan

πŸ’» πŸ›
YuJin Kim

πŸ›
Yuri Dolzhenko

πŸ›
Yurii Dubinka

πŸ›
Zoltan Farkas

πŸ› -
Zustin

πŸ› +
Zustin

πŸ›
aaronhurst-google

πŸ›
alexmodis

πŸ›
andreoss

πŸ›
andrey81inmd

πŸ’» πŸ›
anicoara

πŸ›
arunprasathav

πŸ› -
asiercamara

πŸ› +
asiercamara

πŸ›
astillich-igniti

πŸ’»
avesolovksyy

πŸ›
avishvat

πŸ›
avivmu

πŸ›
axelbarfod1

πŸ›
b-3-n

πŸ› -
balbhadra9

πŸ› +
balbhadra9

πŸ›
base23de

πŸ›
bergander

πŸ›
berkam

πŸ’» πŸ›
breizh31

πŸ›
caesarkim

πŸ›
carolyujing

πŸ› -
cesares-basilico

πŸ› +
cesares-basilico

πŸ›
chrite

πŸ›
cobratbq

πŸ›
coladict

πŸ›
cosmoJFH

πŸ›
cristalp

πŸ›
crunsk

πŸ› -
cwholmes

πŸ› +
cwholmes

πŸ›
cyberjj999

πŸ›
cyw3

πŸ›
d1ss0nanz

πŸ›
danbrycefairsailcom

πŸ›
dariansanity

πŸ›
darrenmiliband

πŸ› -
davidburstrom

πŸ› +
davidburstrom

πŸ›
dbirkman-paloalto

πŸ›
deepak-patra

πŸ›
dependabot[bot]

πŸ’» πŸ›
dinesh150

πŸ›
diziaq

πŸ›
dreaminpast123

πŸ› -
duanyanan

πŸ› +
duanyanan

πŸ›
dutt-sanjay

πŸ›
dylanleung

πŸ›
dzeigler

πŸ›
ekkirala

πŸ›
emersonmoura

πŸ›
fairy

πŸ› -
filiprafalowicz

πŸ’» +
filiprafalowicz

πŸ’»
foxmason

πŸ›
frankegabor

πŸ›
frankl

πŸ›
freafrea

πŸ›
fsapatin

πŸ›
gracia19

πŸ› -
guo fei

πŸ› +
guo fei

πŸ›
gurmsc5

πŸ›
gwilymatgearset

πŸ’» πŸ›
haigsn

πŸ›
hemanshu070

πŸ›
henrik242

πŸ›
hongpuwu

πŸ› -
hvbtup

πŸ’» πŸ› +
hvbtup

πŸ’» πŸ›
igniti GmbH

πŸ›
ilovezfs

πŸ›
itaigilo

πŸ›
jakivey32

πŸ›
jbennett2091

πŸ›
jcamerin

πŸ› -
jkeener1

πŸ› +
jkeener1

πŸ›
jmetertea

πŸ›
johnra2

πŸ’»
josemanuelrolon

πŸ’» πŸ›
kabroxiko

πŸ’» πŸ›
karwer

πŸ›
kaulonline

πŸ› -
kdaemonv

πŸ› +
kdaemonv

πŸ›
kenji21

πŸ’» πŸ›
kfranic

πŸ›
khalidkh

πŸ›
krzyk

πŸ›
lasselindqvist

πŸ›
lihuaib

πŸ› -
lonelyma1021

πŸ› +
lonelyma1021

πŸ›
lpeddy

πŸ›
lujiefsi

πŸ’» +
lukelukes

πŸ’»
lyriccoder

πŸ›
marcelmore

πŸ›
matchbox

πŸ› -
matthiaskraaz

πŸ› -
meandonlyme

πŸ› +
matthiaskraaz

πŸ› +
meandonlyme

πŸ›
mikesive

πŸ›
milossesic

πŸ›
mriddell95

πŸ›
mrlzh

πŸ›
msloan

πŸ› -
mucharlaravalika

πŸ› -
mvenneman

πŸ› +
mucharlaravalika

πŸ› +
mvenneman

πŸ›
nareshl119

πŸ›
nicolas-harraudeau-sonarsource

πŸ›
noerremark

πŸ›
novsirion

πŸ›
oggboy

πŸ› -
oinume

πŸ› -
orimarko

πŸ’» πŸ› +
oinume

πŸ› +
orimarko

πŸ’» πŸ›
pallavi agarwal

πŸ›
parksungrin

πŸ›
patpatpat123

πŸ›
patriksevallius

πŸ›
pbrajesh1

πŸ› -
phoenix384

πŸ› -
piotrszymanski-sc

πŸ’» +
phoenix384

πŸ› +
piotrszymanski-sc

πŸ’»
plan3d

πŸ›
poojasix

πŸ›
prabhushrikant

πŸ›
pujitha8783

πŸ›
r-r-a-j

πŸ› -
raghujayjunk

πŸ› -
rajeshveera

πŸ› +
raghujayjunk

πŸ› +
rajeshveera

πŸ›
rajeswarreddy88

πŸ›
recdevs

πŸ›
reudismam

πŸ’» πŸ›
rijkt

πŸ›
rillig-tk

πŸ› -
rmohan20

πŸ’» πŸ› -
rxmicro

πŸ› +
rmohan20

πŸ’» πŸ› +
rxmicro

πŸ›
ryan-gustafson

πŸ’» πŸ›
sabi0

πŸ›
scais

πŸ›
sebbASF

πŸ›
sergeygorbaty

πŸ’» -
shilko2013

πŸ› -
simeonKondr

πŸ› +
shilko2013

πŸ› +
simeonKondr

πŸ›
snajberk

πŸ›
sniperrifle2004

πŸ›
snuyanzin

πŸ› πŸ’»
sratz

πŸ›
stonio

πŸ› -
sturton

πŸ’» πŸ› -
sudharmohan

πŸ› +
sturton

πŸ’» πŸ› +
sudharmohan

πŸ›
suruchidawar

πŸ›
svenfinitiv

πŸ›
tashiscool

πŸ›
test-git-hook

πŸ›
testation21

πŸ’» πŸ› -
thanosa

πŸ› -
tiandiyixian

πŸ› +
thanosa

πŸ› +
tiandiyixian

πŸ›
tobwoerk

πŸ›
tprouvot

πŸ›
trentchilders

πŸ›
triandicAnt

πŸ›
trishul14

πŸ› -
tsui

πŸ› -
winhkey

πŸ› +
tsui

πŸ› +
winhkey

πŸ›
witherspore

πŸ›
wjljack

πŸ›
wuchiuwong

πŸ›
xingsong

πŸ›
xioayuge

πŸ› -
xnYi9wRezm

πŸ’» πŸ› -
xuanuy

πŸ› +
xnYi9wRezm

πŸ’» πŸ› +
xuanuy

πŸ›
xyf0921

πŸ›
yalechen-cyw3

πŸ›
yasuharu-sato

πŸ›
zenglian

πŸ›
zgrzyt93

πŸ’» πŸ› -
zh3ng

πŸ› -
zt_soft

πŸ› +
zh3ng

πŸ› +
zt_soft

πŸ›
ztt79

πŸ›
zzzzfeng

πŸ›
ÁrpÑd MagosÑnyi

πŸ› diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index 0a27287864..69a29d966d 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -76,14 +76,14 @@ The tool comes with a rather extensive help text, simply running with `--help`! %} {% include custom/cli_option_row.html options="--file-list" option_arg="filepath" - description="Path to file containing a comma delimited list of files to analyze. + description="Path to file containing a list of files to analyze, one path per line. If this is given, then you don't need to provide `--dir`." %} {% include custom/cli_option_row.html options="--force-language" option_arg="lang" description="Force a language to be used for all input files, irrespective of - filenames. When using this option, the automatic language selection - by extension is disabled and all files are tried to be parsed with + file names. When using this option, the automatic language selection + by extension is disabled and PMD tries to parse all files with the given language `<lang>`. Parsing errors are ignored and unparsable files are skipped. @@ -92,9 +92,9 @@ The tool comes with a rather extensive help text, simply running with `--help`! %} {% include custom/cli_option_row.html options="--ignore-list" option_arg="filepath" - description="Path to file containing a comma delimited list of files to ignore. + description="Path to file containing a list of files to ignore, one path per line. This option can be combined with `--dir` and `--file-list`. - This ignore list takes precedence over any files in the filelist." + This ignore list takes precedence over any files in the file-list." %} {% include custom/cli_option_row.html options="--help,-h,-H" description="Display help on usage." diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..2c7c511a92 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,11 +14,42 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy +#### CLI improvements + +The PMD CLI now allows repeating the `--dir` (`-d`) and `--rulesets` (`-R`) options, + as well as providing several space-separated arguments to either of them. For instance: +```shell +pmd -d src/main/java src/test/java -R rset1.xml -R rset2.xml +``` +This also allows globs to be used on the CLI if your shell supports shell expansion. +For instance, the above can be written +```shell +pmd -d src/*/java -R rset*.xml +``` +Please use theses new forms instead of using comma-separated lists as argument to these options. + ### Fixed Issues +* cli + * [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters +* html + * [#3955](https://github.com/pmd/pmd/pull/3955): \[html] Improvements for handling text and comment nodes +* java-design + * [#3874](https://github.com/pmd/pmd/issues/3874): \[java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito) +* javascript + * [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal + ### API Changes +#### Deprecated API + +- {% jdoc core::PMDConfiguration#getInputPaths() %} and +{% jdoc core::PMDConfiguration#setInputPaths(java.lang.String) %} are now deprecated. +A new set of methods have been added, which use lists and do not rely on comma splitting. + ### External Contributions +* [#3964](https://github.com/pmd/pmd/pull/3964): \[java] Fix #3874 - ImmutableField: fix mockito/spring false positives - [@lukelukes](https://github.com/lukelukes) + {% endtocmaker %} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 9d3c67aead..ad3b9603c1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Properties; @@ -102,7 +103,7 @@ public class PMDConfiguration extends AbstractConfiguration { // Rule and source file options private List ruleSets = new ArrayList<>(); private RulePriority minimumPriority = RulePriority.LOW; - private String inputPaths; + private List inputPaths = new ArrayList<>(); private String inputUri; private String inputFilePath; private String ignoreFilePath; @@ -420,19 +421,54 @@ public class PMDConfiguration extends AbstractConfiguration { * Get the comma separated list of input paths to process for source files. * * @return A comma separated list. + * + * @deprecated Use {@link #getAllInputPaths()} */ + @Deprecated public String getInputPaths() { - return inputPaths; + return inputPaths.isEmpty() ? null : StringUtils.join(inputPaths, ","); + } + + /** + * Returns an unmodifiable list. + * + * @throws NullPointerException If the parameter is null + */ + public List getAllInputPaths() { + return Collections.unmodifiableList(inputPaths); } /** * Set the comma separated list of input paths to process for source files. * - * @param inputPaths - * The comma separated list. + * @param inputPaths The comma separated list. + * + * @throws NullPointerException If the parameter is null + * @deprecated Use {@link #setInputPaths(List)} or {@link #addInputPath(String)} */ + @Deprecated public void setInputPaths(String inputPaths) { - this.inputPaths = inputPaths; + List paths = new ArrayList<>(); + Collections.addAll(paths, inputPaths.split(",")); + this.inputPaths = paths; + } + + /** + * Set the input paths to the given list of paths. + * @throws NullPointerException If the parameter is null + */ + public void setInputPaths(List inputPaths) { + this.inputPaths = new ArrayList<>(inputPaths); + } + + /** + * Add an input path. It is not split on commas. + * + * @throws NullPointerException If the parameter is null + */ + public void addInputPath(String inputPath) { + Objects.requireNonNull(inputPath); + this.inputPaths.add(inputPath); } public String getInputFilePath() { @@ -526,7 +562,7 @@ public class PMDConfiguration extends AbstractConfiguration { Renderer renderer = RendererFactory.createRenderer(reportFormat, reportProperties); renderer.setShowSuppressedViolations(showSuppressedViolations); if (reportShortNames && inputPaths != null) { - renderer.setUseShortNames(Arrays.asList(inputPaths.split(","))); + renderer.setUseShortNames(Collections.unmodifiableList(new ArrayList<>(inputPaths))); } if (withReportWriter) { renderer.setReportFile(reportFile); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index f80dad49bd..18c1da8a28 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -8,6 +8,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.RulePriority; @@ -28,20 +30,44 @@ import com.beust.jcommander.validators.PositiveInteger; @InternalApi public class PMDParameters { - @Parameter(names = { "--rulesets", "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", - required = true) - private String rulesets; + @Parameter(names = { "--rulesets", "-rulesets", "-R" }, + description = "Path to a ruleset xml file. " + + "The path may reference a resource on the classpath of the application, be a local file system path, or a URL. " + + "The option can be repeated, and multiple arguments can be provided to a single occurrence of the option.", + required = true, + variableArity = true) + private List rulesets; - @Parameter(names = { "--uri", "-uri", "-u" }, description = "Database URI for sources.") + @Parameter(names = { "--uri", "-uri", "-u" }, + description = "Database URI for sources. " + + "One of --dir, --file-list or --uri must be provided. " + ) private String uri; - @Parameter(names = { "--dir", "-dir", "-d" }, description = "Root directory for sources.") - private String sourceDir; + @Parameter(names = { "--dir", "-dir", "-d" }, + description = "Path to a source file, or directory containing source files to analyze. " + // About the following line: + // In PMD 6, this is only the case for files found in directories. If you + // specify a file directly, and it is unknown, then the Java parser is used. + + "Note that a file is only effectively added if it matches a language known by PMD. " + + "Zip and Jar files are also supported, if they are specified directly " + + "(archive files found while exploring a directory are not recursively expanded). " + + "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. " + + "One of --dir, --file-list or --uri must be provided. ", + variableArity = true) + private List inputPaths = new ArrayList<>(); - @Parameter(names = { "--file-list", "-filelist" }, description = "Path to a file containing a list of files to analyze.") + @Parameter(names = { "--file-list", "-filelist" }, + description = + "Path to a file containing a list of files to analyze, one path per line. " + + "One of --dir, --file-list or --uri must be provided. " + ) private String fileListPath; - @Parameter(names = { "--ignore-list", "-ignorelist" }, description = "Path to a file containing a list of files to ignore.") + @Parameter(names = { "--ignore-list", "-ignorelist" }, + description = "Path to a file containing a list of files to exclude from the analysis, one path per line. " + + "This option can be combined with --dir and --file-list. " + ) private String ignoreListPath; @Parameter(names = { "--format", "-format", "-f" }, description = "Report format type.") @@ -103,12 +129,17 @@ public class PMDParameters { @Parameter(names = { "-language", "-l" }, description = "Specify a language PMD should use.") private String language = null; - @Parameter(names = { "--force-language", "-force-language" }, description = "Force a language to be used for all input files, irrespective of filenames.") + @Parameter(names = { "--force-language", "-force-language" }, + description = "Force a language to be used for all input files, irrespective of file names. " + + "When using this option, the automatic language selection by extension is disabled, and PMD " + + "tries to parse all input files with the given language's parser. " + + "Parsing errors are ignored." + ) private String forceLanguage = null; @Parameter(names = { "--aux-classpath", "-auxclasspath" }, description = "Specifies the classpath for libraries used by the source code. " - + "This is used by the type resolution. The platform specific path delimiter " + + "This is used to resolve types in Java source files. The platform specific path delimiter " + "(\":\" on Linux, \";\" on Windows) is used to separate the entries. " + "Alternatively, a single 'file:' URL to a text file containing path elements on consecutive lines " + "can be specified.") @@ -192,10 +223,6 @@ public class PMDParameters { * @throws IllegalArgumentException if the parameters are inconsistent or incomplete */ public PMDConfiguration toConfiguration() { - if (null == this.getSourceDir() && null == this.getUri() && null == this.getFileListPath()) { - throw new IllegalArgumentException( - "Please provide a parameter for source root directory (-dir or -d), database URI (-uri or -u), or file list path (-filelist)."); - } PMDConfiguration configuration = new PMDConfiguration(); configuration.setInputPaths(this.getSourceDir()); configuration.setInputFilePath(this.getFileListPath()); @@ -329,12 +356,22 @@ public class PMDParameters { return auxclasspath; } + @Deprecated public String getRulesets() { + return StringUtils.join(rulesets, ","); + } + + public List getRulesetRefs() { return rulesets; } + public List getInputPaths() { + return inputPaths; + } + + @Deprecated public String getSourceDir() { - return sourceDir; + return StringUtils.join(inputPaths, ","); } public String getFileListPath() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java index 0ffc8465d0..2f863ba4d9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java @@ -105,13 +105,29 @@ public final class PmdParametersParseResult { jcommander.setProgramName("pmd"); try { - jcommander.parse(args); + parseAndValidate(jcommander, result, args); return new PmdParametersParseResult(result, filterDeprecatedOptions(args)); } catch (ParameterException e) { return new PmdParametersParseResult(e, filterDeprecatedOptions(args)); } } + private static void parseAndValidate(JCommander jcommander, PMDParameters result, String[] args) { + jcommander.parse(args); + if (result.isHelp() || result.isVersion()) { + return; + } + // jcommander has no special support for global parameter validation like this + // For consistency we report this with a ParameterException + if (result.getInputPaths().isEmpty() + && null == result.getUri() + && null == result.getFileListPath()) { + throw new ParameterException( + "Please provide a parameter for source root directory (--dir or -d), database URI (--uri or -u), or file list path (--file-list)."); + } + + } + private static Map filterDeprecatedOptions(String... args) { Map argSet = new LinkedHashMap<>(SUGGESTED_REPLACEMENT); argSet.keySet().retainAll(new HashSet<>(Arrays.asList(args))); 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 e9341542ea..4568a28d35 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 @@ -61,7 +61,7 @@ public class ElementNode extends BaseNodeInfo implements AstNodeOwner { Node node, int siblingPosition, NamePool namePool) { - super(Type.ELEMENT, namePool, node.getXPathNodeName(), parent); + super(determineType(node), namePool, node.getXPathNodeName(), parent); this.document = document; this.parent = parent; @@ -80,6 +80,19 @@ public class ElementNode extends BaseNodeInfo implements AstNodeOwner { document.nodeToElementNode.put(node, this); } + private static int determineType(Node node) { + // As of PMD 6.48.0, only the experimental HTML module uses this naming + // convention to identify non-element nodes. + // TODO PMD 7: maybe generalize this to other languages + String name = node.getXPathNodeName(); + if ("#text".equals(name)) { + return Type.TEXT; + } else if ("#comment".equals(name)) { + return Type.COMMENT; + } + return Type.ELEMENT; + } + private Map getAttributes() { if (attributes == null) { attributes = new HashMap<>(); @@ -147,10 +160,18 @@ public class ElementNode extends BaseNodeInfo implements AstNodeOwner { } @Override - public CharSequence getStringValueCS() { + public String getStringValue() { + if (getNodeKind() == Type.TEXT || getNodeKind() == Type.COMMENT) { + return getUnderlyingNode().getImage(); + } return ""; } + @Override + public CharSequence getStringValueCS() { + return getStringValue(); + } + @Override public int compareOrder(NodeInfo other) { int result; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java index 157caf975d..70c140023e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java @@ -64,7 +64,7 @@ public class CoreCliTest { // create a few files srcDir = Files.createDirectories(root.resolve("src")); writeString(srcDir.resolve("someSource.dummy"), "dummy text"); - + // reset logger? Logger.getLogger("net.sourceforge.pmd"); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java index 22ca614a06..40ff67955e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java @@ -4,10 +4,17 @@ package net.sourceforge.pmd.cli; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.Assert; import org.junit.Test; +import net.sourceforge.pmd.PMDConfiguration; + public class PMDParametersTest { @Test @@ -20,4 +27,60 @@ public class PMDParametersTest { FieldUtils.writeDeclaredField(parameters, "language", "dummy2", true); Assert.assertEquals("1.0", parameters.getVersion()); } + + @Test + public void testMultipleDirsAndRuleSets() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a", "b", "-R", "x.xml", "y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testMultipleDirsAndRuleSetsWithCommas() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a,b", "-R", "x.xml,y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testMultipleDirsAndRuleSetsWithRepeatedOption() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a", "-d", "b", "-R", "x.xml", "-R", "y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testNoPositionalParametersAllowed() { + assertError( + // vvvv + "-R", "x.xml", "-d", "a", "--", "-d", "b" + ); + } + + + private void assertMultipleDirsAndRulesets(PmdParametersParseResult result) { + assertFalse(result.isError()); + PMDConfiguration config = result.toConfiguration(); + assertEquals(config.getAllInputPaths(), listOf("a", "b")); + assertEquals(config.getRuleSetPaths(), listOf("x.xml", "y.xml")); + } + + @Test + public void testEmptyDirOption() { + assertError("-d", "-R", "y.xml"); + } + + @Test + public void testEmptyRulesetOption() { + assertError("-R", "-d", "something"); + } + + private void assertError(String... params) { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters(params); + assertTrue(result.isError()); + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java index ead5c73553..756e41eb46 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/saxon/ElementNodeTest.java @@ -11,6 +11,8 @@ import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode; import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode; +import net.sf.saxon.type.Type; + public class ElementNodeTest { @Test @@ -56,4 +58,32 @@ public class ElementNodeTest { Assert.assertTrue(elementFoo1.compareOrder(elementFoo2) < 0); Assert.assertTrue(elementFoo2.compareOrder(elementFoo1) > 0); } + + @Test + public void verifyTextNodeType() { + DummyNode node = new DummyNode(1, false, "dummy"); + DummyNode foo1 = new DummyNode(2, false, "foo"); + DummyNode foo2 = new DummyNode(2, false, "#text"); + node.jjtAddChild(foo1, 0); + node.jjtAddChild(foo2, 1); + + DocumentNode document = new DocumentNode(node); + ElementNode elementFoo1 = document.nodeToElementNode.get(foo1); + ElementNode elementFoo2 = document.nodeToElementNode.get(foo2); + + Assert.assertEquals(Type.ELEMENT, elementFoo1.getNodeKind()); + Assert.assertEquals(Type.TEXT, elementFoo2.getNodeKind()); + } + + @Test + public void verifyCommentNodeType() { + DummyNode node = new DummyNode(1, false, "dummy"); + DummyNode foo = new DummyNode(2, false, "#comment"); + node.jjtAddChild(foo, 0); + + DocumentNode document = new DocumentNode(node); + ElementNode elementFoo = document.nodeToElementNode.get(foo); + + Assert.assertEquals(Type.COMMENT, elementFoo.getNodeKind()); + } } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 48c9bc3ee3..fdb2a97706 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -36,6 +36,8 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { } } + private final String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); + @Test public void testFileExistence() { assertTrue(getBinaryDistribution().exists()); @@ -75,27 +77,34 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { } @Test - public void runPMD() throws Exception { - String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); + public void testPmdJavaQuickstart() throws Exception { + ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "rulesets/java/quickstart.xml"); + result.assertExecutionResult(4, ""); + } - ExecutionResult result; - - result = PMDExecutor.runPMD(tempDir); // without any argument, display usage help and error - result.assertExecutionResultErrOutput(1, CliMessages.runWithHelpFlagMessage()); - - result = PMDExecutor.runPMD(tempDir, "-h"); - result.assertExecutionResult(0, SUPPORTED_LANGUAGES_PMD); - - result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); - result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:"); - - // also test XML format - result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml"); + @Test + public void testPmdXmlFormat() throws Exception { + ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml"); result.assertExecutionResult(4, "", "JumbledIncrementer.java\">"); result.assertExecutionResult(4, "", "\n" + "

Hello, { greeting}!

\n" - + " " + + " \n" + "
\n" - + " \n" + + " \n" + " \n" + "

Uppercased Full Name: {uppercasedFullName}

\n" + "
\n" + + " \n" + ""; @Test public void selectTextNode() { // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter // "Don’t add spaces around the property, for example, { data } is not valid HTML." - String xpath = "//*[local-name() = '#text'][contains(@Text, '{ ')]"; + String xpath = "//text()[contains(., '{ ')]"; Report report = runXPath(LIGHTNING_WEB_COMPONENT, xpath); Assert.assertEquals(1, report.getViolations().size()); Assert.assertEquals(3, report.getViolations().get(0).getBeginLine()); } + @Test + public void selectTextNodeByNodeNameShouldNotWork() { + String xpath = "//*[local-name() = '#text']"; + Report report = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(0, report.getViolations().size()); + } + + @Test + public void verifyTextNodeName() { + ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("

foobar

"); + ASTHtmlTextNode textNode = document.getFirstDescendantOfType(ASTHtmlTextNode.class); + Assert.assertEquals("#text", textNode.getXPathNodeName()); + } + + @Test + public void verifyCommentNodeName() { + ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("

"); + ASTHtmlComment comment = document.getFirstDescendantOfType(ASTHtmlComment.class); + Assert.assertEquals("#comment", comment.getXPathNodeName()); + } + @Test public void selectAttributes() { // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter @@ -54,17 +74,31 @@ public class HtmlXPathRuleTest { Assert.assertEquals(4, report.getViolations().get(0).getBeginLine()); } - private Report runXPath(String html, String xpath) { - LanguageVersion htmlLanguage = LanguageRegistry.findLanguageByTerseName(HtmlLanguageModule.TERSE_NAME).getDefaultVersion(); - Parser parser = htmlLanguage.getLanguageVersionHandler().getParser(htmlLanguage.getLanguageVersionHandler().getDefaultParserOptions()); + @Test + public void selectAttributesMultiple() { + // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter + // "Don’t add spaces around the property, for example, { data } is not valid HTML." + String xpath = "//*[@*[local-name() = ('value', 'onchange')] = '{']"; + Report report = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(2, report.getViolations().size()); + Assert.assertEquals(4, report.getViolations().get(0).getBeginLine()); + Assert.assertEquals(6, report.getViolations().get(1).getBeginLine()); + } + + @Test + public void selectAttributeByName() { + String xpath = "//*[@*[local-name() = 'if:true']]"; + + Report report = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(1, report.getViolations().size()); + Assert.assertEquals(10, report.getViolations().get(0).getBeginLine()); + } + + private Report runXPath(String html, String xpath) { XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, xpath); rule.setMessage("test"); - Node node = parser.parse("n/a", new StringReader(html)); - RuleContext context = new RuleContext(); - context.setLanguageVersion(htmlLanguage); - context.setCurrentRule(rule); - rule.apply(Arrays.asList(node), context); - return context.getReport(); + rule.setLanguage(HtmlParsingHelper.DEFAULT.getLanguage()); + return HtmlParsingHelper.DEFAULT.executeRule(rule, html); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java index 76fb6639f1..69baff8219 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.rule.design; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -41,6 +43,17 @@ public class ImmutableFieldRule extends AbstractLombokAwareRule { CHECKDECL } + @Override + protected Collection defaultSuppressionAnnotations() { + Collection defaultValues = new ArrayList<>(super.defaultSuppressionAnnotations()); + defaultValues.add("org.mockito.Mock"); + defaultValues.add("org.mockito.InjectMocks"); + defaultValues.add("org.springframework.beans.factory.annotation.Autowired"); + defaultValues.add("org.springframework.boot.test.mock.mockito.MockBean"); + + return defaultValues; + } + @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { Object result = super.visit(node, data); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml index 359a1ce214..b6f6bfb687 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml @@ -572,4 +572,35 @@ public class ExampleImmutableField { } ]]> + + + #3874 [java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito) + 0 + + diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractInfixEcmascriptNode.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractInfixEcmascriptNode.java index 59d2e3d861..0ec8dd04b6 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractInfixEcmascriptNode.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractInfixEcmascriptNode.java @@ -27,7 +27,7 @@ public class AbstractInfixEcmascriptNode extends Abst if (setImage) { if (infixExpression.getOperator() == Token.ASSIGN_BITXOR) { super.setImage("^="); - } else { + } else if (infixExpression.getOperator() != Token.METHOD) { super.setImage(AstRoot.operatorToString(infixExpression.getOperator())); } } diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsTreeDumpTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsTreeDumpTest.java index 849cc1f657..69bc444437 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsTreeDumpTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsTreeDumpTest.java @@ -40,4 +40,10 @@ public class JsTreeDumpTest extends BaseTreeDumpTest { public void templateStrings() { doTest("templateStrings"); } + + @Test + public void issue3948() { + // https://github.com/pmd/pmd/issues/3948 + doTest("issue3948"); + } } diff --git a/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.js b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.js new file mode 100644 index 0000000000..b3e5490860 --- /dev/null +++ b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.js @@ -0,0 +1,3 @@ +x = { + init() { } +}; diff --git a/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.txt b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.txt new file mode 100644 index 0000000000..897cbce9ae --- /dev/null +++ b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/issue3948.txt @@ -0,0 +1,9 @@ ++- AstRoot + +- ExpressionStatement + +- Assignment + +- Name + +- ObjectLiteral + +- ObjectProperty + +- Name + +- FunctionNode + +- Block