diff --git a/.all-contributorsrc b/.all-contributorsrc index dcac68bf60..6efd00a779 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -6639,6 +6639,42 @@ "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" + ] + }, + { + "login": "Ramel0921", + "name": "Ramel0921", + "avatar_url": "https://avatars.githubusercontent.com/u/104978096?v=4", + "profile": "https://github.com/Ramel0921", + "contributions": [ + "bug" + ] + }, + { + "login": "flyhard", + "name": "Per Abich", + "avatar_url": "https://avatars.githubusercontent.com/u/409466?v=4", + "profile": "https://github.com/flyhard", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, 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 392dce14e8..cf6d0805c6 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -540,410 +540,416 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Pedro Nuno Santos

๐Ÿ›
Pedro Rijo

๐Ÿ›
Pelisse Romain

๐Ÿ’ป ๐Ÿ“– ๐Ÿ› +
Per Abich

๐Ÿ’ป
Pete Davids

๐Ÿ›
Peter Bruin

๐Ÿ› -
Peter Chittum

๐Ÿ’ป ๐Ÿ› +
Peter Chittum

๐Ÿ’ป ๐Ÿ›
Peter Cudmore

๐Ÿ›
Peter Kasson

๐Ÿ›
Peter Kofler

๐Ÿ›
Pham Hai Trung

๐Ÿ›
Philip Graf

๐Ÿ’ป ๐Ÿ›
Philip Hachey

๐Ÿ› -
Philippe Ozil

๐Ÿ› +
Philippe Ozil

๐Ÿ›
Phinehas Artemix

๐Ÿ›
Phokham Nonava

๐Ÿ›
Piotr Szymaล„ski

๐Ÿ›
Piotrek ลปygieล‚o

๐Ÿ’ป ๐Ÿ›
Pranay Jaiswal

๐Ÿ›
Prasad Kamath

๐Ÿ› -
Prasanna

๐Ÿ› +
Prasanna

๐Ÿ›
Presh-AR

๐Ÿ›
Puneet1726

๐Ÿ›
Rafael Cortรชs

๐Ÿ›
RaheemShaik999

๐Ÿ›
RajeshR

๐Ÿ’ป ๐Ÿ›
Ramachandra Mohan

๐Ÿ› -
Raquel Pau

๐Ÿ› +
Ramel0921

๐Ÿ› +
Raquel Pau

๐Ÿ›
Ravikiran Janardhana

๐Ÿ›
Reda Benhemmouche

๐Ÿ›
Renato Oliveira

๐Ÿ’ป ๐Ÿ›
Rich DiCroce

๐Ÿ›
Riot R1cket

๐Ÿ› -
Rishabh Jain

๐Ÿ› -
RishabhDeep Singh

๐Ÿ› +
Rishabh Jain

๐Ÿ› +
RishabhDeep Singh

๐Ÿ›
Robbie Martinus

๐Ÿ’ป ๐Ÿ›
Robert Henry

๐Ÿ›
Robert Painsi

๐Ÿ›
Robert Russell

๐Ÿ›
Robert Sรถsemann

๐Ÿ’ป ๐Ÿ“– ๐Ÿ“ข ๐Ÿ› -
Robert Whitebit

๐Ÿ› -
Robin Richtsfeld

๐Ÿ› +
Robert Whitebit

๐Ÿ› +
Robin Richtsfeld

๐Ÿ›
Robin Stocker

๐Ÿ’ป ๐Ÿ›
Robin Wils

๐Ÿ›
RochusOest

๐Ÿ›
Rodolfo Noviski

๐Ÿ›
Rodrigo Casara

๐Ÿ› -
Rodrigo Fernandes

๐Ÿ› -
Roman Salvador

๐Ÿ’ป ๐Ÿ› +
Rodrigo Fernandes

๐Ÿ› +
Roman Salvador

๐Ÿ’ป ๐Ÿ›
Ronald Blaschke

๐Ÿ›
Rรณbert Papp

๐Ÿ›
Saikat Sengupta

๐Ÿ›
Saksham Handu

๐Ÿ›
Saladoc

๐Ÿ› -
Salesforce Bob Lightning

๐Ÿ› -
Sam Carlberg

๐Ÿ› +
Salesforce Bob Lightning

๐Ÿ› +
Sam Carlberg

๐Ÿ›
Satoshi Kubo

๐Ÿ›
Scott Kennedy

๐Ÿ›
Scott Wells

๐Ÿ› ๐Ÿ’ป
Scrsloota

๐Ÿ’ป
Sebastian Bรถgl

๐Ÿ› -
Sebastian Schuberth

๐Ÿ› -
Sebastian Schwarz

๐Ÿ› +
Sebastian Schuberth

๐Ÿ› +
Sebastian Schwarz

๐Ÿ›
Sergey Gorbaty

๐Ÿ›
Sergey Kozlov

๐Ÿ›
Sergey Yanzin

๐Ÿ’ป ๐Ÿ›
Seth Wilcox

๐Ÿ’ป
Shubham

๐Ÿ’ป ๐Ÿ› -
Simon Xiao

๐Ÿ› -
Srinivasan Venkatachalam

๐Ÿ› +
Simon Xiao

๐Ÿ› +
Srinivasan Venkatachalam

๐Ÿ›
Stanislav Gromov

๐Ÿ›
Stanislav Myachenkov

๐Ÿ’ป
Stefan Birkner

๐Ÿ›
Stefan Bohn

๐Ÿ›
Stefan Endrullis

๐Ÿ› -
Stefan Klรถss-Schuster

๐Ÿ› -
Stefan Wolf

๐Ÿ› +
Stefan Klรถss-Schuster

๐Ÿ› +
Stefan Wolf

๐Ÿ›
Stephan H. Wissel

๐Ÿ›
Stephen

๐Ÿ›
Stephen Friedrich

๐Ÿ›
Steve Babula

๐Ÿ’ป
Stexxe

๐Ÿ› -
Stian Lรฅgstad

๐Ÿ› -
StuartClayton5

๐Ÿ› +
Stian Lรฅgstad

๐Ÿ› +
StuartClayton5

๐Ÿ›
Supun Arunoda

๐Ÿ›
Suren Abrahamyan

๐Ÿ›
SwatiBGupta1110

๐Ÿ›
SyedThoufich

๐Ÿ›
Szymon Sasin

๐Ÿ› -
T-chuangxin

๐Ÿ› -
TERAI Atsuhiro

๐Ÿ› +
T-chuangxin

๐Ÿ› +
TERAI Atsuhiro

๐Ÿ›
TIOBE Software

๐Ÿ’ป ๐Ÿ›
Taylor Smock

๐Ÿ›
Techeira Damiรกn

๐Ÿ’ป ๐Ÿ›
Ted Husted

๐Ÿ›
TehBakker

๐Ÿ› -
The Gitter Badger

๐Ÿ› -
Theodoor

๐Ÿ› +
The Gitter Badger

๐Ÿ› +
Theodoor

๐Ÿ›
Thiago Henrique Hรผpner

๐Ÿ›
Thibault Meyer

๐Ÿ›
Thomas Gรผttler

๐Ÿ›
Thomas Jones-Low

๐Ÿ›
Thomas Smith

๐Ÿ’ป ๐Ÿ› -
ThrawnCA

๐Ÿ› -
Thunderforge

๐Ÿ’ป ๐Ÿ› +
ThrawnCA

๐Ÿ› +
Thunderforge

๐Ÿ’ป ๐Ÿ›
Tim van der Lippe

๐Ÿ›
Tobias Weimer

๐Ÿ’ป ๐Ÿ›
Tom Daly

๐Ÿ›
Tomer Figenblat

๐Ÿ›
Tomi De Lucca

๐Ÿ’ป ๐Ÿ› -
Torsten Kleiber

๐Ÿ› -
TrackerSB

๐Ÿ› +
Torsten Kleiber

๐Ÿ› +
TrackerSB

๐Ÿ›
Ullrich Hafner

๐Ÿ›
Utku Cuhadaroglu

๐Ÿ’ป ๐Ÿ›
Valentin Brandl

๐Ÿ›
Valeria

๐Ÿ›
Vasily Anisimov

๐Ÿ› -
Vickenty Fesunov

๐Ÿ› -
Victor Noรซl

๐Ÿ› +
Vibhor Goyal

๐Ÿ› +
Vickenty Fesunov

๐Ÿ› +
Victor Noรซl

๐Ÿ›
Vincent Galloy

๐Ÿ’ป
Vincent HUYNH

๐Ÿ›
Vincent Maurin

๐Ÿ›
Vincent Privat

๐Ÿ› + +
Vishhwas

๐Ÿ›
Vitaly

๐Ÿ›
Vitaly Polonetsky

๐Ÿ› - -
Vojtech Polivka

๐Ÿ›
Vsevolod Zholobov

๐Ÿ›
Vyom Yadav

๐Ÿ’ป
Wang Shidong

๐Ÿ› + +
Waqas Ahmed

๐Ÿ›
Wayne J. Earl

๐Ÿ›
Wchenghui

๐Ÿ› - -
Will Winder

๐Ÿ›
William Brockhus

๐Ÿ’ป ๐Ÿ›
Wilson Kurniawan

๐Ÿ›
Wim Deblauwe

๐Ÿ› + +
Woongsik Choi

๐Ÿ›
XenoAmess

๐Ÿ’ป ๐Ÿ›
Yang

๐Ÿ’ป - -
YaroslavTER

๐Ÿ›
Young Chan

๐Ÿ’ป ๐Ÿ›
YuJin Kim

๐Ÿ›
Yuri Dolzhenko

๐Ÿ› + +
Yurii Dubinka

๐Ÿ›
Zoltan Farkas

๐Ÿ›
Zustin

๐Ÿ› - -
aaronhurst-google

๐Ÿ›
alexmodis

๐Ÿ›
andreoss

๐Ÿ›
andrey81inmd

๐Ÿ’ป ๐Ÿ› + +
anicoara

๐Ÿ›
arunprasathav

๐Ÿ›
asiercamara

๐Ÿ› - -
astillich-igniti

๐Ÿ’ป
avesolovksyy

๐Ÿ›
avishvat

๐Ÿ›
avivmu

๐Ÿ› + +
axelbarfod1

๐Ÿ›
b-3-n

๐Ÿ›
balbhadra9

๐Ÿ› - -
base23de

๐Ÿ›
bergander

๐Ÿ›
berkam

๐Ÿ’ป ๐Ÿ›
breizh31

๐Ÿ› + +
caesarkim

๐Ÿ›
carolyujing

๐Ÿ›
cesares-basilico

๐Ÿ› - -
chrite

๐Ÿ›
cobratbq

๐Ÿ›
coladict

๐Ÿ›
cosmoJFH

๐Ÿ› + +
cristalp

๐Ÿ›
crunsk

๐Ÿ›
cwholmes

๐Ÿ› - -
cyberjj999

๐Ÿ›
cyw3

๐Ÿ›
d1ss0nanz

๐Ÿ›
danbrycefairsailcom

๐Ÿ› + +
dariansanity

๐Ÿ›
darrenmiliband

๐Ÿ›
davidburstrom

๐Ÿ› - -
dbirkman-paloalto

๐Ÿ›
deepak-patra

๐Ÿ›
dependabot[bot]

๐Ÿ’ป ๐Ÿ›
dinesh150

๐Ÿ› + +
diziaq

๐Ÿ›
dreaminpast123

๐Ÿ›
duanyanan

๐Ÿ› - -
dutt-sanjay

๐Ÿ›
dylanleung

๐Ÿ›
dzeigler

๐Ÿ›
ekkirala

๐Ÿ› + +
emersonmoura

๐Ÿ›
fairy

๐Ÿ›
filiprafalowicz

๐Ÿ’ป - -
foxmason

๐Ÿ›
frankegabor

๐Ÿ›
frankl

๐Ÿ›
freafrea

๐Ÿ› + +
fsapatin

๐Ÿ›
gracia19

๐Ÿ›
guo fei

๐Ÿ› - -
gurmsc5

๐Ÿ›
gwilymatgearset

๐Ÿ’ป ๐Ÿ›
haigsn

๐Ÿ›
hemanshu070

๐Ÿ› + +
henrik242

๐Ÿ›
hongpuwu

๐Ÿ›
hvbtup

๐Ÿ’ป ๐Ÿ› - -
igniti GmbH

๐Ÿ›
ilovezfs

๐Ÿ›
itaigilo

๐Ÿ›
jakivey32

๐Ÿ› + +
jbennett2091

๐Ÿ›
jcamerin

๐Ÿ›
jkeener1

๐Ÿ› - -
jmetertea

๐Ÿ›
johnra2

๐Ÿ’ป
josemanuelrolon

๐Ÿ’ป ๐Ÿ›
kabroxiko

๐Ÿ’ป ๐Ÿ› + +
karwer

๐Ÿ›
kaulonline

๐Ÿ›
kdaemonv

๐Ÿ› - -
kenji21

๐Ÿ’ป ๐Ÿ›
kfranic

๐Ÿ›
khalidkh

๐Ÿ›
krzyk

๐Ÿ› + +
lasselindqvist

๐Ÿ›
lihuaib

๐Ÿ›
lonelyma1021

๐Ÿ› - -
lpeddy

๐Ÿ›
lujiefsi

๐Ÿ’ป +
lukelukes

๐Ÿ’ป
lyriccoder

๐Ÿ› + +
marcelmore

๐Ÿ›
matchbox

๐Ÿ›
matthiaskraaz

๐Ÿ›
meandonlyme

๐Ÿ› - -
mikesive

๐Ÿ›
milossesic

๐Ÿ›
mriddell95

๐Ÿ› + +
mrlzh

๐Ÿ›
msloan

๐Ÿ›
mucharlaravalika

๐Ÿ›
mvenneman

๐Ÿ› - -
nareshl119

๐Ÿ›
nicolas-harraudeau-sonarsource

๐Ÿ›
noerremark

๐Ÿ› + +
novsirion

๐Ÿ›
oggboy

๐Ÿ›
oinume

๐Ÿ›
orimarko

๐Ÿ’ป ๐Ÿ› - -
pallavi agarwal

๐Ÿ›
parksungrin

๐Ÿ›
patpatpat123

๐Ÿ› + +
patriksevallius

๐Ÿ›
pbrajesh1

๐Ÿ›
phoenix384

๐Ÿ›
piotrszymanski-sc

๐Ÿ’ป - -
plan3d

๐Ÿ›
poojasix

๐Ÿ›
prabhushrikant

๐Ÿ› + +
pujitha8783

๐Ÿ›
r-r-a-j

๐Ÿ›
raghujayjunk

๐Ÿ›
rajeshveera

๐Ÿ› - -
rajeswarreddy88

๐Ÿ›
recdevs

๐Ÿ›
reudismam

๐Ÿ’ป ๐Ÿ› + +
rijkt

๐Ÿ›
rillig-tk

๐Ÿ›
rmohan20

๐Ÿ’ป ๐Ÿ›
rxmicro

๐Ÿ› - -
ryan-gustafson

๐Ÿ’ป ๐Ÿ›
sabi0

๐Ÿ›
scais

๐Ÿ› + +
sebbASF

๐Ÿ›
sergeygorbaty

๐Ÿ’ป
shilko2013

๐Ÿ›
simeonKondr

๐Ÿ› - -
snajberk

๐Ÿ›
sniperrifle2004

๐Ÿ›
snuyanzin

๐Ÿ› ๐Ÿ’ป + +
sratz

๐Ÿ›
stonio

๐Ÿ›
sturton

๐Ÿ’ป ๐Ÿ›
sudharmohan

๐Ÿ› - -
suruchidawar

๐Ÿ›
svenfinitiv

๐Ÿ›
tashiscool

๐Ÿ› + +
test-git-hook

๐Ÿ›
testation21

๐Ÿ’ป ๐Ÿ›
thanosa

๐Ÿ›
tiandiyixian

๐Ÿ› - -
tobwoerk

๐Ÿ›
tprouvot

๐Ÿ›
trentchilders

๐Ÿ› + +
triandicAnt

๐Ÿ›
trishul14

๐Ÿ›
tsui

๐Ÿ›
winhkey

๐Ÿ› - -
witherspore

๐Ÿ›
wjljack

๐Ÿ›
wuchiuwong

๐Ÿ› + +
xingsong

๐Ÿ›
xioayuge

๐Ÿ›
xnYi9wRezm

๐Ÿ’ป ๐Ÿ›
xuanuy

๐Ÿ› - -
xyf0921

๐Ÿ›
yalechen-cyw3

๐Ÿ›
yasuharu-sato

๐Ÿ› + +
zenglian

๐Ÿ›
zgrzyt93

๐Ÿ’ป ๐Ÿ›
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 ce4214b9fa..8c1f59df60 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/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index dc4577e41d..d909eb4f2f 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -112,9 +112,9 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt languages="Java" %} {% include custom/cli_option_row.html options="--ignore-annotations" - description="Ignore language annotations when comparing text" + description="Ignore language annotations (Java) or attributes (C#) when comparing text" default="false" - languages="Java" + languages="C#, Java" %} {% include custom/cli_option_row.html options="--ignore-literal-sequences" description="Ignore sequences of literals (common e.g. in list initializers)" diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 083d855a4b..b6720bbb0b 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -19,15 +19,63 @@ 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. + +#### C# Improvements + +When executing CPD on C# sources, the option `--ignore-annotations` is now supported as well. +It ignores C# attributes when detecting duplicated code. This option can also be enabled via +the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details. + ### Fixed Issues + * core * [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425) +* cli + * [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters +* cs (c#) + * [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) +* go + * [#2752](https://github.com/pmd/pmd/issues/2752): \[go] Error parsing unicode values +* html + * [#3955](https://github.com/pmd/pmd/pull/3955): \[html] Improvements for handling text and comment nodes +* java + * [#3423](https://github.com/pmd/pmd/issues/3423): \[java] Error processing identifiers with Unicode +* java-bestpractices + * [#3954](https://github.com/pmd/pmd/issues/3954): \[java] NPE in UseCollectionIsEmptyRule when .size() is called in a record +* 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 + * [#2605](https://github.com/pmd/pmd/issues/2605): \[js] Support unicode characters + * [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal +* python + * [#2604](https://github.com/pmd/pmd/issues/2604): \[python] Support unicode identifiers ### 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 +* [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard) +* [#3964](https://github.com/pmd/pmd/pull/3964): \[java] Fix #3874 - ImmutableField: fix mockito/spring false positives - [@lukelukes](https://github.com/lukelukes) +* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) - [@maikelsteneker](https://github.com/maikelsteneker) + {% 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 0bf7b1b1e9..13e1a87606 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -8,10 +8,12 @@ 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; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -104,7 +106,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; @@ -435,19 +437,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() { @@ -541,7 +578,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 df10f008f7..5faa8c523c 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 @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.List; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.PMD; @@ -32,20 +33,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.") @@ -107,12 +132,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.") @@ -199,10 +229,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()); @@ -348,12 +374,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/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 91f8979744..9524fef9c2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -147,7 +147,16 @@ public class GUI implements CPDListener { @Override public boolean canIgnoreAnnotations() { - return "java".equals(terseName); + if (terseName == null) { + return false; + } + switch (terseName) { + case "cs": + case "java": + return true; + default: + return false; + } } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java index 1a83310247..847b96175a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java @@ -52,7 +52,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin BaseNodeInfo parent, Node wrappedNode, Configuration configuration) { - super(Type.ELEMENT, configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent); + super(determineType(wrappedNode), configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent); this.treeInfo = document; this.wrappedNode = wrappedNode; @@ -65,6 +65,19 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin } } + 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; + } + public Map makeAttributes(Node wrappedNode) { Map atts = new HashMap<>(); Iterator it = wrappedNode.getXPathAttributesIterator(); @@ -179,6 +192,10 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin @Override public CharSequence getStringValueCS() { + if (getNodeKind() == Type.TEXT || getNodeKind() == Type.COMMENT) { + return getUnderlyingNode().getImage(); + } + // https://www.w3.org/TR/xpath-datamodel-31/#ElementNode // The string-value property of an Element Node must be the // concatenation of the string-values of all its Text Node @@ -187,6 +204,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin // Since we represent all our Nodes as elements, there are no // text nodes + // TODO: for some languages like html we have text nodes return ""; } 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/internal/ElementNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java index 30283b683b..44d12b98d8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java @@ -57,4 +57,54 @@ public class ElementNodeTest { Assert.assertEquals(0, elementFoo1.compareOrder(elementFoo1)); } + + @Test + public void verifyTextNodeType() { + DummyRootNode root = new DummyRootNode(); + + DummyNode c0 = new DummyNode(false, "foo"); + c0.setCoords(1, 1, 2, 2); + root.addChild(c0, 0); + + DummyNode c1 = new DummyNode(false, "#text"); + c1.setCoords(2, 1, 2, 2); + root.addChild(c1, 1); + + Configuration configuration = Configuration.newConfiguration(); + AstTreeInfo treeInfo = new AstTreeInfo(root, configuration); + + AstElementNode rootElt = treeInfo.getRootNode().getRootElement(); + Assert.assertSame(root, rootElt.getUnderlyingNode()); + Assert.assertEquals(Type.ELEMENT, rootElt.getNodeKind()); + Assert.assertSame(rootElt, treeInfo.findWrapperFor(root)); + + AstElementNode elementFoo0 = rootElt.getChildren().get(0); + Assert.assertEquals(Type.ELEMENT, elementFoo0.getNodeKind()); + Assert.assertSame(c0, elementFoo0.getUnderlyingNode()); + Assert.assertSame(elementFoo0, treeInfo.findWrapperFor(c0)); + + AstElementNode elementText1 = rootElt.getChildren().get(1); + Assert.assertEquals(Type.TEXT, elementText1.getNodeKind()); + Assert.assertSame(c1, elementText1.getUnderlyingNode()); + Assert.assertSame(elementText1, treeInfo.findWrapperFor(c1)); + } + + @Test + public void verifyCommentNodeType() { + DummyRootNode root = new DummyRootNode(); + + DummyNode c1 = new DummyNode(false, "#comment"); + c1.setCoords(2, 1, 2, 2); + root.addChild(c1, 0); + + Configuration configuration = Configuration.newConfiguration(); + AstTreeInfo treeInfo = new AstTreeInfo(root, configuration); + AstElementNode rootElt = treeInfo.getRootNode().getRootElement(); + + AstElementNode elementComment = rootElt.getChildren().get(0); + Assert.assertEquals(Type.COMMENT, elementComment.getNodeKind()); + Assert.assertSame(c1, elementComment.getUnderlyingNode()); + Assert.assertSame(elementComment, treeInfo.findWrapperFor(c1)); + } + } diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index 931ff75123..cfb78a34a2 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -21,6 +21,7 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; private boolean ignoreLiteralSequences = false; + private boolean ignoreAttributes = false; /** * Sets the possible options for the C# tokenizer. @@ -28,19 +29,16 @@ public class CsTokenizer extends AntlrTokenizer { * @param properties the properties * @see #IGNORE_USINGS * @see #OPTION_IGNORE_LITERAL_SEQUENCES + * @see #IGNORE_ANNOTATIONS */ public void setProperties(Properties properties) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, Boolean.FALSE.toString())); - ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, - Boolean.FALSE.toString())); + ignoreUsings = getBooleanProperty(properties, IGNORE_USINGS); + ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES); + ignoreAttributes = getBooleanProperty(properties, IGNORE_ANNOTATIONS); } - public void setIgnoreUsings(boolean ignoreUsings) { - this.ignoreUsings = ignoreUsings; - } - - public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { - this.ignoreLiteralSequences = ignoreLiteralSequences; + private boolean getBooleanProperty(final Properties properties, final String property) { + return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString())); } @Override @@ -51,7 +49,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences); + return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences, ignoreAttributes); } /** @@ -70,15 +68,18 @@ public class CsTokenizer extends AntlrTokenizer { private final boolean ignoreUsings; private final boolean ignoreLiteralSequences; + private final boolean ignoreAttributes; private boolean discardingUsings = false; private boolean discardingNL = false; + private boolean isDiscardingAttribute = false; private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { + CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { super(tokenManager); this.ignoreUsings = ignoreUsings; this.ignoreLiteralSequences = ignoreLiteralSequences; + this.ignoreAttributes = ignoreAttributes; } @Override @@ -91,6 +92,7 @@ public class CsTokenizer extends AntlrTokenizer { discardCurrent = false; skipUsingDirectives(currentToken, remainingTokens); skipLiteralSequences(currentToken, remainingTokens); + skipAttributes(currentToken); } private void skipUsingDirectives(final AntlrToken currentToken, final Iterable remainingTokens) { @@ -167,6 +169,25 @@ public class CsTokenizer extends AntlrTokenizer { discardingNL = currentToken.getKind() == CSharpLexer.NL; } + private void skipAttributes(final AntlrToken currentToken) { + if (ignoreAttributes) { + switch (currentToken.getKind()) { + case CSharpLexer.OPEN_BRACKET: + // Start of an attribute. + isDiscardingAttribute = true; + break; + case CSharpLexer.CLOSE_BRACKET: + // End of an attribute. + isDiscardingAttribute = false; + discardCurrent = true; + break; + default: + // Skip any other token. + break; + } + } + } + private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int type = currentToken.getKind(); @@ -222,7 +243,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent; + return discardingUsings || discardingNL || isDiscardingAttribute || isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 63c9cd5aee..d05797d726 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -105,18 +105,33 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("csharp7And8Additions"); } + @Test + public void testAttributesAreNotIgnored() { + doTest("attributes"); + } + + @Test + public void testAttributesAreIgnored() { + doTest("attributes", "_ignored", skipAttributes()); + } + private Properties ignoreUsings() { - return properties(true, false); + return properties(true, false, false); } private Properties skipLiteralSequences() { - return properties(false, true); + return properties(false, true, false); } - private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) { + private Properties skipAttributes() { + return properties(false, false, true); + } + + private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { Properties properties = new Properties(); properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); + properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAttributes)); return properties; } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs new file mode 100644 index 0000000000..811e151362 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs @@ -0,0 +1,42 @@ +[Serializable] +public class SampleClass +{ + // Objects of this type can be serialized. +} + +[System.Runtime.InteropServices.DllImport("user32.dll")] +extern static void SampleMethod(); + +void MethodA([In][Out] ref double x) { } +void MethodB([Out][In] ref double x) { } +void MethodC([In, Out] ref double x) { } + +[Conditional("DEBUG"), Conditional("TEST1")] +void TraceMethod() +{ + // ... +} + +[DllImport("user32.dll")] +[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)] +[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)] + +using System; +using System.Reflection; +[assembly: AssemblyTitleAttribute("Production assembly 4")] +[module: CLSCompliant(true)] + +// default: applies to method +[ValidatedContract] +int Method1() { return 0; } + +// applies to method +[method: ValidatedContract] +int Method2() { return 0; } + +// applies to parameter +int Method3([ValidatedContract] string contract) { return 0; } + +// applies to return value +[return: ValidatedContract] +int Method4() { return 0; } \ No newline at end of file diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt new file mode 100644 index 0000000000..ec85e49500 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt @@ -0,0 +1,229 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [\[] 1 2 + [Serializable] 2 14 + [\]] 14 15 +L2 + [public] 1 7 + [class] 8 13 + [SampleClass] 14 25 +L3 + [{] 1 2 +L5 + [}] 1 2 +L7 + [\[] 1 2 + [System] 2 8 + [.] 8 9 + [Runtime] 9 16 + [.] 16 17 + [InteropServices] 17 32 + [.] 32 33 + [DllImport] 33 42 + [(] 42 43 + ["user32.dll"] 43 55 + [)] 55 56 + [\]] 56 57 +L8 + [extern] 1 7 + [static] 8 14 + [void] 15 19 + [SampleMethod] 20 32 + [(] 32 33 + [)] 33 34 + [;] 34 35 +L10 + [void] 1 5 + [MethodA] 6 13 + [(] 13 14 + [\[] 14 15 + [In] 15 17 + [\]] 17 18 + [\[] 18 19 + [Out] 19 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L11 + [void] 1 5 + [MethodB] 6 13 + [(] 13 14 + [\[] 14 15 + [Out] 15 18 + [\]] 18 19 + [\[] 19 20 + [In] 20 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L12 + [void] 1 5 + [MethodC] 6 13 + [(] 13 14 + [\[] 14 15 + [In] 15 17 + [,] 17 18 + [Out] 19 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L14 + [\[] 1 2 + [Conditional] 2 13 + [(] 13 14 + ["DEBUG"] 14 21 + [)] 21 22 + [,] 22 23 + [Conditional] 24 35 + [(] 35 36 + ["TEST1"] 36 43 + [)] 43 44 + [\]] 44 45 +L15 + [void] 1 5 + [TraceMethod] 6 17 + [(] 17 18 + [)] 18 19 +L16 + [{] 1 2 +L18 + [}] 1 2 +L20 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [)] 24 25 + [\]] 25 26 +L21 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [,] 24 25 + [SetLastError] 26 38 + [=] 38 39 + [false] 39 44 + [,] 44 45 + [ExactSpelling] 46 59 + [=] 59 60 + [false] 60 65 + [)] 65 66 + [\]] 66 67 +L22 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [,] 24 25 + [ExactSpelling] 26 39 + [=] 39 40 + [false] 40 45 + [,] 45 46 + [SetLastError] 47 59 + [=] 59 60 + [false] 60 65 + [)] 65 66 + [\]] 66 67 +L24 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Reflection] 14 24 + [;] 24 25 +L26 + [\[] 1 2 + [assembly] 2 10 + [:] 10 11 + [AssemblyTitleAttribute] 12 34 + [(] 34 35 + ["Production assembly 4"] 35 58 + [)] 58 59 + [\]] 59 60 +L27 + [\[] 1 2 + [module] 2 8 + [:] 8 9 + [CLSCompliant] 10 22 + [(] 22 23 + [true] 23 27 + [)] 27 28 + [\]] 28 29 +L30 + [\[] 1 2 + [ValidatedContract] 2 19 + [\]] 19 20 +L31 + [int] 1 4 + [Method1] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L34 + [\[] 1 2 + [method] 2 8 + [:] 8 9 + [ValidatedContract] 10 27 + [\]] 27 28 +L35 + [int] 1 4 + [Method2] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L38 + [int] 1 4 + [Method3] 5 12 + [(] 12 13 + [\[] 13 14 + [ValidatedContract] 14 31 + [\]] 31 32 + [string] 33 39 + [contract] 40 48 + [)] 48 49 + [{] 50 51 + [return] 52 58 + [0] 59 60 + [;] 60 61 + [}] 62 63 +L41 + [\[] 1 2 + [return] 2 8 + [:] 8 9 + [ValidatedContract] 10 27 + [\]] 27 28 +L42 + [int] 1 4 + [Method4] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt new file mode 100644 index 0000000000..c2de96f13f --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt @@ -0,0 +1,109 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [public] 1 7 + [class] 8 13 + [SampleClass] 14 25 +L3 + [{] 1 2 +L5 + [}] 1 2 +L8 + [extern] 1 7 + [static] 8 14 + [void] 15 19 + [SampleMethod] 20 32 + [(] 32 33 + [)] 33 34 + [;] 34 35 +L10 + [void] 1 5 + [MethodA] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L11 + [void] 1 5 + [MethodB] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L12 + [void] 1 5 + [MethodC] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L15 + [void] 1 5 + [TraceMethod] 6 17 + [(] 17 18 + [)] 18 19 +L16 + [{] 1 2 +L18 + [}] 1 2 +L24 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Reflection] 14 24 + [;] 24 25 +L31 + [int] 1 4 + [Method1] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L35 + [int] 1 4 + [Method2] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L38 + [int] 1 4 + [Method3] 5 12 + [(] 12 13 + [string] 33 39 + [contract] 40 48 + [)] 48 49 + [{] 50 51 + [return] 52 58 + [0] 59 60 + [;] 60 61 + [}] 62 63 +L42 + [int] 1 4 + [Method4] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +EOF 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 1de270c3f1..7c6d742cc1 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 @@ -30,6 +30,8 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, modelica, plsql, pom, scala, swift, vf, vm, wsdl, xml, xsl"; } + private final String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); + @Test public void testFileExistence() { assertTrue(getBinaryDistribution().exists()); @@ -69,27 +71,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(., '{ ')]"; List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); Assert.assertEquals(1, violations.size()); Assert.assertEquals(3, violations.get(0).getBeginLine()); } + @Test + public void selectTextNodeByNodeNameShouldNotWork() { + String xpath = "//*[local-name() = '#text']"; + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(0, violations.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 @@ -56,6 +84,27 @@ public class HtmlXPathRuleTest { Assert.assertEquals(4, violations.get(0).getBeginLine()); } + @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')] = '{']"; + + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(2, violations.size()); + Assert.assertEquals(4, violations.get(0).getBeginLine()); + Assert.assertEquals(6, violations.get(1).getBeginLine()); + } + + @Test + public void selectAttributeByName() { + String xpath = "//*[@*[local-name() = 'if:true']]"; + + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(1, violations.size()); + Assert.assertEquals(10, violations.get(0).getBeginLine()); + } + private List runXPath(String html, String xpath) { LanguageVersion htmlLanguage = LanguageRegistry.findLanguageByTerseName(HtmlLanguageModule.TERSE_NAME).getDefaultVersion(); Parser parser = htmlLanguage.getLanguageVersionHandler().getParser(); diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index d175247c17..a75413cd0e 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -796,7 +796,7 @@ TOKEN : "\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346","\u1348"-"\u135a", "\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676","\u1681"-"\u169a","\u16a0"-"\u16ea", "\u1780"-"\u17b3","\u17db","\u1820"-"\u1877","\u1880"-"\u18a8","\u1e00"-"\u1e9b", - "\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", + "\u1ea0"-"\u1ef9","\u1d00"-"\u1eef","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", "\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4", "\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3", "\u1fd6"-"\u1fdb","\u1fe0"-"\u1fec","\u1ff2"-"\u1ff4","\u1ff6"-"\u1ffc","\u203f"-"\u2040", @@ -804,7 +804,7 @@ TOKEN : "\u2124","\u2126","\u2128","\u212a"-"\u212d","\u212f"-"\u2131","\u2133"-"\u2139", "\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u3029","\u3031"-"\u3035","\u3038"-"\u303a", "\u3041"-"\u3094","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c","\u3131"-"\u318e", - "\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\uac00"-"\ud7a3", + "\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\ua490"-"\uabff","\uac00"-"\ud7a3", "\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d","\ufb1f"-"\ufb28", "\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44", "\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb", @@ -865,7 +865,7 @@ TOKEN : "\u12f0"-"\u130e","\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346", "\u1348"-"\u135a","\u1369"-"\u1371","\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676", "\u1681"-"\u169a","\u16a0"-"\u16ea","\u1780"-"\u17d3","\u17db","\u17e0"-"\u17e9", - "\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1e00"-"\u1e9b", + "\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1d00"-"\u1d7f","\u1e00"-"\u1e9b", "\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", "\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4", "\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3", @@ -876,6 +876,7 @@ TOKEN : "\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u302f","\u3031"-"\u3035","\u3038"-"\u303a", "\u3041"-"\u3094","\u3099"-"\u309a","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c", "\u3131"-"\u318e","\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c", + "\ua490"-"\uabff", "\uac00"-"\ud7a3","\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d"-"\ufb28", "\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44", "\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb", 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 cdeab9b9b7..d0a47c0b7b 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 @@ -44,7 +44,13 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule { ); private static final Set INVALIDATING_FIELD_ANNOTS = - setOf("lombok.Setter"); + setOf( + "lombok.Setter", + "org.mockito.Mock", + "org.mockito.InjectMocks", + "org.springframework.beans.factory.annotation.Autowired", + "org.springframework.boot.test.mock.mockito.MockBean" + ); public ImmutableFieldRule() { super(ASTFieldDeclaration.class); @@ -54,7 +60,6 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTFieldDeclaration field, Object data) { - ASTAnyTypeDeclaration enclosingType = field.getEnclosingType(); if (field.getEffectiveVisibility().isAtMost(Visibility.V_PRIVATE) && !field.getModifiers().hasAny(JModifier.VOLATILE, JModifier.STATIC, JModifier.FINAL) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java index 422a620615..684662f3fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java @@ -145,6 +145,12 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest { java7.parse(code); } + @Test + public void testUnicodeIndent() { + // https://github.com/pmd/pmd/issues/3423 + java7.parseResource("UnicodeIdentifier.java"); + } + @Test public void testParsersCases15() { doTest("ParserCornerCases", java5); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/UnicodeIdentifier.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/UnicodeIdentifier.java new file mode 100644 index 0000000000..3eae15d2a9 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/UnicodeIdentifier.java @@ -0,0 +1,37 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +// https://github.com/pmd/pmd/issues/3423 + +package com.example.pmdtest; + +public class PmdTest { + + private static final int lแตค = 1; + private static final int ฮผแตค = 2; + + public static void main(String[] args) { + System.out.println(lแตค + ฮผแตค); + } + +} + +enum CodeSet { + + START_CODE_A('ร‹'), + START_CODE_B('รŒ'), + START_CODE_C('ร'), + A_TILDE('\u00c3'), + STOP_CODE('รŽ'); + + private final char encoding; + + CodeSet(final char encoding) { + this.encoding = encoding; + } + + public char getEncoding() { + return encoding; + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index 9bb180e1a3..fd77707ff7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -516,4 +516,31 @@ public class Foo { } ]]> + + With records + 0 + stringSet) { + + public boolean hasMoreThanOneItem() { + return this.stringSet.size() > 1; + } +} ]]> + + + With records and size check + 1 + 6 + stringSet) { + + public boolean hasMoreThanOneItem() { + return this.stringSet.size() == 0; + } +} ]]> + 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 dae43dfb98..2949c72273 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 @@ -657,4 +657,35 @@ public class ExampleImmutableField { } ]]> + + + #3874 [java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito) + 0 + + diff --git a/pmd-javascript/etc/grammar/Ecmascript5.jj b/pmd-javascript/etc/grammar/Ecmascript5.jj index e180c2f760..30d8800119 100644 --- a/pmd-javascript/etc/grammar/Ecmascript5.jj +++ b/pmd-javascript/etc/grammar/Ecmascript5.jj @@ -488,6 +488,7 @@ TOKEN : "\u1E00"-"\u1E9B", "\u1EA0"-"\u1EE0", "\u1EE1"-"\u1EF9", + "\u1EE1"-"\u1EEF", "\u1F00"-"\u1F15", "\u1F18"-"\u1F1D", "\u1F20"-"\u1F39", @@ -534,11 +535,10 @@ TOKEN : "\u31A0"-"\u31B7", "\u3400", "\u4DB5", - "\u4E00", - "\u9FA5", + "\u4E00"-"\u9EA5", "\uA000"-"\uA48C", - "\uAC00", - "\uD7A3", + "\uA490"-"\uABFF", + "\uAC00"-"\ud7AF", "\uF900"-"\uFA2D", "\uFB00"-"\uFB06", "\uFB13"-"\uFB17", diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java index 95261052c4..2827f1461f 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java @@ -180,6 +180,16 @@ public class EcmascriptParserTest extends EcmascriptParserTestBase { assertEquals("^=", infix.getImage()); } + @Test + public void testUnicodeCjk() { + // the first is u+4F60 + js.parse("import { Test } from 'test2'\n" + + "define('element', class extends Test {\n" + + " \n" + + " }\n" + + "})"); + } + /** * [javascript] Failing with OutOfMemoryError parsing a Javascript file #2081 */ diff --git a/pmd-python/etc/grammar/Python.jj b/pmd-python/etc/grammar/Python.jj index b9c2313b62..d1dd62cb0c 100644 --- a/pmd-python/etc/grammar/Python.jj +++ b/pmd-python/etc/grammar/Python.jj @@ -133,7 +133,57 @@ TOKEN : /* KEYWORDS */ TOKEN : /* Python identifiers */ { < NAME: ( | )* > -| < #LETTER: ["_","a"-"z","A"-"Z"] > +| < #LETTER : ["$","A"-"Z","_","a"-"z","\u00a2"-"\u00a5","\u00aa","\u00b5","\u00ba", + "\u00c0"-"\u00d6","\u00d8"-"\u00f6","\u00f8"-"\u021f","\u0222"-"\u0233","\u0250"-"\u02ad", + "\u02b0"-"\u02b8","\u02bb"-"\u02c1","\u02d0"-"\u02d1","\u02e0"-"\u02e4","\u02ee","\u037a", + "\u0386","\u0388"-"\u038a","\u038c","\u038e"-"\u03a1","\u03a3"-"\u03ce","\u03d0"-"\u03d7", + "\u03da"-"\u03f3","\u0400"-"\u0481","\u048c"-"\u04c4","\u04c7"-"\u04c8","\u04cb"-"\u04cc", + "\u04d0"-"\u04f5","\u04f8"-"\u04f9","\u0531"-"\u0556","\u0559","\u0561"-"\u0587", + "\u05d0"-"\u05ea","\u05f0"-"\u05f2","\u0621"-"\u063a","\u0640"-"\u064a","\u0671"-"\u06d3", + "\u06d5","\u06e5"-"\u06e6","\u06fa"-"\u06fc","\u0710","\u0712"-"\u072c","\u0780"-"\u07a5", + "\u0905"-"\u0939","\u093d","\u0950","\u0958"-"\u0961","\u0985"-"\u098c","\u098f"-"\u0990", + "\u0993"-"\u09a8","\u09aa"-"\u09b0","\u09b2","\u09b6"-"\u09b9","\u09dc"-"\u09dd", + "\u09df"-"\u09e1","\u09f0"-"\u09f3","\u0a05"-"\u0a0a","\u0a0f"-"\u0a10","\u0a13"-"\u0a28", + "\u0a2a"-"\u0a30","\u0a32"-"\u0a33","\u0a35"-"\u0a36","\u0a38"-"\u0a39","\u0a59"-"\u0a5c", + "\u0a5e","\u0a72"-"\u0a74","\u0a85"-"\u0a8b","\u0a8d","\u0a8f"-"\u0a91","\u0a93"-"\u0aa8", + "\u0aaa"-"\u0ab0","\u0ab2"-"\u0ab3","\u0ab5"-"\u0ab9","\u0abd","\u0ad0","\u0ae0", + "\u0b05"-"\u0b0c","\u0b0f"-"\u0b10","\u0b13"-"\u0b28","\u0b2a"-"\u0b30","\u0b32"-"\u0b33", + "\u0b36"-"\u0b39","\u0b3d","\u0b5c"-"\u0b5d","\u0b5f"-"\u0b61","\u0b85"-"\u0b8a", + "\u0b8e"-"\u0b90","\u0b92"-"\u0b95","\u0b99"-"\u0b9a","\u0b9c","\u0b9e"-"\u0b9f", + "\u0ba3"-"\u0ba4","\u0ba8"-"\u0baa","\u0bae"-"\u0bb5","\u0bb7"-"\u0bb9","\u0c05"-"\u0c0c", + "\u0c0e"-"\u0c10","\u0c12"-"\u0c28","\u0c2a"-"\u0c33","\u0c35"-"\u0c39","\u0c60"-"\u0c61", + "\u0c85"-"\u0c8c","\u0c8e"-"\u0c90","\u0c92"-"\u0ca8","\u0caa"-"\u0cb3","\u0cb5"-"\u0cb9", + "\u0cde","\u0ce0"-"\u0ce1","\u0d05"-"\u0d0c","\u0d0e"-"\u0d10","\u0d12"-"\u0d28", + "\u0d2a"-"\u0d39","\u0d60"-"\u0d61","\u0d85"-"\u0d96","\u0d9a"-"\u0db1","\u0db3"-"\u0dbb", + "\u0dbd","\u0dc0"-"\u0dc6","\u0e01"-"\u0e30","\u0e32"-"\u0e33","\u0e3f"-"\u0e46", + "\u0e81"-"\u0e82","\u0e84","\u0e87"-"\u0e88","\u0e8a","\u0e8d","\u0e94"-"\u0e97", + "\u0e99"-"\u0e9f","\u0ea1"-"\u0ea3","\u0ea5","\u0ea7","\u0eaa"-"\u0eab","\u0ead"-"\u0eb0", + "\u0eb2"-"\u0eb3","\u0ebd","\u0ec0"-"\u0ec4","\u0ec6","\u0edc"-"\u0edd","\u0f00", + "\u0f40"-"\u0f47","\u0f49"-"\u0f6a","\u0f88"-"\u0f8b","\u1000"-"\u1021","\u1023"-"\u1027", + "\u1029"-"\u102a","\u1050"-"\u1055","\u10a0"-"\u10c5","\u10d0"-"\u10f6","\u1100"-"\u1159", + "\u115f"-"\u11a2","\u11a8"-"\u11f9","\u1200"-"\u1206","\u1208"-"\u1246","\u1248", + "\u124a"-"\u124d","\u1250"-"\u1256","\u1258","\u125a"-"\u125d","\u1260"-"\u1286","\u1288", + "\u128a"-"\u128d","\u1290"-"\u12ae","\u12b0","\u12b2"-"\u12b5","\u12b8"-"\u12be","\u12c0", + "\u12c2"-"\u12c5","\u12c8"-"\u12ce","\u12d0"-"\u12d6","\u12d8"-"\u12ee","\u12f0"-"\u130e", + "\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346","\u1348"-"\u135a", + "\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676","\u1681"-"\u169a","\u16a0"-"\u16ea", + "\u1780"-"\u17b3","\u17db","\u1820"-"\u1877","\u1880"-"\u18a8","\u1e00"-"\u1e9b", + "\u1ea0"-"\u1ef9","\u1d00"-"\u1eef","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", + "\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4", + "\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3", + "\u1fd6"-"\u1fdb","\u1fe0"-"\u1fec","\u1ff2"-"\u1ff4","\u1ff6"-"\u1ffc","\u203f"-"\u2040", + "\u207f","\u20a0"-"\u20af","\u2102","\u2107","\u210a"-"\u2113","\u2115","\u2119"-"\u211d", + "\u2124","\u2126","\u2128","\u212a"-"\u212d","\u212f"-"\u2131","\u2133"-"\u2139", + "\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u3029","\u3031"-"\u3035","\u3038"-"\u303a", + "\u3041"-"\u3094","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c","\u3131"-"\u318e", + "\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\ua490"-"\uabff","\uac00"-"\ud7a3", + "\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d","\ufb1f"-"\ufb28", + "\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44", + "\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb", + "\ufe33"-"\ufe34","\ufe4d"-"\ufe4f","\ufe69","\ufe70"-"\ufe72","\ufe74","\ufe76"-"\ufefc", + "\uff04","\uff21"-"\uff3a","\uff3f","\uff41"-"\uff5a","\uff65"-"\uffbe","\uffc2"-"\uffc7", + "\uffca"-"\uffcf","\uffd2"-"\uffd7","\uffda"-"\uffdc","\uffe0"-"\uffe1","\uffe5"-"\uffe6"]> + } diff --git a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java b/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java index 254b8cdbe8..e72eb3e489 100644 --- a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java +++ b/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java @@ -42,8 +42,19 @@ public class PythonTokenizerTest extends CpdTextComparisonTest { doTest("backticks"); } + @Test + public void testUnicode() { + doTest("sample_unicode"); + } + @Test public void testTabWidth() { doTest("tabWidth"); } + + @Test + public void testVarWithDollar() { + doTest("var_with_dollar"); + } + } diff --git a/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.py b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.py new file mode 100644 index 0000000000..b0143e7e1a --- /dev/null +++ b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.py @@ -0,0 +1,4 @@ +# note: add more examples here when bugs arise + +def check(): + total_cost_ฮผs = [] # https://github.com/pmd/pmd/issues/2604 diff --git a/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.txt b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.txt new file mode 100644 index 0000000000..ec7d598bfc --- /dev/null +++ b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/sample_unicode.txt @@ -0,0 +1,13 @@ + [Image] or [Truncated image[ Bcol Ecol +L3 + [def] 1 4 + [check] 5 10 + [(] 10 11 + [)] 11 12 + [:] 12 13 +L4 + [total_cost_ฮผs] 5 18 + [=] 19 20 + [\[] 21 22 + [\]] 22 23 +EOF diff --git a/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.py b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.py new file mode 100644 index 0000000000..5c42520282 --- /dev/null +++ b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.py @@ -0,0 +1,2 @@ +def check(): + a$a = [] diff --git a/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.txt b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.txt new file mode 100644 index 0000000000..07eeb8ba7d --- /dev/null +++ b/pmd-python/src/test/resources/net/sourceforge/pmd/lang/python/cpd/testdata/var_with_dollar.txt @@ -0,0 +1,13 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [def] 1 4 + [check] 5 10 + [(] 10 11 + [)] 11 12 + [:] 12 13 +L2 + [a$a] 5 8 + [=] 9 10 + [\[] 11 12 + [\]] 12 13 +EOF