diff --git a/.all-contributorsrc b/.all-contributorsrc index 99708783e8..908c5cc8cb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1053,7 +1053,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/86377278?v=4", "profile": "https://github.com/aaronhurst-google", "contributions": [ - "bug" + "bug", + "code" ] }, { @@ -5917,7 +5918,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/35368290?v=4", "profile": "https://github.com/tprouvot", "contributions": [ - "bug" + "bug", + "code" ] }, { @@ -6768,6 +6770,42 @@ "code", "financial" ] + }, + { + "login": "abyss638", + "name": "Simon Abykov", + "avatar_url": "https://avatars.githubusercontent.com/u/90252673?v=4", + "profile": "https://github.com/abyss638", + "contributions": [ + "code" + ] + }, + { + "login": "eklimo", + "name": "Edward Klimoshenko", + "avatar_url": "https://avatars.githubusercontent.com/u/39220927?v=4", + "profile": "https://github.com/eklimo", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "nvuillam", + "name": "Nicolas Vuillamy", + "avatar_url": "https://avatars.githubusercontent.com/u/17500430?v=4", + "profile": "https://github.com/nvuillam", + "contributions": [ + "doc" + ] + },{ + "login": "pacvz", + "name": "pacvz", + "avatar_url": "https://avatars.githubusercontent.com/u/35453365?v=4", + "profile": "https://github.com/pacvz", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.ci/build.sh b/.ci/build.sh index 828fcb9bbf..6c935f0a52 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -28,6 +28,15 @@ function build() { pmd_ci_utils_determine_build_env pmd/pmd echo + if ! pmd_ci_utils_is_fork_or_pull_request; then + if [ "${PMD_CI_BRANCH}" = "experimental-apex-parser" ]; then + pmd_ci_log_group_start "Build with mvnw" + ./mvnw clean install --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" + pmd_ci_log_group_end + exit 0 + fi + fi + if pmd_ci_utils_is_fork_or_pull_request; then pmd_ci_log_group_start "Build with mvnw" ./mvnw clean install --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01b91ce2d5..862bb5234b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: - main - master - pmd/7.0.x + - experimental-apex-parser tags: - '**' pull_request: diff --git a/SPONSORS.md b/SPONSORS.md new file mode 100644 index 0000000000..4bbe1c7b55 --- /dev/null +++ b/SPONSORS.md @@ -0,0 +1,10 @@ +# PMD's sponsors + +Many thanks to all our sponsors: + +* [Matt Hargett](https://github.com/matthargett) (@matthargett) + +If you also want to sponsor PMD, you have two options: + +* [Sponsor @pmd on GitHub Sponsors](https://github.com/sponsors/pmd) +* [PMD - Open Collective](https://opencollective.com/pmd) diff --git a/docs/_config.yml b/docs/_config.yml index f89896c168..841070517d 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,7 +2,7 @@ repository: pmd/pmd pmd: version: 7.0.0-SNAPSHOT - previous_version: 6.47.0 + previous_version: 6.49.0 date: ??-?????-2022 release_type: major diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index deccde46f6..4f054eff8d 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -246,6 +246,76 @@ the breaking API changes will be performed in 7.0.0. an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0, we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %} +#### 6.49.0 + +##### Deprecated API + +* In order to reduce the dependency on Apex Jorje classes, the following methods have been deprecated. + These methods all leaked internal Jorje enums. These enums have been replaced now by enums the + PMD's AST package. + * {% jdoc !!apex::lang.apex.ast.ASTAssignmentExpression#getOperator() %} + * {% jdoc !!apex::lang.apex.ast.ASTBinaryExpression#getOperator() %} + * {% jdoc !!apex::lang.apex.ast.ASTBooleanExpression#getOperator() %} + * {% jdoc !!apex::lang.apex.ast.ASTPostfixExpression#getOperator() %} + * {% jdoc !!apex::lang.apex.ast.ASTPrefixExpression#getOperator() %} + + All these classes have now a new `getOp()` method. Existing code should be refactored to use this method instead. + It returns the new enums, like {% jdoc apex::lang.apex.ast.AssignmentOperator %}, and avoids + the dependency to Jorje. + +#### 6.48.0 + +##### CPD CLI + +* CPD has a new CLI option `--debug`. This option has the same behavior as in PMD. It enables more verbose + logging output. + +##### Rule Test Framework + +* The module "pmd-test", which contains support classes to write rule tests, now **requires Java 8**. If you depend on + this module for testing your own custom rules, you'll need to make sure to use at least Java 8. +* The new module "pmd-test-schema" contains now the XSD schema and the code to parse the rule test XML files. The + schema has been extracted in order to easily share it with other tools like the Rule Designer or IDE plugins. +* Test schema changes: + * The attribute `isRegressionTest` of `test-code` is deprecated. The new + attribute `disabled` should be used instead for defining whether a rule test should be skipped or not. + * The attributes `reinitializeRule` and `useAuxClasspath` of `test-code` are deprecated and assumed true. + They will not be replaced. + * The new attribute `focused` of `test-code` allows disabling all tests except the focused one temporarily. +* More information about the rule test framework can be found in the documentation: + [Testing your rules](pmd_userdocs_extending_testing.html) + +##### Deprecated API + +* The experimental Java AST class {% jdoc java::lang.java.ast.ASTGuardedPattern %} has been deprecated and + will be removed. It was introduced for Java 17 and Java 18 Preview as part of pattern matching for switch, + but it is no longer supported with Java 19 Preview. +* The interface {% jdoc core::cpd.renderer.CPDRenderer %} is deprecated. For custom CPD renderers + the new interface {% jdoc core::cpd.renderer.CPDReportRenderer %} should be used. +* The class {% jdoc test::testframework.TestDescriptor %} is deprecated, replaced with {% jdoc test-schema::test.schema.RuleTestDescriptor %}. +* Many methods of {% jdoc test::testframework.RuleTst %} have been deprecated as internal API. + +##### Experimental APIs + +* To support the Java preview language features "Pattern Matching for Switch" and "Record Patterns", the following + AST nodes have been introduced as experimental: + * {% jdoc java::lang.java.ast.ASTSwitchGuard %} + * {% jdoc java::lang.java.ast.ASTRecordPattern %} + * {% jdoc java::lang.java.ast.ASTComponentPatternList %} + +##### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* {%jdoc !!core::cpd.CPDConfiguration#setRenderer(net.sourceforge.pmd.cpd.Renderer) %} +* {%jdoc !!core::cpd.CPDConfiguration#setCPDRenderer(net.sourceforge.pmd.cpd.renderer.CPDRenderer) %} +* {%jdoc !!core::cpd.CPDConfiguration#getRenderer() %} +* {%jdoc !!core::cpd.CPDConfiguration#getCPDRenderer() %} +* {%jdoc !!core::cpd.CPDConfiguration#getRendererFromString(java.lang.String,java.lang.String) %} +* {%jdoc !!core::cpd.CPDConfiguration#getCPDRendererFromString(java.lang.String,java.lang.String) %} +* {%jdoc core::cpd.renderer.CPDRendererAdapter %} + #### 6.47.0 No changes. diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 1af5d1cf1c..4017071bc9 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -231,737 +231,743 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Eden Hao

🐛 +
Edward Klimoshenko

🐛 💻
Egor Bredikhin

🐛
Elan P. Kugelmass

🐛
Elder S.

🐛
Emile

🐛
Eric

🐛 -
Eric Kintzer

🐛 +
Eric Kintzer

🐛
Eric Perret

🐛
Eric Squires

🐛
Erich L Foster

🐛
Erik Bleske

🐛
Ernst Reissner

🐛
F.W. Dekker

🐛 -
FSchliephacke

🐛 +
FSchliephacke

🐛
Facundo

🐛
Federico Giust

🐛
Fedor Sherstobitov

🐛
Felix Lampe

🐛
Filip Golonka

🐛
Filipe Esperandio

💻 🐛 -
Filippo Nova

🐛 +
Filippo Nova

🐛
Francesco la Torre

🐛
Francisco Duarte

🐛
Frieder Bluemle

🐛
Frits Jalvingh

💻 🐛
G. Bazior

🐛
Gabe Henkes

🐛 -
Genoud Magloire

🐛 +
Genoud Magloire

🐛
Geoffrey555

🐛
Georg Romstorfer

🐛
Gio

🐛
Gol

🐛
Gonzalo Exequiel Ibars Ingman

💻 🐛
GooDer

🐛 -
Gregor Riegler

🐛 +
Gregor Riegler

🐛
Grzegorz Olszewski

🐛
Gunther Schrijvers

💻 🐛
Gustavo Krieger

🐛
Guy Elsmore-Paddock

🐛
Görkem Mülayim

🐛
Hanzel Godinez

🐛 -
Haoliang Chen

🐛 +
Haoliang Chen

🐛
Harsh Kukreja

🐛
Heber

🐛
Henning Schmiedehausen

💻 🐛
Henning von Bargen

💻
Hervé Boutemy

🐛
Himanshu Pandey

🐛 -
Hokwang Lee

🐛 +
Hokwang Lee

🐛
Hooperbloob

💻
Hung PHAN

🐛
IDoCodingStuffs

💻 🐛
Iccen Gan

🐛
Ignacio Mariano Tirabasso

🐛
Igor Melnichenko

🐛 -
Igor Moreno

🐛 +
Igor Moreno

🐛
Intelesis-MS

🐛
Iroha_

🐛
Ishan Srivastava

🐛
Ivano Guerini

🐛
Ivar Andreas Bonsaksen

🐛
Ivo Šmíd

🐛 -
JJengility

🐛 +
JJengility

🐛
Jake Hemmerle

🐛
James Harrison

🐛 💻
Jan

🐛
Jan Aertgeerts

💻 🐛
Jan Brümmer

🐛
Jan Tříska

🐛 -
Jan-Lukas Else

🐛 +
Jan-Lukas Else

🐛
Jason Qiu

💻 📖
Jason Williams

🐛
Jean-Paul Mayer

🐛
Jean-Simon Larochelle

🐛
Jeff Bartolotta

💻 🐛
Jeff Hube

💻 🐛 -
Jeff Jensen

🐛 +
Jeff Jensen

🐛
Jeff May

🐛
Jens Gerdes

🐛
Jeroen Borgers

🐛 💻
Jerome Russ

🐛
JerritEic

💻 📖
Jiri Pejchal

🐛 -
Jithin Sunny

🐛 +
Jithin Sunny

🐛
Jiří Škorpil

🐛
Joao Machado

🐛
Jochen Krauss

🐛
Johan Hammar

🐛
John Karp

🐛
John Zhang

🐛 -
John-Teng

💻 🐛 +
John-Teng

💻 🐛
Jon Moroney

💻 🐛
Jonas Geiregat

🐛
Jonathan Wiesel

💻 🐛
Jordan

🐛
Jordi Llach

🐛
Jorge Solórzano

🐛 -
JorneVL

🐛 +
JorneVL

🐛
Jose Palafox

🐛
Jose Stovall

🐛
Joseph

💻
Joseph Heenan

🐛
Josh Feingold

💻 🐛
Josh Holthaus

🐛 -
Joshua S Arquilevich

🐛 +
Joshua S Arquilevich

🐛
João Ferreira

💻 🐛
João Pedro Schmitt

🐛
Juan Martín Sotuyo Dodero

💻 📖 🐛 🚧
Juan Pablo Civile

🐛
Julian Voronetsky

🐛
Julien

🐛 -
Julius

🐛 +
Julius

🐛
JustPRV

🐛
Jörn Huxhorn

🐛
KThompso

🐛
Kai Amundsen

🐛
Karel Vervaeke

🐛
Karl-Andero Mere

🐛 -
Karl-Philipp Richter

🐛 +
Karl-Philipp Richter

🐛
Karsten Silz

🐛
Kazuma Watanabe

🐛
Kev

🐛
Keve Müller

🐛
Kevin Guerra

💻
Kevin Jones

🐛 -
Kevin Wayne

🐛 +
Kevin Wayne

🐛
Kieran Black

🐛
Kirill Zubov

🐛
Kirk Clemens

💻 🐛
Klaus Hartl

🐛
Koen Van Looveren

🐛
Kris Scheibe

💻 🐛 -
Kunal Thanki

🐛 +
Kunal Thanki

🐛
LaLucid

💻
Larry Diamond

💻 🐛
Lars Knickrehm

🐛
Leo Gutierrez

🐛
LiGaOg

💻
Lintsi

🐛 -
Linus Fernandes

🐛 +
Linus Fernandes

🐛
Lixon Lookose

🐛
Logesh

🐛
Lorenzo Gabriele

🐛
Loïc Ledoyen

🐛
Lucas Silva

🐛
Lucas Soncini

💻 🐛 -
Lukasz Slonina

🐛 +
Lukasz Slonina

🐛
Lukebray

🐛
Lyor Goldstein

🐛
MCMicS

🐛
Macarse

🐛
Machine account for PMD

💻
Maciek Siemczyk

🐛 -
Maikel Steneker

💻 🐛 +
Maikel Steneker

💻 🐛
Maksim Moiseikin

🐛
Manfred Koch

🐛
Manuel Moya Ferrer

💻 🐛
Manuel Ryan

🐛
Marat Vyshegorodtsev

🐛
Marcel Härle

🐛 -
Marcello Fialho

🐛 +
Marcello Fialho

🐛
Marcin Rataj

🐛
Mark Adamcin

🐛
Mark Hall

💻 🐛
Mark Kolich

🐛
Mark Pritchard

🐛
Markus Rathgeb

🐛 -
Marquis Wang

🐛 +
Marquis Wang

🐛
Martin Feldsztejn

🐛
Martin Lehmann

🐛
Martin Spamer

🐛
Martin Tarjányi

🐛
MatFl

🐛
Mateusz Stefanski

🐛 -
Mathieu Gouin

🐛 +
Mathieu Gouin

🐛
MatiasComercio

💻 🐛
Matt Benson

🐛
Matt De Poorter

🐛
Matt Hargett

💻 💵
Matt Harrah

🐛
Matt Nelson

🐛 -
Matthew Amos

🐛 +
Matthew Amos

🐛
Matthew Duggan

🐛
Matthew Hall

🐛
Matías Fraga

💻 🐛
Maxime Robert

💻 🐛
MetaBF

🐛
Michael

🐛 -
Michael Bell

🐛 +
Michael Bell

🐛
Michael Bernstein

🐛
Michael Clay

🐛
Michael Dombrowski

🐛
Michael Hausegger

🐛
Michael Hoefer

🐛
Michael Möbius

🐛 -
Michael N. Lipp

🐛 +
Michael N. Lipp

🐛
Michael Pellegrini

🐛
Michal Kordas

🐛
Michał Borek

🐛
Michał Kuliński

🐛
Miguel Núñez Díaz-Montes

🐛
Mihai Ionut

🐛 -
Mirek Hankus

🐛 +
Mirek Hankus

🐛
Mladjan Gadzic

🐛
MrAngry52

🐛
Muminur Choudhury

🐛
Mykhailo Palahuta

💻 🐛
Nagendra Kumar Singh

🐛
Nahuel Barrios

🐛 -
Nathan Braun

🐛 +
Nathan Braun

🐛
Nathan Reynolds

🐛
Nathan Reynolds

🐛
Nathanaël

🐛
Naveen

💻
Nazdravi

🐛
Neha-Dhonde

🐛 -
Nicholas Doyle

🐛 +
Nicholas Doyle

🐛
Nick Butcher

🐛
Nico Gallinal

🐛
Nicola Dal Maso

🐛
Nicolas Filotto

💻 +
Nicolas Vuillamy

📖
Nikita Chursin

🐛 -
Niklas Baudy

🐛 -
Nikolas Havrikov

🐛 +
Niklas Baudy

🐛 +
Nikolas Havrikov

🐛
Nilesh Virkar

🐛
Nimit Patel

🐛
Niranjan Harpale

🐛
Noah Sussman

🐛
Noah0120

🐛 -
Noam Tamim

🐛 -
Noel Grandin

🐛 +
Noam Tamim

🐛 +
Noel Grandin

🐛
Olaf Haalstra

🐛
Oleg Pavlenko

🐛
Oleksii Dykov

💻
Oliver Eikemeier

🐛
Olivier Parent

💻 🐛 -
Ollie Abbey

💻 🐛 -
OverDrone

🐛 +
Ollie Abbey

💻 🐛 +
OverDrone

🐛
Ozan Gulle

💻 🐛
PUNEET JAIN

🐛
Parbati Bose

🐛
Paul Berg

🐛
Pavel Bludov

🐛 -
Pavel Mička

🐛 -
Pedro Nuno Santos

🐛 +
Pavel Mička

🐛 +
Pedro Nuno Santos

🐛
Pedro Rijo

🐛
Pelisse Romain

💻 📖 🐛
Per Abich

💻
Pete Davids

🐛
Peter Bruin

🐛 -
Peter Chittum

💻 🐛 -
Peter Cudmore

🐛 +
Peter Chittum

💻 🐛 +
Peter Cudmore

🐛
Peter Kasson

🐛
Peter Kofler

🐛
Peter Paul Bakker

💻
Pham Hai Trung

🐛
Philip Graf

💻 🐛 -
Philip Hachey

🐛 -
Philippe Ozil

🐛 +
Philip Hachey

🐛 +
Philippe Ozil

🐛
Phinehas Artemix

🐛
Phokham Nonava

🐛
Piotr Szymański

🐛
Piotrek Żygieło

💻 🐛
Pranay Jaiswal

🐛 -
Prasad Kamath

🐛 -
Prasanna

🐛 +
Prasad Kamath

🐛 +
Prasanna

🐛
Presh-AR

🐛
Puneet1726

🐛
Rafael Cortês

🐛
RaheemShaik999

🐛
RajeshR

💻 🐛 -
Ramachandra Mohan

🐛 -
Ramel0921

🐛 +
Ramachandra Mohan

🐛 +
Ramel0921

🐛
Raquel Pau

🐛
Ravikiran Janardhana

🐛
Reda Benhemmouche

🐛
Renato Oliveira

💻 🐛
Rich DiCroce

🐛 -
Riot R1cket

🐛 -
Rishabh Jain

🐛 +
Riot R1cket

🐛 +
Rishabh Jain

🐛
RishabhDeep Singh

🐛
Robbie Martinus

💻 🐛
Robert Henry

🐛
Robert Painsi

🐛
Robert Russell

🐛 -
Robert Sösemann

💻 📖 📢 🐛 -
Robert Whitebit

🐛 +
Robert Sösemann

💻 📖 📢 🐛 +
Robert Whitebit

🐛
Robin Richtsfeld

🐛
Robin Stocker

💻 🐛
Robin Wils

🐛
RochusOest

🐛
Rodolfo Noviski

🐛 -
Rodrigo Casara

🐛 -
Rodrigo Fernandes

🐛 +
Rodrigo Casara

🐛 +
Rodrigo Fernandes

🐛
Roman Salvador

💻 🐛
Ronald Blaschke

🐛
Róbert Papp

🐛
Saikat Sengupta

🐛
Saksham Handu

🐛 -
Saladoc

🐛 -
Salesforce Bob Lightning

🐛 +
Saladoc

🐛 +
Salesforce Bob Lightning

🐛
Sam Carlberg

🐛
Satoshi Kubo

🐛
Scott Kennedy

🐛
Scott Wells

🐛 💻
Scrsloota

💻 -
Sebastian Bögl

🐛 -
Sebastian Schuberth

🐛 +
Sebastian Bögl

🐛 +
Sebastian Schuberth

🐛
Sebastian Schwarz

🐛
Sergey Gorbaty

🐛
Sergey Kozlov

🐛
Sergey Yanzin

💻 🐛
Seth Wilcox

💻 -
Shubham

💻 🐛 -
Simon Xiao

🐛 +
Shubham

💻 🐛 +
Simon Abykov

💻 +
Simon Xiao

🐛
Srinivasan Venkatachalam

🐛
Stanislav Gromov

🐛
Stanislav Myachenkov

💻
Stefan Birkner

🐛 + +
Stefan Bohn

🐛
Stefan Endrullis

🐛
Stefan Klöss-Schuster

🐛 - -
Stefan Wolf

🐛
Stephan H. Wissel

🐛
Stephen

🐛
Stephen Friedrich

🐛 + +
Steve Babula

💻
Stexxe

🐛
Stian Lågstad

🐛 - -
StuartClayton5

🐛
Supun Arunoda

🐛
Suren Abrahamyan

🐛
SwatiBGupta1110

🐛 + +
SyedThoufich

🐛
Szymon Sasin

🐛
T-chuangxin

🐛 - -
TERAI Atsuhiro

🐛
TIOBE Software

💻 🐛
Taylor Smock

🐛
Techeira Damián

💻 🐛 + +
Ted Husted

🐛
TehBakker

🐛
The Gitter Badger

🐛 - -
Theodoor

🐛
Thiago Henrique Hüpner

🐛
Thibault Meyer

🐛
Thomas Güttler

🐛 + +
Thomas Jones-Low

🐛
Thomas Smith

💻 🐛
ThrawnCA

🐛 - -
Thunderforge

💻 🐛
Tim van der Lippe

🐛
Tobias Weimer

💻 🐛
Tom Daly

🐛 + +
Tomer Figenblat

🐛
Tomi De Lucca

💻 🐛
Torsten Kleiber

🐛 - -
TrackerSB

🐛
Ullrich Hafner

🐛
Utku Cuhadaroglu

💻 🐛
Valentin Brandl

🐛 + +
Valeria

🐛
Vasily Anisimov

🐛
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

🐛 +
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

🐛
dalizi007

💻
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

🐛
lgemeinhardt

🐛
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

🐛 +
orimarko

💻 🐛 +
pacvz

💻 +
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

🐛
shiomiyan

📖
simeonKondr

🐛
snajberk

🐛 - -
sniperrifle2004

🐛
snuyanzin

🐛 💻
sratz

🐛 + +
stonio

🐛
sturton

💻 🐛
sudharmohan

🐛
suruchidawar

🐛 - -
svenfinitiv

🐛
tashiscool

🐛
test-git-hook

🐛 + +
testation21

💻 🐛
thanosa

🐛
tiandiyixian

🐛
tobwoerk

🐛 - - -
tprouvot

🐛 +
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/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index d909eb4f2f..67b81492be 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -73,6 +73,9 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt description="Sources code language." default="java" %} + {% include custom/cli_option_row.html options="--debug,--verbose" + description="Debug mode. Prints more log output." + %} {% include custom/cli_option_row.html options="--encoding" description="Character encoding to use when processing files. If not specified, CPD uses the system default encoding." %} diff --git a/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md b/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md index 4e1a23a0d0..f21bd5b448 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md +++ b/docs/pages/pmd/userdocs/cpd/cpd_report_formats.md @@ -104,9 +104,9 @@ Example: - - - - - ruleChainVisits = query.getRuleChainVisits(); diff --git a/docs/pages/pmd/userdocs/tools/ci.md b/docs/pages/pmd/userdocs/tools/ci.md index f12b366935..513374f26e 100644 --- a/docs/pages/pmd/userdocs/tools/ci.md +++ b/docs/pages/pmd/userdocs/tools/ci.md @@ -28,4 +28,10 @@ result of the PMD maven plugin. See [Other Tools / Integrations](pmd_userdocs_tools.html#github-actions) +## MegaLinter + +[🦙 Mega-Linter](https://oxsecurity.github.io/megalinter/latest/) analyzes 50 languages, 22 formats, 21 tooling formats, excessive copy-pastes, spelling mistakes and security issues in your repository sources with a GitHub Action, other CI tools or locally. + +It [natively embeds PMD](https://oxsecurity.github.io/megalinter/latest/descriptors/java_pmd/). + diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 094e92c0ef..8abb75f161 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -19,112 +19,11 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy -#### Java 19 Support - -This release of PMD brings support for Java 19. There are no new standard language features. - -PMD supports [JEP 427: Pattern Matching for switch (Third Preview)](https://openjdk.org/jeps/427) and -[JEP 405: Record Patterns (Preview)](https://openjdk.org/jeps/405) as preview language features. - -In order to analyze a project with PMD that uses these language features, -you'll need to enable it via the environment variable `PMD_JAVA_OPTS` and select the new language -version `19-preview`: - - export PMD_JAVA_OPTS=--enable-preview - ./run.sh pmd -language java -version 19-preview ... - -Note: Support for Java 17 preview language features have been removed. The version "17-preview" is no longer available. - -#### Gherkin support -Thanks to the contribution from [Anne Brouwers](https://github.com/ASBrouwers) PMD now has CPD support -for the [Gherkin](https://cucumber.io/docs/gherkin/) language. It is used to defined test cases for the -[Cucumber](https://cucumber.io/) testing tool for behavior-driven development. - -Being based on a proper Antlr grammar, CPD can: - -* ignore comments -* honor [comment-based suppressions](pmd_userdocs_cpd.html#suppression) - ### Fixed Issues -* apex - * [#4056](https://github.com/pmd/pmd/pull/4056): \[apex] ApexSOQLInjection: Add support count query -* core - * [#4021](https://github.com/pmd/pmd/pull/4021): \[core] CPD: Add total number of tokens to XML reports - * [#4031](https://github.com/pmd/pmd/issues/4031): \[core] If report is written to stdout, stdout should not be closed - * [#4053](https://github.com/pmd/pmd/pull/4053): \[core] Allow building PMD under Java 18+ -* java - * [#4015](https://github.com/pmd/pmd/issues/4015): \[java] Support JDK 19 -* java-bestpractices - * [#3455](https://github.com/pmd/pmd/issues/3455): \[java] WhileLoopWithLiteralBoolean - false negative with complex expressions -* java-design - * [#3729](https://github.com/pmd/pmd/issues/3729): \[java] TooManyMethods ignores "real" methods which are named like getters or setters - * [#3949](https://github.com/pmd/pmd/issues/3949): \[java] FinalFieldCouldBeStatic - false negative with unnecessary parenthesis -* java-performance - * [#3625](https://github.com/pmd/pmd/issues/3625): \[java] AddEmptyString - false negative with empty var -* lua - * [#4061](https://github.com/pmd/pmd/pull/4061): \[lua] Fix several related Lua parsing issues found when using CPD -* test - * [#3302](https://github.com/pmd/pmd/pull/3302): \[test] Improve xml test schema - * [#3758](https://github.com/pmd/pmd/issues/3758): \[test] Move pmd-test to java 8 - * [#3976](https://github.com/pmd/pmd/pull/3976): \[test] Extract xml schema module ### API Changes -#### Rule Test Framework - -* The module "pmd-test", which contains support classes to write rule tests, now **requires Java 8**. If you depend on - this module for testing your own custom rules, you'll need to make sure to use at least Java 8. -* The new module "pmd-test-schema" contains now the XSD schema and the code to parse the rule test XML files. The - schema has been extracted in order to easily share it with other tools like the Rule Designer or IDE plugins. -* Test schema changes: - * The attribute `isRegressionTest` of `test-code` is deprecated. The new - attribute `disabled` should be used instead for defining whether a rule test should be skipped or not. - * The attributes `reinitializeRule` and `useAuxClasspath` of `test-code` are deprecated and assumed true. - They will not be replaced. - * The new attribute `focused` of `test-code` allows disabling all tests except the focused one temporarily. -* More information about the rule test framework can be found in the documentation: - [Testing your rules](pmd_userdocs_extending_testing.html) - -#### Deprecated API - -* The experimental Java AST class {% jdoc java::lang.java.ast.ASTGuardedPattern %} has been deprecated and - will be removed. It was introduced for Java 17 and Java 18 Preview as part of pattern matching for switch, - but it is no longer supported with Java 19 Preview. -* The interface {% jdoc core::cpd.renderer.CPDRenderer %} is deprecated. For custom CPD renderers - the new interface {% jdoc core::cpd.renderer.CPDReportRenderer %} should be used. -* The class {% jdoc test::testframework.TestDescriptor %} is deprecated, replaced with {% jdoc test-schema::testframework.RuleTestDescriptor %}. -* Many methods of {% jdoc test::testframework.RuleTst %} have been deprecated as internal API. - -#### Experimental APIs - -* To support the Java preview language features "Pattern Matching for Switch" and "Record Patterns", the following - AST nodes have been introduced as experimental: - * {% jdoc java::lang.java.ast.ASTSwitchGuard %} - * {% jdoc java::lang.java.ast.ASTRecordPattern %} - * {% jdoc java::lang.java.ast.ASTComponentPatternList %} - -#### Internal API - -Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. -You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. - -* {%jdoc !!core::cpd.CPDConfiguration#setRenderer(net.sourceforge.pmd.cpd.Renderer) %} -* {%jdoc !!core::cpd.CPDConfiguration#setCPDRenderer(net.sourceforge.pmd.cpd.renderer.CPDRenderer) %} -* {%jdoc !!core::cpd.CPDConfiguration#getRenderer() %} -* {%jdoc !!core::cpd.CPDConfiguration#getCPDRenderer() %} -* {%jdoc !!core::cpd.CPDConfiguration#getRendererFromString(java.lang.String,java.lang.String) %} -* {%jdoc !!core::cpd.CPDConfiguration#getCPDRendererFromString(java.lang.String,java.lang.String) %} -* {%jdoc core::cpd.renderer.CPDRendererAdapter %} - ### External Contributions -* [#3984](https://github.com/pmd/pmd/pull/3984): \[java] Fix AddEmptyString false-negative issue - [@LiGaOg](https://github.com/LiGaOg) -* [#3988](https://github.com/pmd/pmd/pull/3988): \[java] Modify WhileLoopWithLiteralBoolean to meet the missing case #3455 - [@VoidxHoshi](https://github.com/VoidxHoshi) -* [#3992](https://github.com/pmd/pmd/pull/3992): \[java] FinalFieldCouldBeStatic - fix false negative with unnecessary parenthesis - [@dalizi007](https://github.com/dalizi007) -* [#3994](https://github.com/pmd/pmd/pull/3994): \[java] TooManyMethods - improve getter/setter detection (#3729) - [@341816041](https://github.com/341816041) -* [#4017](https://github.com/pmd/pmd/pull/4017): Add Gherkin support to CPD - [@ASBrouwers](https://github.com/ASBrouwers) -* [#4021](https://github.com/pmd/pmd/pull/4021): \[core] CPD: Add total number of tokens to XML reports - [@maikelsteneker](https://github.com/maikelsteneker) -* [#4056](https://github.com/pmd/pmd/pull/4056): \[apex] ApexSOQLInjection: Add support count query - [@gwilymatgearset](https://github.com/gwilymatgearset) -* [#4061](https://github.com/pmd/pmd/pull/4061): \[lua] Fix several related Lua parsing issues found when using CPD - [@matthargett](https://github.com/matthargett) {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 52432fe7c6..82a6f972a2 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,218 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 31-August-2022 - 6.49.0 + +The PMD team is pleased to announce PMD 6.49.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Updated PMD Designer](#updated-pmd-designer) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Deprecated API](#deprecated-api) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### Updated PMD Designer + +This PMD release ships a new version of the pmd-designer. +For the changes, see [PMD Designer Changelog](https://github.com/pmd/pmd-designer/releases/tag/6.49.0). + +### Fixed Issues + +* apex + * [#4096](https://github.com/pmd/pmd/issues/4096): \[apex] ApexAssertionsShouldIncludeMessage and ApexUnitTestClassShouldHaveAsserts: support new Assert class (introduced with Apex v56.0) +* core + * [#3970](https://github.com/pmd/pmd/issues/3970): \[core] FileCollector.addFile ignores language parameter +* java-codestyle + * [#4082](https://github.com/pmd/pmd/issues/4082): \[java] UnnecessaryImport false positive for on-demand imports of nested classes + +### API Changes + +#### Deprecated API + +* In order to reduce the dependency on Apex Jorje classes, the following methods have been deprecated. + These methods all leaked internal Jorje enums. These enums have been replaced now by enums the + PMD's AST package. + * ASTAssignmentExpression#getOperator + * ASTBinaryExpression#getOperator + * ASTBooleanExpression#getOperator + * ASTPostfixExpression#getOperator + * ASTPrefixExpression#getOperator + + All these classes have now a new `getOp()` method. Existing code should be refactored to use this method instead. + It returns the new enums, like AssignmentOperator, and avoids + the dependency to Jorje. + +### External Contributions + +* [#4081](https://github.com/pmd/pmd/pull/4081): \[apex] Remove Jorje leaks outside `ast` package - [@eklimo](https://github.com/eklimo) +* [#4083](https://github.com/pmd/pmd/pull/4083): \[java] UnnecessaryImport false positive for on-demand imports of nested classes (fix for #4082) - [@abyss638](https://github.com/abyss638) +* [#4092](https://github.com/pmd/pmd/pull/4092): \[apex] Implement ApexQualifiableNode for ASTUserEnum - [@aaronhurst-google](https://github.com/aaronhurst-google) +* [#4095](https://github.com/pmd/pmd/pull/4095): \[core] CPD: Added begin and end token to XML reports - [@pacvz](https://github.com/pacvz) +* [#4097](https://github.com/pmd/pmd/pull/4097): \[apex] ApexUnitTestClassShouldHaveAssertsRule: Support new Assert class (Apex v56.0) - [@tprouvot](https://github.com/tprouvot) +* [#4104](https://github.com/pmd/pmd/pull/4104): \[doc] Add MegaLinter in the list of integrations - [@nvuillam](https://github.com/nvuillam) + +### Stats +* 49 commits +* 10 closed tickets & PRs +* Days since last release: 32 + +## 30-July-2022 - 6.48.0 + +The PMD team is pleased to announce PMD 6.48.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Java 19 Support](#java-19-support) + * [Gherkin support](#gherkin-support) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [CPD CLI](#cpd-cli) + * [Rule Test Framework](#rule-test-framework) + * [Deprecated API](#deprecated-api) + * [Experimental APIs](#experimental-apis) + * [Internal API](#internal-api) +* [Financial Contributions](#financial-contributions) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### Java 19 Support + +This release of PMD brings support for Java 19. There are no new standard language features. + +PMD supports [JEP 427: Pattern Matching for switch (Third Preview)](https://openjdk.org/jeps/427) and +[JEP 405: Record Patterns (Preview)](https://openjdk.org/jeps/405) as preview language features. + +In order to analyze a project with PMD that uses these language features, +you'll need to enable it via the environment variable `PMD_JAVA_OPTS` and select the new language +version `19-preview`: + + export PMD_JAVA_OPTS=--enable-preview + ./run.sh pmd -language java -version 19-preview ... + +Note: Support for Java 17 preview language features have been removed. The version "17-preview" is no longer available. + +#### Gherkin support +Thanks to the contribution from [Anne Brouwers](https://github.com/ASBrouwers) PMD now has CPD support +for the [Gherkin](https://cucumber.io/docs/gherkin/) language. It is used to defined test cases for the +[Cucumber](https://cucumber.io/) testing tool for behavior-driven development. + +Being based on a proper Antlr grammar, CPD can: + +* ignore comments +* honor [comment-based suppressions](pmd_userdocs_cpd.html#suppression) + +### Fixed Issues +* apex + * [#4056](https://github.com/pmd/pmd/pull/4056): \[apex] ApexSOQLInjection: Add support count query +* core + * [#3796](https://github.com/pmd/pmd/issues/3796): \[core] CPD should also provide a `--debug` flag + * [#4021](https://github.com/pmd/pmd/pull/4021): \[core] CPD: Add total number of tokens to XML reports + * [#4031](https://github.com/pmd/pmd/issues/4031): \[core] If report is written to stdout, stdout should not be closed + * [#4051](https://github.com/pmd/pmd/issues/4051): \[doc] Additional rulesets are not listed in documentation + * [#4053](https://github.com/pmd/pmd/pull/4053): \[core] Allow building PMD under Java 18+ +* java + * [#4015](https://github.com/pmd/pmd/issues/4015): \[java] Support JDK 19 +* java-bestpractices + * [#3455](https://github.com/pmd/pmd/issues/3455): \[java] WhileLoopWithLiteralBoolean - false negative with complex expressions +* java-design + * [#3729](https://github.com/pmd/pmd/issues/3729): \[java] TooManyMethods ignores "real" methods which are named like getters or setters + * [#3949](https://github.com/pmd/pmd/issues/3949): \[java] FinalFieldCouldBeStatic - false negative with unnecessary parenthesis +* java-performance + * [#3625](https://github.com/pmd/pmd/issues/3625): \[java] AddEmptyString - false negative with empty var +* lua + * [#4061](https://github.com/pmd/pmd/pull/4061): \[lua] Fix several related Lua parsing issues found when using CPD +* test + * [#3302](https://github.com/pmd/pmd/pull/3302): \[test] Improve xml test schema + * [#3758](https://github.com/pmd/pmd/issues/3758): \[test] Move pmd-test to java 8 + * [#3976](https://github.com/pmd/pmd/pull/3976): \[test] Extract xml schema module + +### API Changes + +#### CPD CLI + +* CPD has a new CLI option `--debug`. This option has the same behavior as in PMD. It enables more verbose + logging output. + +#### Rule Test Framework + +* The module "pmd-test", which contains support classes to write rule tests, now **requires Java 8**. If you depend on + this module for testing your own custom rules, you'll need to make sure to use at least Java 8. +* The new module "pmd-test-schema" contains now the XSD schema and the code to parse the rule test XML files. The + schema has been extracted in order to easily share it with other tools like the Rule Designer or IDE plugins. +* Test schema changes: + * The attribute `isRegressionTest` of `test-code` is deprecated. The new + attribute `disabled` should be used instead for defining whether a rule test should be skipped or not. + * The attributes `reinitializeRule` and `useAuxClasspath` of `test-code` are deprecated and assumed true. + They will not be replaced. + * The new attribute `focused` of `test-code` allows disabling all tests except the focused one temporarily. +* More information about the rule test framework can be found in the documentation: + [Testing your rules](pmd_userdocs_extending_testing.html) + +#### Deprecated API + +* The experimental Java AST class ASTGuardedPattern has been deprecated and + will be removed. It was introduced for Java 17 and Java 18 Preview as part of pattern matching for switch, + but it is no longer supported with Java 19 Preview. +* The interface CPDRenderer is deprecated. For custom CPD renderers + the new interface CPDReportRenderer should be used. +* The class TestDescriptor is deprecated, replaced with RuleTestDescriptor. +* Many methods of RuleTst have been deprecated as internal API. + +#### Experimental APIs + +* To support the Java preview language features "Pattern Matching for Switch" and "Record Patterns", the following + AST nodes have been introduced as experimental: + * ASTSwitchGuard + * ASTRecordPattern + * ASTComponentPatternList + +#### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* CPDConfiguration#setRenderer +* CPDConfiguration#setCPDRenderer +* CPDConfiguration#getRenderer +* CPDConfiguration#getCPDRenderer +* CPDConfiguration#getRendererFromString +* CPDConfiguration#getCPDRendererFromString +* CPDRendererAdapter + +### Financial Contributions + +Many thanks to our sponsors: + +* [Matt Hargett](https://github.com/matthargett) (@matthargett) + +### External Contributions +* [#3984](https://github.com/pmd/pmd/pull/3984): \[java] Fix AddEmptyString false-negative issue - [@LiGaOg](https://github.com/LiGaOg) +* [#3988](https://github.com/pmd/pmd/pull/3988): \[java] Modify WhileLoopWithLiteralBoolean to meet the missing case #3455 - [@VoidxHoshi](https://github.com/VoidxHoshi) +* [#3992](https://github.com/pmd/pmd/pull/3992): \[java] FinalFieldCouldBeStatic - fix false negative with unnecessary parenthesis - [@dalizi007](https://github.com/dalizi007) +* [#3994](https://github.com/pmd/pmd/pull/3994): \[java] TooManyMethods - improve getter/setter detection (#3729) - [@341816041](https://github.com/341816041) +* [#4017](https://github.com/pmd/pmd/pull/4017): Add Gherkin support to CPD - [@ASBrouwers](https://github.com/ASBrouwers) +* [#4021](https://github.com/pmd/pmd/pull/4021): \[core] CPD: Add total number of tokens to XML reports - [@maikelsteneker](https://github.com/maikelsteneker) +* [#4056](https://github.com/pmd/pmd/pull/4056): \[apex] ApexSOQLInjection: Add support count query - [@gwilymatgearset](https://github.com/gwilymatgearset) +* [#4061](https://github.com/pmd/pmd/pull/4061): \[lua] Fix several related Lua parsing issues found when using CPD - [@matthargett](https://github.com/matthargett) + +### Stats +* 102 commits +* 26 closed tickets & PRs +* Days since last release: 35 + ## 25-June-2022 - 6.47.0 The PMD team is pleased to announce PMD 6.47.0. diff --git a/javacc-wrapper.xml b/javacc-wrapper.xml index 404c8c2fd8..c7f1d7eead 100644 --- a/javacc-wrapper.xml +++ b/javacc-wrapper.xml @@ -59,8 +59,8 @@ - - + + @@ -284,6 +284,25 @@ + + + + + + + + + + + + + + + + + + + @@ -348,6 +367,7 @@ @@ -396,7 +416,7 @@ public final class ${token-constants-name} \{${line.separator} * be used as a basis for a CPD Tokenizer. */ @net.sourceforge.pmd.annotation.InternalApi - public static net.sourceforge.pmd.lang.TokenManager<%%%API_PACK%%%.impl.javacc.JavaccToken> newTokenManager(%%%API_PACK%%%.CharStream cs) { + public static net.sourceforge.pmd.lang.TokenManager<%%%API_PACK%%%.impl.javacc.JavaccToken> newTokenManager(%%%API_PACK%%%.impl.javacc.CharStream cs) { return new %%%TOKEN_MGR_NAME%%%(cs); } @@ -405,6 +425,17 @@ public final class ${token-constants-name} \{${line.separator} + + }; + TOKEN_NAMES = java.util.Collections.unmodifiableList(java.util.Arrays.asList(tokenImage)); +]]> + + + diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index 3d37916f88..6a37f0e677 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -4,21 +4,25 @@ package net.sourceforge.pmd.lang.apex; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + import net.sourceforge.pmd.lang.BaseLanguageModule; -import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; import apex.jorje.services.Version; public class ApexLanguageModule extends BaseLanguageModule { - private static final String FIRST_EXTENSION = "cls"; - private static final String[] REMAINING_EXTENSIONS = {"trigger"}; public static final String NAME = "Apex"; public static final String TERSE_NAME = "apex"; - public static final String[] EXTENSIONS = CollectionUtil.listOf(FIRST_EXTENSION, REMAINING_EXTENSIONS).toArray(new String[0]); public ApexLanguageModule() { - super(NAME, null, TERSE_NAME, FIRST_EXTENSION, REMAINING_EXTENSIONS); + super(NAME, null, TERSE_NAME, listOf("cls", "trigger")); addVersion(String.valueOf((int) Version.CURRENT.getExternal()), new ApexHandler(), true); } + + public static Language getInstance() { + return LanguageRegistry.PMD.getLanguageByFullName(NAME); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java index 35563f7967..4ef11d130f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java @@ -19,7 +19,15 @@ public final class ASTAssignmentExpression extends AbstractApexNode { + private ApexQualifiedName qname; ASTUserEnum(UserEnum userEnum) { super(userEnum); @@ -17,4 +18,19 @@ public final class ASTUserEnum extends BaseApexClass { return visitor.visit(this, data); } + @Override + public ApexQualifiedName getQualifiedName() { + if (qname == null) { + + ASTUserClass parent = this.getFirstParentOfType(ASTUserClass.class); + + if (parent != null) { + qname = ApexQualifiedName.ofNestedEnum(parent.getQualifiedName(), this); + } else { + qname = ApexQualifiedName.ofOuterEnum(this); + } + } + + return qname; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java index ef356b4b19..eb41270bb3 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java @@ -37,9 +37,7 @@ public final class ApexParser implements Parser { final Compilation astRoot = CompilerService.INSTANCE.parseApex(task.getTextDocument()); - if (astRoot == null) { - throw new ParseException("Couldn't parse the source - there is not root node - Syntax Error??"); - } + assert astRoot != null : "Normally replaced by Compilation.INVALID"; String property = task.getProperties().getProperty(MULTIFILE_DIRECTORY); ApexMultifileAnalysis analysisHandler = ApexMultifileAnalysis.getAnalysisInstance(property); @@ -48,7 +46,7 @@ public final class ApexParser implements Parser { final ApexTreeBuilder treeBuilder = new ApexTreeBuilder(task); return treeBuilder.buildTree(astRoot, analysisHandler); } catch (apex.jorje.services.exception.ParseException e) { - throw new ParseException(e); + throw new ParseException(e).setFileName(task.getFileDisplayName()); } } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java index 17932f39b2..3f9bacef9a 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java @@ -159,6 +159,20 @@ public final class ApexQualifiedName { } + static ApexQualifiedName ofOuterEnum(ASTUserEnum astUserEnum) { + String ns = astUserEnum.getNamespace(); + String[] classes = {astUserEnum.getImage()}; + return new ApexQualifiedName(StringUtils.isEmpty(ns) ? "c" : ns, classes, null); + } + + + static ApexQualifiedName ofNestedEnum(ApexQualifiedName parent, ASTUserEnum astUserEnum) { + String[] classes = Arrays.copyOf(parent.classes, parent.classes.length + 1); + classes[classes.length - 1] = astUserEnum.getImage(); + return new ApexQualifiedName(parent.nameSpace, classes, null); + } + + private static String getOperationString(ASTMethod node) { StringBuilder sb = new StringBuilder(); sb.append(node.getImage()).append('('); @@ -182,6 +196,14 @@ public final class ApexQualifiedName { static ApexQualifiedName ofMethod(ASTMethod node) { + // Check first, as enum must be innermost potential parent + ASTUserEnum enumParent = node.ancestors(ASTUserEnum.class).first(); + if (enumParent != null) { + ApexQualifiedName baseName = enumParent.getQualifiedName(); + + return new ApexQualifiedName(baseName.nameSpace, baseName.classes, getOperationString(node)); + } + ASTUserClassOrInterface parent = node.ancestors(ASTUserClassOrInterface.class).firstOrThrow(); if (parent instanceof ASTUserTrigger) { ASTUserTrigger trigger = (ASTUserTrigger) parent; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AssignmentOperator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AssignmentOperator.java new file mode 100644 index 0000000000..6ee2bf6910 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AssignmentOperator.java @@ -0,0 +1,67 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.data.ast.AssignmentOp; + +/** + * Apex assignment operator + */ +public enum AssignmentOperator { + EQUALS("="), + ADDITION_EQUALS("+="), + SUBTRACTION_EQUALS("-="), + MULTIPLICATION_EQUALS("*="), + DIVISION_EQUALS("/="), + LEFT_SHIFT_EQUALS("<<="), + RIGHT_SHIFT_SIGNED_EQUALS(">>="), + RIGHT_SHIFT_UNSIGNED_EQUALS(">>>="), + BITWISE_AND_EQUALS("&="), + BITWISE_OR_EQUALS("|="), + BITWISE_XOR_EQUALS("^="); + + private final String symbol; + + AssignmentOperator(String symbol) { + this.symbol = symbol; + } + + @Override + public String toString() { + return this.symbol; + } + + /** + * Returns a {@link AssignmentOperator} corresponding to the given {@link AssignmentOp}. + */ + public static AssignmentOperator valueOf(AssignmentOp op) { + switch (op) { + case EQUALS: + return EQUALS; + case ADDITION_EQUALS: + return ADDITION_EQUALS; + case SUBTRACTION_EQUALS: + return SUBTRACTION_EQUALS; + case MULTIPLICATION_EQUALS: + return MULTIPLICATION_EQUALS; + case DIVISION_EQUALS: + return DIVISION_EQUALS; + case LEFT_SHIFT_EQUALS: + return LEFT_SHIFT_EQUALS; + case RIGHT_SHIFT_EQUALS: + return RIGHT_SHIFT_SIGNED_EQUALS; + case UNSIGNED_RIGHT_SHIFT_EQUALS: + return RIGHT_SHIFT_UNSIGNED_EQUALS; + case AND_EQUALS: + return BITWISE_AND_EQUALS; + case OR_EQUALS: + return BITWISE_OR_EQUALS; + case XOR_EQUALS: + return BITWISE_XOR_EQUALS; + default: + throw new IllegalArgumentException("Invalid assignment operator " + op); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BinaryOperator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BinaryOperator.java new file mode 100644 index 0000000000..ea18dce06b --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BinaryOperator.java @@ -0,0 +1,64 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.data.ast.BinaryOp; + +/** + * Apex binary operator + */ +public enum BinaryOperator { + ADDITION("+"), + SUBTRACTION("-"), + MULTIPLICATION("*"), + DIVISION("/"), + LEFT_SHIFT("<<"), + RIGHT_SHIFT_SIGNED(">>"), + RIGHT_SHIFT_UNSIGNED(">>>"), + BITWISE_AND("&"), + BITWISE_OR("|"), + BITWISE_XOR("^"); + + private final String symbol; + + BinaryOperator(String symbol) { + this.symbol = symbol; + } + + @Override + public String toString() { + return this.symbol; + } + + /** + * Returns a {@link BinaryOperator} corresponding to the given {@link BinaryOp}. + */ + public static BinaryOperator valueOf(BinaryOp op) { + switch (op) { + case ADDITION: + return ADDITION; + case SUBTRACTION: + return SUBTRACTION; + case MULTIPLICATION: + return MULTIPLICATION; + case DIVISION: + return DIVISION; + case LEFT_SHIFT: + return LEFT_SHIFT; + case RIGHT_SHIFT: + return RIGHT_SHIFT_SIGNED; + case UNSIGNED_RIGHT_SHIFT: + return RIGHT_SHIFT_UNSIGNED; + case AND: + return BITWISE_AND; + case OR: + return BITWISE_OR; + case XOR: + return BITWISE_XOR; + default: + throw new IllegalArgumentException("Invalid binary operator " + op); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BooleanOperator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BooleanOperator.java new file mode 100644 index 0000000000..e463eb2790 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/BooleanOperator.java @@ -0,0 +1,67 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.data.ast.BooleanOp; + +/** + * Apex boolean operator + */ +public enum BooleanOperator { + EQUAL("=="), + NOT_EQUAL("!="), + ALT_NOT_EQUAL("<>"), + EXACTLY_EQUAL("==="), + EXACTLY_NOT_EQUAL("!=="), + LESS_THAN("<"), + GREATER_THAN(">"), + LESS_THAN_OR_EQUAL("<="), + GREATER_THAN_OR_EQUAL(">="), + LOGICAL_AND("&&"), + LOGICAL_OR("||"); + + private final String symbol; + + BooleanOperator(String symbol) { + this.symbol = symbol; + } + + @Override + public String toString() { + return this.symbol; + } + + /** + * Returns a {@link BooleanOperator} corresponding to the given {@link BooleanOp}. + */ + public static BooleanOperator valueOf(BooleanOp op) { + switch (op) { + case DOUBLE_EQUAL: + return EQUAL; + case NOT_EQUAL: + return NOT_EQUAL; + case ALT_NOT_EQUAL: + return ALT_NOT_EQUAL; + case TRIPLE_EQUAL: + return EXACTLY_EQUAL; + case NOT_TRIPLE_EQUAL: + return EXACTLY_NOT_EQUAL; + case LESS_THAN: + return LESS_THAN; + case GREATER_THAN: + return GREATER_THAN; + case LESS_THAN_EQUAL: + return LESS_THAN_OR_EQUAL; + case GREATER_THAN_EQUAL: + return GREATER_THAN_OR_EQUAL; + case AND: + return LOGICAL_AND; + case OR: + return LOGICAL_OR; + default: + throw new IllegalArgumentException("Invalid boolean operator " + op); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PostfixOperator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PostfixOperator.java new file mode 100644 index 0000000000..5aa5a22756 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PostfixOperator.java @@ -0,0 +1,40 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.data.ast.PostfixOp; + +/** + * Apex postfix operator + */ +public enum PostfixOperator { + INCREMENT("++"), + DECREMENT("--"); + + private final String symbol; + + PostfixOperator(String symbol) { + this.symbol = symbol; + } + + @Override + public String toString() { + return this.symbol; + } + + /** + * Returns a {@link PostfixOperator} corresponding to the given {@link PostfixOp}. + */ + public static PostfixOperator valueOf(PostfixOp op) { + switch (op) { + case INC: + return INCREMENT; + case DEC: + return DECREMENT; + default: + throw new IllegalArgumentException("Invalid postfix operator " + op); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PrefixOperator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PrefixOperator.java new file mode 100644 index 0000000000..03c3b8379a --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/PrefixOperator.java @@ -0,0 +1,52 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.data.ast.PrefixOp; + +/** + * Apex prefix operator + */ +public enum PrefixOperator { + POSITIVE("+"), + NEGATIVE("-"), + LOGICAL_NOT("!"), + BITWISE_NOT("~"), + INCREMENT("++"), + DECREMENT("--"); + + private final String symbol; + + PrefixOperator(String symbol) { + this.symbol = symbol; + } + + @Override + public String toString() { + return this.symbol; + } + + /** + * Returns a {@link PrefixOperator} corresponding to the given {@link PrefixOp}. + */ + public static PrefixOperator valueOf(PrefixOp op) { + switch (op) { + case POSITIVE: + return POSITIVE; + case NEGATIVE: + return NEGATIVE; + case NOT: + return LOGICAL_NOT; + case BITWISE_COMPLEMENT: + return BITWISE_NOT; + case INC: + return INCREMENT; + case DEC: + return DECREMENT; + default: + throw new IllegalArgumentException("Invalid prefix operator " + op); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/ApexMetricsHelper.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/ApexMetricsHelper.java index 5a93ea7260..e8bdb67e4f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/ApexMetricsHelper.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/ApexMetricsHelper.java @@ -9,8 +9,7 @@ import java.util.Set; import net.sourceforge.pmd.lang.apex.ast.ASTBooleanExpression; import net.sourceforge.pmd.lang.apex.ast.ASTStandardCondition; - -import apex.jorje.data.ast.BooleanOp; +import net.sourceforge.pmd.lang.apex.ast.BooleanOperator; /** * @@ -34,8 +33,8 @@ public final class ApexMetricsHelper { int complexity = 0; for (ASTBooleanExpression sub : subs) { - BooleanOp op = sub.getOperator(); - if (op != null && (op == BooleanOp.AND || op == BooleanOp.OR)) { + BooleanOperator op = sub.getOp(); + if (op == BooleanOperator.LOGICAL_AND || op == BooleanOperator.LOGICAL_OR) { complexity++; } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/CognitiveComplexityVisitor.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/CognitiveComplexityVisitor.java index a771bde90e..20867060b5 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/CognitiveComplexityVisitor.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/internal/CognitiveComplexityVisitor.java @@ -19,11 +19,10 @@ import net.sourceforge.pmd.lang.apex.ast.ASTTernaryExpression; import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.ast.ApexVisitorBase; +import net.sourceforge.pmd.lang.apex.ast.BooleanOperator; +import net.sourceforge.pmd.lang.apex.ast.PrefixOperator; import net.sourceforge.pmd.lang.apex.metrics.internal.CognitiveComplexityVisitor.State; -import apex.jorje.data.ast.BooleanOp; -import apex.jorje.data.ast.PrefixOp; - /** * @author Gwilym Kuiper */ @@ -36,7 +35,7 @@ public class CognitiveComplexityVisitor extends ApexVisitorBase { private int complexity = 0; private int nestingLevel = 0; - private BooleanOp currentBooleanOperation = null; + private BooleanOperator currentBooleanOperation = null; private String methodName = null; public int getComplexity() { @@ -58,7 +57,7 @@ public class CognitiveComplexityVisitor extends ApexVisitorBase { complexity++; } - void booleanOperation(BooleanOp op) { + void booleanOperation(BooleanOperator op) { if (currentBooleanOperation != op) { if (op != null) { fundamentalComplexity(); @@ -171,8 +170,8 @@ public class CognitiveComplexityVisitor extends ApexVisitorBase { @Override public Void visit(ASTBooleanExpression node, State state) { - BooleanOp op = node.getOperator(); - if (op == BooleanOp.AND || op == BooleanOp.OR) { + BooleanOperator op = node.getOp(); + if (op == BooleanOperator.LOGICAL_AND || op == BooleanOperator.LOGICAL_OR) { state.booleanOperation(op); } @@ -182,8 +181,8 @@ public class CognitiveComplexityVisitor extends ApexVisitorBase { @Override public Void visit(ASTPrefixExpression node, State state) { - PrefixOp op = node.getOperator(); - if (op == PrefixOp.NOT) { + PrefixOperator op = node.getOp(); + if (op == PrefixOperator.LOGICAL_NOT) { state.booleanOperation(null); } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index 79e0e311e8..c00f59cff9 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -5,8 +5,6 @@ package net.sourceforge.pmd.lang.apex.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.apex.ast.ApexParserVisitor; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -14,10 +12,6 @@ import net.sourceforge.pmd.lang.rule.AbstractRule; public abstract class AbstractApexRule extends AbstractRule implements ApexParserVisitor { - public AbstractApexRule() { - super.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME)); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexAssertionsShouldIncludeMessageRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexAssertionsShouldIncludeMessageRule.java index 679935d617..2919206121 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexAssertionsShouldIncludeMessageRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexAssertionsShouldIncludeMessageRule.java @@ -12,17 +12,39 @@ public class ApexAssertionsShouldIncludeMessageRule extends AbstractApexUnitTest private static final String ASSERT = "System.assert"; private static final String ASSERT_EQUALS = "System.assertEquals"; private static final String ASSERT_NOT_EQUALS = "System.assertNotEquals"; + private static final String ARE_EQUAL = "Assert.areEqual"; + private static final String ARE_NOT_EQUAL = "Assert.areNotEqual"; + private static final String IS_FALSE = "Assert.isFalse"; + private static final String FAIL = "Assert.fail"; + private static final String IS_INSTANCE_OF_TYPE = "Assert.isInstanceOfType"; + private static final String IS_NOT_INSTANCE_OF_TYPE = "Assert.isNotInstanceOfType"; + private static final String IS_NOT_NULL = "Assert.isNotNull"; + private static final String IS_NULL = "Assert.isNull"; + private static final String IS_TRUE = "Assert.isTrue"; @Override public Object visit(ASTMethodCallExpression node, Object data) { String methodName = node.getFullMethodName(); - if (ASSERT.equalsIgnoreCase(methodName) && node.getNumChildren() == 2) { + if (FAIL.equalsIgnoreCase(methodName) && node.getNumChildren() == 1) { + addViolationWithMessage(data, node, + "''{0}'' should have 1 parameters.", + new Object[] { FAIL }); + } else if ((ASSERT.equalsIgnoreCase(methodName) + || IS_FALSE.equalsIgnoreCase(methodName) + || IS_NOT_NULL.equalsIgnoreCase(methodName) + || IS_NULL.equalsIgnoreCase(methodName) + || IS_TRUE.equalsIgnoreCase(methodName)) + && node.getNumChildren() == 2) { addViolationWithMessage(data, node, "''{0}'' should have 2 parameters.", - new Object[] { ASSERT }); + new Object[] { methodName }); } else if ((ASSERT_EQUALS.equalsIgnoreCase(methodName) - || ASSERT_NOT_EQUALS.equalsIgnoreCase(methodName)) + || ASSERT_NOT_EQUALS.equalsIgnoreCase(methodName) + || ARE_EQUAL.equalsIgnoreCase(methodName) + || ARE_NOT_EQUAL.equalsIgnoreCase(methodName) + || IS_INSTANCE_OF_TYPE.equalsIgnoreCase(methodName) + || IS_NOT_INSTANCE_OF_TYPE.equalsIgnoreCase(methodName)) && node.getNumChildren() == 3) { addViolationWithMessage(data, node, "''{0}'' should have 3 parameters.", diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java index 084a25daf6..a2f655d732 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java @@ -37,18 +37,39 @@ public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTest ASSERT_METHODS.add("system.assert"); ASSERT_METHODS.add("system.assertequals"); ASSERT_METHODS.add("system.assertnotequals"); + ASSERT_METHODS.add("assert.areequal"); + ASSERT_METHODS.add("assert.arenotequal"); + ASSERT_METHODS.add("assert.fail"); + ASSERT_METHODS.add("assert.isfalse"); + ASSERT_METHODS.add("assert.isinstanceoftype"); + ASSERT_METHODS.add("assert.isnotinstanceoftype"); + ASSERT_METHODS.add("assert.isnull"); + ASSERT_METHODS.add("assert.isnotnull"); + ASSERT_METHODS.add("assert.istrue"); // Fully-qualified variants...rare but still valid/possible ASSERT_METHODS.add("system.system.assert"); ASSERT_METHODS.add("system.system.assertequals"); ASSERT_METHODS.add("system.system.assertnotequals"); + ASSERT_METHODS.add("system.assert.areequal"); + ASSERT_METHODS.add("system.assert.arenotequal"); + ASSERT_METHODS.add("system.assert.fail"); + ASSERT_METHODS.add("system.assert.isfalse"); + ASSERT_METHODS.add("system.assert.isinstanceoftype"); + ASSERT_METHODS.add("system.assert.isnotinstanceoftype"); + ASSERT_METHODS.add("system.assert.isnull"); + ASSERT_METHODS.add("system.assert.isnotnull"); + ASSERT_METHODS.add("system.assert.istrue"); } - // Using a string property instead of a regex property to ensure that the compiled pattern can be case-insensitive - private static final PropertyDescriptor ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR = - stringProperty("additionalAssertMethodPattern") - .desc("A regular expression for one or more custom test assertion method patterns.").defaultValue("").build(); + // Using a string property instead of a regex property to ensure that the + // compiled pattern can be case-insensitive + private static final PropertyDescriptor ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR = stringProperty( + "additionalAssertMethodPattern") + .desc("A regular expression for one or more custom test assertion method patterns.").defaultValue("") + .build(); - // A simple compiled pattern cache to ensure that we only ever try to compile the configured pattern once for a given run + // A simple compiled pattern cache to ensure that we only ever try to compile + // the configured pattern once for a given run private Optional compiledAdditionalAssertMethodPattern = null; public ApexUnitTestClassShouldHaveAssertsRule() { @@ -81,7 +102,8 @@ public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTest } } - // If we didn't find assert method invocations the simple way and we have a configured pattern, try it + // If we didn't find assert method invocations the simple way and we have a + // configured pattern, try it if (!isAssertFound) { final String additionalAssertMethodPattern = getProperty(ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR); final Pattern compiledPattern = getCompiledAdditionalAssertMethodPattern(additionalAssertMethodPattern); @@ -105,12 +127,15 @@ public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTest private Pattern getCompiledAdditionalAssertMethodPattern(String additionalAssertMethodPattern) { if (StringUtils.isNotBlank(additionalAssertMethodPattern)) { - // Check for presence first since we will cache a null value for patterns that don't compile + // Check for presence first since we will cache a null value for patterns that + // don't compile if (compiledAdditionalAssertMethodPattern == null) { try { - compiledAdditionalAssertMethodPattern = Optional.of(Pattern.compile(additionalAssertMethodPattern, Pattern.CASE_INSENSITIVE)); + compiledAdditionalAssertMethodPattern = Optional + .of(Pattern.compile(additionalAssertMethodPattern, Pattern.CASE_INSENSITIVE)); } catch (IllegalArgumentException e) { - // Cache a null compiled pattern so that we won't try to compile this one again during the run + // Cache a null compiled pattern so that we won't try to compile this one again + // during the run compiledAdditionalAssertMethodPattern = Optional.ofNullable(null); throw e; } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/LanguageVersionTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/LanguageVersionTest.java index f668936b68..cad18b9163 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/LanguageVersionTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/LanguageVersionTest.java @@ -10,7 +10,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; import net.sourceforge.pmd.AbstractLanguageVersionTest; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; public class LanguageVersionTest extends AbstractLanguageVersionTest { @@ -22,6 +21,6 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { return Arrays.asList(new Object[][] { { ApexLanguageModule.NAME, ApexLanguageModule.TERSE_NAME, "35", - LanguageRegistry.getLanguage("Apex").getVersion("35"), }, }); + getLanguage("Apex").getVersion("35"), }, }); } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index b941d37c0a..1df895dc91 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -171,7 +171,7 @@ class ApexParserTest extends ApexParserTestBase { void parseInheritedSharingClass() throws IOException { String source = IOUtil.readToString(ApexParserTest.class.getResourceAsStream("InheritedSharing.cls"), StandardCharsets.UTF_8); - parse(source); + Assert.assertNotNull(parse(source)); } /** @@ -184,6 +184,7 @@ class ApexParserTest extends ApexParserTestBase { String source = IOUtil.readToString(ApexParserTest.class.getResourceAsStream("StackOverflowClass.cls"), StandardCharsets.UTF_8); ASTUserClassOrInterface rootNode = parse(source); + Assert.assertNotNull(rootNode); int count = visitPosition(rootNode, 0); assertEquals(487, count); diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java index 3c5ccd5f6a..a39a40ed23 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java @@ -8,6 +8,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; import org.junit.jupiter.api.Test; @@ -88,4 +91,32 @@ class ApexQualifiedNameTest extends ApexParserTestBase { ASTMethod m = root.descendants(ASTMethod.class).firstOrThrow(); assertEquals("c__trigger.Account#myAccountTrigger", m.getQualifiedName().toString()); } + + + @Test + public void testUnqualifiedEnum() { + ASTUserEnum root = (ASTUserEnum) parse("public enum primaryColor { RED, YELLOW, BLUE }"); + + ApexQualifiedName enumQName = root.getQualifiedName(); + List methods = root.descendants(ASTMethod.class).toList(); + + assertEquals("c__primaryColor", enumQName.toString()); + for (ASTMethod m : methods) { + assertTrue(m.getQualifiedName().toString().startsWith("c__primaryColor#")); + } + } + + @Test + public void testQualifiedEnum() { + ASTUserClass root = (ASTUserClass) parse("public class Outer { public enum Inner { OK } }"); + + ASTUserEnum enumNode = root.descendants(ASTUserEnum.class).firstOrThrow(); + ApexQualifiedName enumQName = enumNode.getQualifiedName(); + List methods = enumNode.descendants(ASTMethod.class).toList(); + + assertEquals("c__Outer.Inner", enumQName.toString()); + for (ASTMethod m : methods) { + assertTrue(m.getQualifiedName().toString().startsWith("c__Outer.Inner#")); + } + } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java index a0286ca0f7..b13d5aa921 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java @@ -9,11 +9,8 @@ import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSize; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase; import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; /** * @author daniels @@ -21,10 +18,7 @@ import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; class ApexXPathRuleTest extends ApexParserTestBase { private XPathRule makeXPath(String expression) { - XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, expression); - rule.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME)); - rule.setMessage("XPath Rule Failed"); - return rule; + return apex.newXpathRule(expression); } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexAssertionsShouldIncludeMessage.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexAssertionsShouldIncludeMessage.xml index 71790b39db..7a499d556a 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexAssertionsShouldIncludeMessage.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexAssertionsShouldIncludeMessage.xml @@ -46,6 +46,141 @@ public class Foo { System.assertEquals('Test opportunity', o.Name); System.assert(o.isClosed); } +} + ]]> + + + + [apex] Support new Assert class (Apex v56.0) #4097 + 0 + + + + + [apex] Support new Assert class (Apex v56.0) - negative test case #4097 + 9 + 6,12,19,28,34,40,46,52,58 + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml index 8e9520f15d..686ffd3cb6 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml @@ -76,13 +76,13 @@ private class C2_Assignment_Report_Job_Test { #1089 [apex] ApexUnitTestClassShouldHaveAsserts: Verify use of additionalAssertMethodPattern, positive test - (Assert\.\w+|verify\w+) + (MyAssert\.\w+|verify\w+) 0 + + + [apex] ApexUnitTestClassShouldHaveAssertsRule: Support new Assert class (Apex v56.0) #4097 + 0 + + diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index f37faf57b8..500a752818 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -13,29 +13,6 @@ - - org.apache.maven.plugins - maven-antrun-plugin - true - - - generate-sources - generate-sources - - - - - - - - - - run - - - - - org.codehaus.mojo build-helper-maven-plugin diff --git a/pmd-core/src/main/ant/alljavacc.xml b/pmd-core/src/main/ant/alljavacc.xml deleted file mode 100644 index d58a8251a5..0000000000 --- a/pmd-core/src/main/ant/alljavacc.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Using JavaCC home: ${javacc-home.path} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index ad1d9508a8..086f4fd019 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -165,7 +165,9 @@ public final class PMD { return StatusCode.ERROR; } - PMDConfiguration configuration = Objects.requireNonNull(parseResult.toConfiguration()); + PMDConfiguration configuration = Objects.requireNonNull( + parseResult.toConfiguration() + ); MessageReporter pmdReporter = setupMessageReporter(configuration); configuration.setReporter(pmdReporter); 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 314e4ce3ea..01ed36d138 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -94,6 +94,7 @@ import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; * */ public class PMDConfiguration extends AbstractConfiguration { + private static final LanguageRegistry DEFAULT_REGISTRY = LanguageRegistry.PMD; /** The default suppress marker string. */ public static final String DEFAULT_SUPPRESS_MARKER = "NOPMD"; @@ -102,7 +103,7 @@ public class PMDConfiguration extends AbstractConfiguration { private String suppressMarker = DEFAULT_SUPPRESS_MARKER; private int threads = Runtime.getRuntime().availableProcessors(); private ClassLoader classLoader = getClass().getClassLoader(); - private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer(); + private LanguageVersionDiscoverer languageVersionDiscoverer; private LanguageVersion forceLanguageVersion; private MessageReporter reporter = new SimpleMessageReporter(LoggerFactory.getLogger(PMD.class)); @@ -127,8 +128,18 @@ public class PMDConfiguration extends AbstractConfiguration { private boolean benchmark; private AnalysisCache analysisCache = new NoopAnalysisCache(); private boolean ignoreIncrementalAnalysis; + private final LanguageRegistry langRegistry; private boolean progressBar = false; + public PMDConfiguration() { + this(DEFAULT_REGISTRY); + } + + public PMDConfiguration(@NonNull LanguageRegistry languageRegistry) { + this.langRegistry = Objects.requireNonNull(languageRegistry); + this.languageVersionDiscoverer = new LanguageVersionDiscoverer(languageRegistry); + } + /** * Get the suppress marker. This is the source level marker used to indicate * a RuleViolation should be suppressed. @@ -351,7 +362,7 @@ public class PMDConfiguration extends AbstractConfiguration { // FUTURE Delete this? I can't think of a good reason to keep it around. // Failure to determine the LanguageVersion for a file should be a hard // error, or simply cause the file to be skipped? - public LanguageVersion getLanguageVersionOfFile(String fileName) { + public @Nullable LanguageVersion getLanguageVersionOfFile(String fileName) { LanguageVersion forcedVersion = getForceLanguageVersion(); if (forcedVersion != null) { // use force language if given @@ -359,13 +370,11 @@ public class PMDConfiguration extends AbstractConfiguration { } // otherwise determine by file extension - LanguageVersion languageVersion = languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); - if (languageVersion == null) { - // For compatibility with older code that does not always pass in - // a correct filename. - languageVersion = languageVersionDiscoverer.getDefaultLanguageVersion(LanguageRegistry.getDefaultLanguage()); - } - return languageVersion; + return languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); + } + + LanguageRegistry languages() { + return langRegistry; } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index 4455b29fd8..ce84aa200c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -163,9 +163,7 @@ public final class PmdAnalysis implements AutoCloseable { * } */ public RuleSetLoader newRuleSetLoader() { - RuleSetLoader loader = RuleSetLoader.fromPmdConfig(configuration); - loader.setReporter(this.reporter); - return loader; + return RuleSetLoader.fromPmdConfig(configuration); } /** @@ -347,6 +345,7 @@ public final class PmdAnalysis implements AutoCloseable { for (RuleSet ruleSet : ruleSets) { for (final Rule rule : ruleSet.getRules()) { final Language ruleLanguage = rule.getLanguage(); + Objects.requireNonNull(ruleLanguage, "Rule has no language " + rule); if (!languages.contains(ruleLanguage)) { final LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage); if (RuleSet.applies(rule, version)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index da7aed2728..0cab3390f6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -650,6 +650,7 @@ public class RuleSet implements ChecksumAware { public static boolean applies(Rule rule, LanguageVersion languageVersion) { final LanguageVersion min = rule.getMinimumLanguageVersion(); final LanguageVersion max = rule.getMaximumLanguageVersion(); + Objects.requireNonNull(rule.getLanguage(), "Rule has no language " + rule); return rule.getLanguage().equals(languageVersion.getLanguage()) && (min == null || min.compareTo(languageVersion) <= 0) && (max == null || max.compareTo(languageVersion) >= 0); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index ccd56add3a..1b1a594bdc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -45,6 +46,7 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; @@ -76,6 +78,7 @@ final class RuleSetFactory { private static final Logger LOG = LoggerFactory.getLogger(RuleSetFactory.class); private final ResourceLoader resourceLoader; + private final LanguageRegistry languageRegistry; private final RulePriority minimumPriority; private final boolean warnDeprecated; private final RuleSetFactoryCompatibility compatibilityFilter; @@ -85,12 +88,14 @@ final class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); RuleSetFactory(ResourceLoader resourceLoader, + LanguageRegistry languageRegistry, RulePriority minimumPriority, boolean warnDeprecated, RuleSetFactoryCompatibility compatFilter, boolean includeDeprecatedRuleReferences, MessageReporter reporter) { this.resourceLoader = resourceLoader; + this.languageRegistry = Objects.requireNonNull(languageRegistry); this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; this.includeDeprecatedRuleReferences = includeDeprecatedRuleReferences; @@ -476,7 +481,7 @@ final class RuleSetFactory { && !isRuleName(ruleNode, ruleSetReferenceId.getRuleName())) { return; } - Rule rule = new RuleFactory(resourceLoader).buildRule(ruleNode, err); + Rule rule = new RuleFactory(resourceLoader, languageRegistry).buildRule(ruleNode, err); rule.setRuleSetName(ruleSetBuilder.getName()); if (warnDeprecated && StringUtils.isBlank(ruleNode.getAttribute("language"))) { @@ -563,7 +568,7 @@ final class RuleSetFactory { RuleReference ruleReference; try { - ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleNode, err); + ruleReference = new RuleFactory(resourceLoader, languageRegistry).decorateRule(referencedRule, ruleSetReference, ruleNode, err); } catch (XmlException e) { throw err.at(ruleNode).error(e, "Error while parsing rule reference"); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java index c3a5373dae..99c7a49d4d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -37,6 +37,7 @@ import net.sourceforge.pmd.util.log.internal.NoopReporter; public final class RuleSetLoader { private static final Logger LOG = LoggerFactory.getLogger(RuleSetLoader.class); + private LanguageRegistry languageRegistry = LanguageRegistry.PMD; private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; @@ -52,8 +53,9 @@ public final class RuleSetLoader { // default } - void setReporter(MessageReporter reporter) { + RuleSetLoader withReporter(MessageReporter reporter) { this.reporter = reporter; + return this; } /** @@ -72,6 +74,11 @@ public final class RuleSetLoader { return this; } + public RuleSetLoader withLanguages(LanguageRegistry languageRegistry) { + this.languageRegistry = languageRegistry; + return this; + } + /** * Filter loaded rules to only those that match or are above * the given priority. The default is {@link RulePriority#LOW}, @@ -137,6 +144,7 @@ public final class RuleSetLoader { public RuleSetFactory toFactory() { return new RuleSetFactory( this.resourceLoader, + this.languageRegistry, this.minimumPriority, this.warnDeprecated, this.compatFilter, @@ -270,7 +278,9 @@ public final class RuleSetLoader { */ public static RuleSetLoader fromPmdConfig(PMDConfiguration configuration) { return new RuleSetLoader().filterAbovePriority(configuration.getMinimumPriority()) - .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); + .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()) + .withLanguages(configuration.languages()) + .withReporter(configuration.getReporter()); } @@ -289,7 +299,7 @@ public final class RuleSetLoader { public List getStandardRuleSets() { String rulesetsProperties; List ruleSetReferenceIds = new ArrayList<>(); - for (Language language : LanguageRegistry.getLanguages()) { + for (Language language : languageRegistry.getLanguages()) { Properties props = new Properties(); rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java index 192a714f6e..ac77e2540e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java @@ -49,6 +49,8 @@ public class PMDTask extends Task { try { PMDTaskImpl mirror = new PMDTaskImpl(this); mirror.execute(); + } catch (Exception e) { + throw new BuildException(e); } finally { Thread.currentThread().setContextClassLoader(oldClassloader); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index 3b1dd1fc4f..de1ec6b952 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -79,7 +79,7 @@ public class PMDTaskImpl { SourceLanguage version = task.getSourceLanguage(); if (version != null) { - Language lang = LanguageRegistry.findLanguageByTerseName(version.getName()); + Language lang = LanguageRegistry.PMD.getLanguageById(version.getName()); LanguageVersion languageVersion = lang == null ? null : lang.getVersion(version.getVersion()); if (languageVersion == null) { throw new BuildException("The following language is not supported:" + version + '.'); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java index 5e90676c06..d6e3ac6132 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.cli; import java.util.Properties; -import java.util.stream.Collectors; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMD.StatusCode; @@ -145,7 +144,7 @@ public final class PMDCommandLineInterface { private static String supportedVersions() { return "Languages and version suported:" + PMD.EOL - + LanguageRegistry.getLanguages().stream().map(Language::getTerseName).collect(Collectors.joining(", ")) + + LanguageRegistry.PMD.commaSeparatedList(Language::getId) + PMD.EOL; } 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 ecbadad53e..aaabd28711 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 @@ -10,6 +10,7 @@ import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.PMD; @@ -229,6 +230,22 @@ public class PMDParameters { * @throws IllegalArgumentException if the parameters are inconsistent or incomplete */ public PMDConfiguration toConfiguration() { + return toConfiguration(LanguageRegistry.PMD); + } + + /** + * Converts these parameters into a configuration. The given language + * registry is used to resolve references to languages in the parameters. + * + * @return A new PMDConfiguration corresponding to these parameters + * + * @throws IllegalArgumentException if the parameters are inconsistent or incomplete + */ + public @NonNull PMDConfiguration toConfiguration(LanguageRegistry registry) { + 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.getInputPaths()); configuration.setInputFilePath(this.getFileListPath()); @@ -253,12 +270,12 @@ public class PMDParameters { configuration.setIgnoreIncrementalAnalysis(this.isIgnoreIncrementalAnalysis()); configuration.setProgressBar(this.isProgressBar()); - LanguageVersion forceLangVersion = getForceLangVersion(); + LanguageVersion forceLangVersion = getForceLangVersion(registry); if (forceLangVersion != null) { configuration.setForceLanguageVersion(forceLangVersion); } - LanguageVersion languageVersion = getLangVersion(); + LanguageVersion languageVersion = getLangVersion(registry); if (languageVersion != null) { configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(languageVersion); } @@ -277,15 +294,6 @@ public class PMDParameters { } - /** - * {@link #toConfiguration()}. - * @deprecated To be removed in 7.0.0. Use the instance method {@link #toConfiguration()}. - */ - @Deprecated - public static PMDConfiguration transformParametersIntoConfiguration(PMDParameters params) { - return params.toConfiguration(); - } - public boolean isDebug() { return debug; } @@ -342,27 +350,23 @@ public class PMDParameters { return reportfile; } - private @Nullable LanguageVersion getLangVersion() { - Language lang = language != null ? LanguageRegistry.findLanguageByTerseName(language) - : LanguageRegistry.getDefaultLanguage(); - - return version != null ? lang.getVersion(version) - : lang.getDefaultVersion(); + private @Nullable LanguageVersion getLangVersion(LanguageRegistry registry) { + if (language != null) { + Language lang = registry.getLanguageById(language); + if (lang != null) { + return version != null ? lang.getVersion(version) + : lang.getDefaultVersion(); + } + } + return null; } - public String getVersion() { - if (version != null) { - return version; - } - return LanguageRegistry.findLanguageByTerseName(getLanguage()).getDefaultVersion().getVersion(); + public @Nullable String getLanguage() { + return language; } - public String getLanguage() { - return language != null ? language : LanguageRegistry.getDefaultLanguage().getTerseName(); - } - - private @Nullable LanguageVersion getForceLangVersion() { - Language lang = forceLanguage != null ? LanguageRegistry.findLanguageByTerseName(forceLanguage) : null; + private @Nullable LanguageVersion getForceLangVersion(LanguageRegistry registry) { + Language lang = forceLanguage != null ? registry.getLanguageById(forceLanguage) : null; return lang != null ? lang.getDefaultVersion() : null; } 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 2f863ba4d9..35f8a9ad08 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 @@ -11,6 +11,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.PMDConfiguration; import com.beust.jcommander.JCommander; @@ -83,8 +85,12 @@ public final class PmdParametersParseResult { * Returns the resulting configuration if parsing succeeded and neither {@link #isHelp()} nor {@link #isVersion()} is requested. * Otherwise returns null. */ - public PMDConfiguration toConfiguration() { - return result != null && !isHelp() && !isVersion() ? result.toConfiguration() : null; + public @Nullable PMDConfiguration toConfiguration() { + return isValidParameterSet() ? result.toConfiguration() : null; + } + + private boolean isValidParameterSet() { + return result != null && !isHelp() && !isVersion(); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index e3ff04fd98..fb8c43b39d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -19,10 +19,12 @@ import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.cli.internal.CliMessages; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.util.FileFinder; import net.sourceforge.pmd.util.IOUtil; @@ -31,7 +33,8 @@ import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; public class CPD { - private static final Logger LOG = LoggerFactory.getLogger(CPD.class); + // not final, in order to re-initialize logging + private static Logger log = LoggerFactory.getLogger(CPD.class); private CPDConfiguration configuration; @@ -55,8 +58,10 @@ public class CPD { } public void go() { + log.debug("Running match algorithm on {} files...", source.size()); matchAlgorithm = new MatchAlgorithm(source, tokens, configuration.getMinimumTileSize(), listener); matchAlgorithm.findMatches(); + log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); } public Iterator getMatches() { @@ -81,6 +86,7 @@ public class CPD { if (!dir.exists()) { throw new FileNotFoundException("Couldn't find directory " + dir); } + log.debug("Searching directory " + dir + " for files"); FileFinder finder = new FileFinder(); // TODO - could use SourceFileSelector here add(finder.findFilesFrom(dir, configuration.filenameFilter(), recurse)); @@ -119,19 +125,19 @@ public class CPD { DBMSMetadata dbmsmetadata = new DBMSMetadata(dburi); List sourceObjectList = dbmsmetadata.getSourceObjectList(); - LOG.debug("Located {} database source objects", sourceObjectList.size()); + log.debug("Located {} database source objects", sourceObjectList.size()); for (SourceObject sourceObject : sourceObjectList) { // Add DBURI as a faux-file String falseFilePath = sourceObject.getPseudoFileName(); - LOG.trace("Adding database source object {}", falseFilePath); + log.trace("Adding database source object {}", falseFilePath); SourceCode sourceCode = configuration.sourceCodeFor(dbmsmetadata.getSourceCode(sourceObject), falseFilePath); add(sourceCode); } } catch (Exception sqlException) { - LOG.error("Problem with Input URI", sqlException); + log.error("Problem with Input URI", sqlException); throw new RuntimeException("Problem with DBURI: " + dburi, sqlException); } } @@ -146,6 +152,7 @@ public class CPD { } private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException { + log.debug("Tokenizing {}", sourceCode.getFileName()); configuration.tokenizer().tokenize(sourceCode, tokens); listener.addedFile(1, new File(sourceCode.getFileName())); source.put(sourceCode.getFileName(), sourceCode); @@ -207,6 +214,24 @@ public class CPD { return statusCode; } + // only reconfigure logging, if debug flag was used on command line + // otherwise just use whatever is in conf/simplelogger.properties which happens automatically + if (arguments.isDebug()) { + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); + } + // always need to reload the logger with the new/changed configuration + // unit tests might reset the logging configuration + log = LoggerFactory.getLogger(CPD.class); + + // TODO CLI errors should also be reported through this + // TODO this should not use the logger as backend, otherwise without + // slf4j implementation binding, errors are entirely ignored. + // always install java.util.logging to slf4j bridge + Slf4jSimpleConfiguration.installJulBridge(); + // logging, mostly for testing purposes + Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); + log.info("Log level is at {}", defaultLogLevel); + CPD cpd = new CPD(arguments); try { @@ -231,8 +256,8 @@ public class CPD { statusCode = StatusCode.OK; } } catch (IOException | RuntimeException e) { - LOG.debug(e.toString(), e); - LOG.error(CliMessages.errorDetectedMessage(1, CPDCommandLineInterface.PROGRAM_NAME)); + log.debug(e.toString(), e); + log.error(CliMessages.errorDetectedMessage(1, CPDCommandLineInterface.PROGRAM_NAME)); statusCode = StatusCode.ERROR; } return statusCode; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 72bf7b61ff..0b322797d4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -141,6 +141,9 @@ public class CPDConfiguration extends AbstractConfiguration { description = "By default CPD exits with status 4 if code duplications are found. Disable this option with '-failOnViolation false' to exit with 0 instead and just write the report.") private boolean failOnViolation = true; + @Parameter(names = { "--debug", "--verbose" }, description = "Debug mode.") + private boolean debug = false; + // this has to be a public static class, so that JCommander can use it! public static class LanguageConverter implements IStringConverter { @@ -540,4 +543,14 @@ public class CPDConfiguration extends AbstractConfiguration { public void setFailOnViolation(boolean failOnViolation) { this.failOnViolation = failOnViolation; } + + @Override + public boolean isDebug() { + return debug; + } + + @Override + public void setDebug(boolean debug) { + this.debug = debug; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index c741a337ad..30e13c4b04 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -35,6 +35,10 @@ public class Mark implements Comparable { return this.token.getBeginColumn(); // TODO Java 1.8 make optional } + public int getBeginTokenIndex() { + return this.token.getIndex(); + } + public int getEndLine() { return getBeginLine() + getLineCount() - 1; } @@ -48,6 +52,10 @@ public class Mark implements Comparable { return this.endToken == null ? -1 : this.endToken.getEndColumn(); // TODO Java 1.8 make optional } + public int getEndTokenIndex() { + return this.endToken == null ? -1 : this.endToken.getIndex(); + } + public int getLineCount() { return this.lineCount; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java index 39f3163080..67cafe395e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java @@ -152,6 +152,12 @@ public final class XMLRenderer implements Renderer, CPDRenderer, CPDReportRender if (endCol != -1) { file.setAttribute("endcolumn", String.valueOf(endCol)); } + final int beginIndex = mark.getBeginTokenIndex(); + final int endIndex = mark.getEndTokenIndex(); + file.setAttribute("begintoken", String.valueOf(beginIndex)); + if (endIndex != -1) { + file.setAttribute("endtoken", String.valueOf(endIndex)); + } duplication.appendChild(file); } return duplication; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java index 295bf9f7fa..3c45b96033 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java @@ -13,10 +13,10 @@ import net.sourceforge.pmd.cpd.Tokens; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStreamFactory; +import net.sourceforge.pmd.lang.ast.FileAnalysisException; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.document.CpdCompat; import net.sourceforge.pmd.lang.document.TextDocument; @@ -24,11 +24,11 @@ public abstract class JavaCCTokenizer implements Tokenizer { @SuppressWarnings("PMD.CloseResource") protected TokenManager getLexerForSource(TextDocument sourceCode) throws IOException { - return makeLexerImpl(makeCharStream(sourceCode)); + return makeLexerImpl(CharStream.create(sourceCode, tokenBehavior())); } - protected CharStream makeCharStream(TextDocument sourceCode) { - return CharStreamFactory.simpleCharStream(sourceCode); + protected TokenDocumentBehavior tokenBehavior() { + return TokenDocumentBehavior.DEFAULT; } protected abstract TokenManager makeLexerImpl(CharStream sourceCode); @@ -55,7 +55,7 @@ public abstract class JavaCCTokenizer implements Tokenizer { tokenEntries.add(processToken(tokenEntries, currentToken)); currentToken = tokenFilter.getNextToken(); } - } catch (TokenMgrError e) { + } catch (FileAnalysisException e) { throw e.setFileName(sourceCode.getFileName()); } finally { tokenEntries.add(TokenEntry.getEOF()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java index 4491ff2a42..1303795db5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java @@ -33,10 +33,17 @@ public abstract class BaseLanguageModule implements Language { String terseName, String firstExtension, String... otherExtensions) { + this(name, shortName, terseName, CollectionUtil.listOf(firstExtension, otherExtensions)); + } + + public BaseLanguageModule(String name, + String shortName, + String terseName, + List extensions) { this.name = name; this.shortName = shortName; this.terseName = terseName; - this.extensions = CollectionUtil.listOf(firstExtension, otherExtensions); + this.extensions = CollectionUtil.defensiveUnmodifiableCopy(extensions); } private void addVersion(String version, LanguageVersionHandler languageVersionHandler, boolean isDefault, String... versionAliases) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java index a755d4cddd..c2ebbc1729 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java @@ -61,6 +61,19 @@ public interface Language extends Comparable { */ String getTerseName(); + + /** + * Returns the ID of this language. This is a short, alphanumeric, + * lowercase name, eg {@code "java"}. It's used to identify the language + * in the ruleset XML, and is also in the package name of the language + * module. + * + * @return The ID of this language. + */ + default String getId() { + return getTerseName(); + } + /** * Returns the list of file extensions associated with this language. * This list is unmodifiable. Extensions do not have a '.' prefix. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index 73859c2547..26731bd3fe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -5,64 +5,146 @@ package net.sourceforge.pmd.lang; import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; import java.util.Set; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; -import net.sourceforge.pmd.internal.LanguageServiceBase; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sourceforge.pmd.annotation.DeprecatedUntil700; +import net.sourceforge.pmd.util.CollectionUtil; /** - * Provides access to the registered PMD languages. These are found - * from the classpath of the {@link ClassLoader} of this class. + * A set of languages with convenient methods. In the PMD CLI, languages + * are loaded from the classloader of this class. These are in the registry + * {@link #PMD}. You can otherwise create different registries with different + * languages, eg filter some out. */ -public final class LanguageRegistry extends LanguageServiceBase { +public final class LanguageRegistry implements Iterable { - // sort languages by name. Avoiding differences in the order of languages - // across JVM versions / OS. - private static final Comparator LANGUAGE_COMPARATOR = new Comparator() { - @Override - public int compare(Language o1, Language o2) { - return o1.getTerseName().compareToIgnoreCase(o2.getTerseName()); - } - }; + private static final Logger LOG = LoggerFactory.getLogger(LanguageRegistry.class); - private static final NameExtractor NAME_EXTRACTOR = new NameExtractor() { - @Override - public String getName(Language language) { - return language.getName(); - } - }; + /** + * Contains the languages that support PMD and are found on the classpath + * of the classloader of this class. This can be used as a "default" registry. + */ + public static final LanguageRegistry PMD = loadLanguages(LanguageRegistry.class.getClassLoader()); - private static final NameExtractor TERSE_NAME_EXTRACTOR = new NameExtractor() { - @Override - public String getName(Language language) { - return language.getTerseName(); - } - }; + private final Set languages; - // Important: the INSTANCE needs to be defined *after* LANGUAGE_COMPARATOR and *NAME_EXTRACTOR - // as these are needed in the constructor. - private static final LanguageRegistry INSTANCE = new LanguageRegistry(); + private final Map languagesById; + private final Map languagesByFullName; - private LanguageRegistry() { - super(Language.class, LANGUAGE_COMPARATOR, NAME_EXTRACTOR, TERSE_NAME_EXTRACTOR); + /** + * Create a new registry that contains the given set of languages. + * @throws NullPointerException If the parameter is null + */ + public LanguageRegistry(Set languages) { + this.languages = languages.stream() + .sorted(Comparator.comparing(Language::getTerseName, String::compareToIgnoreCase)) + .collect(CollectionUtil.toUnmodifiableSet()); + this.languagesById = CollectionUtil.associateBy(languages, Language::getTerseName); + this.languagesByFullName = CollectionUtil.associateBy(languages, Language::getName); + } + + @Override + public @NonNull Iterator iterator() { + return languages.iterator(); } /** - * @deprecated Use the static methods instead, will be made private + * Create a new registry by loading the languages registered via {@link ServiceLoader} + * on the classpath of the given classloader. + * + * @param classLoader A classloader */ - @Deprecated - public static LanguageRegistry getInstance() { - return INSTANCE; + public static @NonNull LanguageRegistry loadLanguages(ClassLoader classLoader) { + // sort languages by terse name. Avoiding differences in the order of languages + // across JVM versions / OS. + Set languages = new TreeSet<>(Comparator.comparing(Language::getTerseName, String::compareToIgnoreCase)); + ServiceLoader languageLoader = ServiceLoader.load(Language.class, classLoader); + Iterator iterator = languageLoader.iterator(); + while (true) { + // this loop is weird, but both hasNext and next may throw ServiceConfigurationError, + // it's more robust that way + try { + if (iterator.hasNext()) { + Language language = iterator.next(); + languages.add(language); + } else { + break; + } + } catch (UnsupportedClassVersionError | ServiceConfigurationError e) { + // Some languages require java8 and are therefore only available + // if java8 or later is used as runtime. + LOG.warn("Cannot load PMD language, ignored", e); + } + } + return new LanguageRegistry(languages); } /** * Returns a set of all the known languages. The ordering of the languages * is by terse name. */ - public static Set getLanguages() { - return INSTANCE.languages; + public Set getLanguages() { + return languages; + } + + /** + * Returns a language from its {@linkplain Language#getName() full name} + * (eg {@code "Java"}). This is case sensitive. + * + * @param languageName Language name + * + * @return A language, or null if the name is unknown + * + * @deprecated Use {@link #getLanguageByFullName(String) PMD.getLanguageByFullName} + */ + @Deprecated + @DeprecatedUntil700 + public static Language getLanguage(String languageName) { + return PMD.getLanguageByFullName(languageName); + } + + /** + * Returns a language from its {@linkplain Language#getId() ID} + * (eg {@code "java"}). This is case-sensitive. + * + * @param langId Language ID + * + * @return A language, or null if the name is unknown, or the parameter is null + */ + public @Nullable Language getLanguageById(@Nullable String langId) { + return languagesById.get(langId); + } + + /** + * Returns a language version from its {@linkplain Language#getId() language ID} + * (eg {@code "java"}). This is case-sensitive. + * + * @param langId Language ID + * @param version Version ID + * + * @return A language, or null if the name is unknown + */ + public @Nullable LanguageVersion getLanguageVersionById(@Nullable String langId, @Nullable String version) { + Language lang = languagesById.get(langId); + if (lang == null) { + return null; + } + return version == null ? lang.getDefaultVersion() + : lang.getVersion(version); } /** @@ -73,26 +155,8 @@ public final class LanguageRegistry extends LanguageServiceBase { * * @return A language, or null if the name is unknown */ - public static Language getLanguage(String languageName) { - return INSTANCE.languagesByName.get(languageName); - } - - /** - * Returns a "default language" known to the service loader. This - * is the Java language if available, otherwise an arbitrary one. - * If no languages are loaded, returns null. - * - * @return A language, or null if the name is unknown - */ - public static Language getDefaultLanguage() { - Language defaultLanguage = getLanguage("Java"); - if (defaultLanguage == null) { - Collection allLanguages = getInstance().languagesByName.values(); - if (!allLanguages.isEmpty()) { - defaultLanguage = allLanguages.iterator().next(); - } - } - return defaultLanguage; + public @Nullable Language getLanguageByFullName(String languageName) { + return languagesByFullName.get(languageName); } /** @@ -102,19 +166,27 @@ public final class LanguageRegistry extends LanguageServiceBase { * @param terseName Language terse name * * @return A language, or null if the name is unknown + * + * @deprecated Use {@link #getLanguageById(String) PMD.getLanguageById}. */ - public static Language findLanguageByTerseName(String terseName) { - return INSTANCE.languagesByTerseName.get(terseName); + @Deprecated + @DeprecatedUntil700 + public static @Nullable Language findLanguageByTerseName(@Nullable String terseName) { + return PMD.getLanguageById(terseName); } /** * Returns all languages that support the given extension. * * @param extensionWithoutDot A file extension (without '.' prefix) + * + * @deprecated Not replaced, extension will be extended to match full name in PMD 7. */ + @Deprecated + @DeprecatedUntil700 public static List findByExtension(String extensionWithoutDot) { List languages = new ArrayList<>(); - for (Language language : getLanguages()) { + for (Language language : PMD.getLanguages()) { if (language.hasExtension(extensionWithoutDot)) { languages.add(language); } @@ -122,4 +194,13 @@ public final class LanguageRegistry extends LanguageServiceBase { return languages; } + /** + * Formats the set of languages with the given formatter, sort and + * join everything with commas. Convenience method. + */ + public @NonNull String commaSeparatedList(Function languageToString) { + return getLanguages().stream().map(languageToString).sorted().collect(Collectors.joining(", ")); + } + + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java index b1ac43e348..84324e3357 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java @@ -24,7 +24,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * *

Example usage: *

- * Language javaLanguage = LanguageRegistry.{@link LanguageRegistry#getLanguage(String) getLanguage}("Java");
+ * Language javaLanguage = LanguageRegistry.PMD.{@link LanguageRegistry#getLanguageById(String) getLanguageById}("java");
  * LanguageVersion java11 = javaLanguage.{@link Language#getVersion(String) getVersion}("11");
  * LanguageVersionHandler handler = java11.getLanguageVersionHandler();
  * Parser parser = handler.getParser(handler.getDefaultParserOptions());
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
index 4e22b03a4f..927e6572d3 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
@@ -9,9 +9,12 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
+import net.sourceforge.pmd.annotation.DeprecatedUntil700;
 import net.sourceforge.pmd.internal.util.AssertionUtil;
 
 /**
@@ -20,13 +23,11 @@ import net.sourceforge.pmd.internal.util.AssertionUtil;
  * here.
  */
 public class LanguageVersionDiscoverer {
-    private Map languageToLanguageVersion = new HashMap<>();
 
+    private final LanguageRegistry languageRegistry;
+    private final Map languageToLanguageVersion = new HashMap<>();
     private LanguageVersion forcedVersion;
 
-    public LanguageVersionDiscoverer() {
-        this(null);
-    }
 
     /**
      * Build a new instance.
@@ -35,10 +36,18 @@ public class LanguageVersionDiscoverer {
      *                      The methods of this class still work as usual and do not
      *                      care about the forced language version.
      */
-    public LanguageVersionDiscoverer(LanguageVersion forcedVersion) {
+    public LanguageVersionDiscoverer(LanguageRegistry registry, LanguageVersion forcedVersion) {
+        this.languageRegistry = registry;
         this.forcedVersion = forcedVersion;
     }
 
+    /**
+     * Build a new instance with no forced version.
+     */
+    public LanguageVersionDiscoverer(LanguageRegistry registry) {
+        this(registry, null);
+    }
+
     /**
      * Set the given LanguageVersion as the current default for it's Language.
      *
@@ -96,7 +105,7 @@ public class LanguageVersionDiscoverer {
      *         null if there are no supported Languages for the
      *         file.
      */
-    public LanguageVersion getDefaultLanguageVersionForFile(String fileName) {
+    public @Nullable LanguageVersion getDefaultLanguageVersionForFile(String fileName) {
         List languages = getLanguagesForFile(fileName);
         LanguageVersion languageVersion = null;
         if (!languages.isEmpty()) {
@@ -119,7 +128,11 @@ public class LanguageVersionDiscoverer {
      * @param sourceFile
      *            The file.
      * @return The Languages for the source file, may be empty.
+     *
+     * @deprecated PMD 7 avoids using {@link File}.
      */
+    @Deprecated
+    @DeprecatedUntil700
     public List getLanguagesForFile(File sourceFile) {
         return getLanguagesForFile(sourceFile.getName());
     }
@@ -133,7 +146,9 @@ public class LanguageVersionDiscoverer {
      */
     public List getLanguagesForFile(String fileName) {
         String extension = getExtension(fileName);
-        return LanguageRegistry.findByExtension(extension);
+        return languageRegistry.getLanguages().stream()
+                               .filter(it -> it.hasExtension(extension))
+                               .collect(Collectors.toList());
     }
 
     // Get the extensions from a file
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/CharStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/CharStream.java
deleted file mode 100644
index 5cf6044ae7..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/CharStream.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.ast;
-
-
-import java.io.IOException;
-
-import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
-
-/**
- * PMD flavour of character streams used by JavaCC parsers.
- *
- * TODO for when all JavaCC languages are aligned:
- *   * rename methods to match decent naming conventions
- *   * move to impl.javacc package
- */
-public interface CharStream {
-
-    /**
-     * Returns the next character from the input. After a {@link #backup(int)},
-     * some of the already read chars must be spit out again.
-     *
-     * @return The next character
-     *
-     * @throws IOException  If the underlying char stream throws
-     */
-    char readChar() throws IOException;
-
-
-    /**
-     * Calls {@link #readChar()} and returns its value, marking its position
-     * as the beginning of the next token. All characters must remain in
-     * the buffer between two successive calls to this method to implement
-     * backup correctly.
-     */
-    char BeginToken() throws IOException; // SUPPRESS CHECKSTYLE we'll rename it later
-
-
-    /**
-     * Returns a string made up of characters from the token mark up to
-     * to the current buffer position.
-     */
-    String GetImage(); // SUPPRESS CHECKSTYLE we'll rename it later
-
-
-    /**
-     * Returns an array of characters that make up the suffix of length 'len' for
-     * the current token. This is used to build up the matched string
-     * for use in actions in the case of MORE. A simple and inefficient
-     * implementation of this is as follows :
-     *
-     * 
{@code
-     * String t = tokenImage();
-     * return t.substring(t.length() - len, t.length()).toCharArray();
-     * }
- * - * @param len Length of the returned array - * - * @return The suffix - * - * @throws IndexOutOfBoundsException If len is greater than the length of the - * current token - */ - char[] GetSuffix(int len); // SUPPRESS CHECKSTYLE we'll rename it later - - - /** - * Pushes a given number of already read chars into the buffer. - * Subsequent calls to {@link #readChar()} will read those characters - * before proceeding to read the underlying char stream. - * - *

A lexer calls this method if it has already read some characters, - * but cannot use them to match a (longer) token. So, they will - * be used again as the prefix of the next token. - * - * @throws AssertionError If the requested amount is greater than the - * number of read chars - */ - void backup(int amount); - - @Deprecated - int getBeginColumn(); - - @Deprecated - int getBeginLine(); - - - /** Returns the column number of the last character for the current token. */ - int getEndColumn(); - - - /** Returns the line number of the last character for current token. */ - int getEndLine(); - - // These methods are added by PMD - - - /** - * Returns the token document for the tokens being built. Having it - * here is the most convenient place for the time being. - */ - default JavaccTokenDocument getTokenDocument() { - return null; // for VelocityCharStream - } - - - /** Returns the start offset of the current token (in the original source), inclusive. */ - default int getStartOffset() { - return -1; - } - - - /** Returns the end offset of the current token (in the original source), exclusive. */ - default int getEndOffset() { - return -1; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java index 1619de12b8..0c7f9de52c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.ast; import java.util.Objects; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.document.TextFile; @@ -39,7 +40,7 @@ public class FileAnalysisException extends RuntimeException { super(message, cause); } - FileAnalysisException setFileName(String filename) { + public FileAnalysisException setFileName(String filename) { this.filename = Objects.requireNonNull(filename); return this; } @@ -55,6 +56,22 @@ public class FileAnalysisException extends RuntimeException { return filename; } + @Override + public String getMessage() { + return errorKind() + StringUtils.uncapitalize(positionToString()) + ": " + super.getMessage(); + } + + protected String errorKind() { + return "Error"; + } + + protected String positionToString() { + if (hasFileName()) { + return " in file '" + getFileName() + "'"; + } + return ""; + } + /** * Wraps the cause into an analysis exception. If it is itself an analysis diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java index f7256447bb..0912714a99 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/GenericToken.java @@ -6,8 +6,11 @@ package net.sourceforge.pmd.lang.ast; import java.util.Iterator; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.internal.util.IteratorUtil; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.reporting.Reportable; @@ -37,18 +40,37 @@ public interface GenericToken> extends Comparable, T getPreviousComment(); /** - * Returns the token's text. + * Returns the token's text as a string. */ default String getImage() { return getImageCs().toString(); } + /** - * Returns the image as a {@link CharSequence}. + * Returns the text of the token as a char sequence. + * This should be preferred when you can use eg {@link StringUtils} + * to do some processing, without having to create a string. */ CharSequence getImageCs(); + /** + * Returns true if the image of this token equals + * the given charsequence. This does not create a + * string. + * + * @param charSeq A character sequence + */ + default boolean imageEquals(CharSequence charSeq) { + CharSequence imageCs = getImageCs(); + if (imageCs instanceof Chars) { + return ((Chars) imageCs).contentEquals(charSeq); + } + return StringUtils.equals(imageCs, charSeq); + } + + /** Returns a text region with the coordinates of this token. */ TextRegion getRegion(); @@ -58,6 +80,7 @@ public interface GenericToken> extends Comparable, */ boolean isEof(); + /** * Returns true if this token is implicit, ie was inserted artificially * and has a zero-length image. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java index c90e342a97..c9c2e14816 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java @@ -132,7 +132,7 @@ public interface Node extends Reportable { // Those are kept here because they're handled specially as XPath - // attributes + // attributes, for now @Override default int getBeginLine() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java index ece028bb13..8b27689406 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java @@ -40,11 +40,6 @@ public class ParseException extends FileAnalysisException { this.currentToken = null; } - public ParseException(String message, Throwable cause) { - super(message, cause); - this.currentToken = null; - } - public ParseException(String message, JavaccToken token) { super(message); this.currentToken = token; @@ -59,6 +54,11 @@ public class ParseException extends FileAnalysisException { currentToken = currentTokenVal; } + @Override + protected String errorKind() { + return "Parse exception"; + } + /** * It uses "currentToken" and "expectedTokenSequences" to generate a parse * error message and returns it. If this object has been created diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java index ca252c0200..0aa8419fb4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java @@ -46,21 +46,28 @@ public interface Parser { private final SemanticErrorReporter reporter; private final ClassLoader auxclasspathClassLoader; - private final PropertySource propertySource; + private final ParserTaskProperties propertySource; public ParserTask(TextDocument textDoc, SemanticErrorReporter reporter, ClassLoader auxclasspathClassLoader) { - this.textDoc = Objects.requireNonNull(textDoc, "Text document was null"); - this.reporter = Objects.requireNonNull(reporter, "reporter was null"); - this.auxclasspathClassLoader = Objects.requireNonNull(auxclasspathClassLoader, "auxclasspathClassLoader was null"); - - this.propertySource = new ParserTaskProperties(); - propertySource.definePropertyDescriptor(COMMENT_MARKER); + this(textDoc, reporter, new ParserTaskProperties(), auxclasspathClassLoader); } public ParserTask(TextDocument textDoc, SemanticErrorReporter reporter) { this(textDoc, reporter, Parser.class.getClassLoader()); } + private ParserTask(TextDocument textDoc, + SemanticErrorReporter reporter, + ParserTaskProperties source, + ClassLoader auxclasspathClassLoader) { + this.textDoc = Objects.requireNonNull(textDoc, "Text document was null"); + this.reporter = Objects.requireNonNull(reporter, "reporter was null"); + this.auxclasspathClassLoader = Objects.requireNonNull(auxclasspathClassLoader, "auxclasspathClassLoader was null"); + + this.propertySource = new ParserTaskProperties(source); + } + + public static final PropertyDescriptor COMMENT_MARKER = PropertyFactory.stringProperty("suppressionCommentMarker") .desc("deprecated! NOPMD") @@ -117,9 +124,33 @@ public interface Parser { return getProperties().getProperty(COMMENT_MARKER); } + /** + * Replace the text document with another. + */ + public ParserTask withTextDocument(TextDocument doc) { + return new ParserTask(doc, this.reporter, this.propertySource, this.auxclasspathClassLoader); + } + private static final class ParserTaskProperties extends AbstractPropertySource { + ParserTaskProperties() { + definePropertyDescriptor(COMMENT_MARKER); + } + + ParserTaskProperties(ParserTaskProperties toCopy) { + for (PropertyDescriptor prop : toCopy.getPropertyDescriptors()) { + definePropertyDescriptor(prop); + } + toCopy.getOverriddenPropertyDescriptors().forEach( + prop -> copyProperty(prop, toCopy, this) + ); + } + + static void copyProperty(PropertyDescriptor prop, PropertySource source, PropertySource target) { + target.setProperty(prop, source.getProperty(prop)); + } + @Override protected String getPropertySourceType() { return "ParserOptions"; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TextAvailableNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TextAvailableNode.java index 708b40db4b..8f400cbfb6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TextAvailableNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TextAvailableNode.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.ast; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; @@ -17,23 +19,39 @@ public interface TextAvailableNode extends Node { /** - * Returns the exact region of text delimiting - * the node in the underlying text document. Note - * that {@link #getReportLocation()} does not need - * to match this region. {@link #getReportLocation()} - * can be scoped down to a specific token, eg the - * class identifier. + * Returns the exact region of text delimiting the node in the underlying + * text document. Note that {@link #getReportLocation()} does not need + * to match this region. {@link #getReportLocation()} can be scoped down + * to a specific token, eg the class identifier. This region uses + * the translated coordinate system, ie the coordinate system of + * {@link #getTextDocument()}. */ @Override TextRegion getTextRegion(); /** - * Returns the original source code underlying this node. In - * particular, for a {@link RootNode}, returns the whole text - * of the file. + * Returns the original source code underlying this node, before + * any escapes have been translated. In particular, for a {@link RootNode}, + * returns the whole text of the file. + * + * @see TextDocument#sliceOriginalText(TextRegion) */ @NoAttribute - CharSequence getText(); + default Chars getOriginalText() { + return getTextDocument().sliceOriginalText(getTextRegion()); + } + + /** + * Returns the source code underlying this node, after any escapes + * have been translated. In particular, for a {@link RootNode}, returns + * the whole text of the file. + * + * @see TextDocument#sliceTranslatedText(TextRegion) + */ + @NoAttribute + default Chars getText() { + return getTextDocument().sliceTranslatedText(getTextRegion()); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java index 3625251bbe..06dd3b01a1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java @@ -53,12 +53,14 @@ public final class TokenMgrError extends FileAnalysisException { return column; } - + @Override + protected String positionToString() { + return super.positionToString() + " at line " + line + ", column " + column; + } @Override - public String getMessage() { - String leader = hasFileName() ? "Lexical error in file " + getFileName() : "Lexical error"; - return leader + " at line " + line + ", column " + column + ". Encountered: " + super.getMessage(); + protected String errorKind() { + return "Lexical error"; } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java index 5c7073e4bb..bd11611c28 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.impl.AbstractNode; -import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.util.StringUtil; @@ -48,11 +47,6 @@ public abstract class AbstractJjtreeNode, N e this.image = image; } - @Override - public final Chars getText() { - return getTextDocument().sliceText(getTextRegion()); - } - @Override public final TextRegion getTextRegion() { return TextRegion.fromBothOffsets(getFirstToken().getStartOffset(), diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractTokenManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractTokenManager.java similarity index 83% rename from pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractTokenManager.java rename to pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractTokenManager.java index 8bb6bf0469..9635ccc94e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractTokenManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractTokenManager.java @@ -2,19 +2,16 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.ast; +package net.sourceforge.pmd.lang.ast.impl.javacc; import java.util.HashMap; import java.util.Map; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; /** * A base class for the token managers generated by JavaCC. - * - * TODO move to impl.javacc package */ public abstract class AbstractTokenManager implements TokenManager { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/BackslashEscapeTranslator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/BackslashEscapeTranslator.java new file mode 100644 index 0000000000..4b9c0b258f --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/BackslashEscapeTranslator.java @@ -0,0 +1,71 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import static java.lang.Integer.min; + +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * A base class for readers that handle escapes starting with a backslash. + */ +public abstract class BackslashEscapeTranslator extends EscapeTranslator { + + private static final char BACKSLASH = '\\'; + + /** + * An offset until which we read backslashes and decided they were not + * an escape. The read procedure may cut off in the middle of the escape, + * and turn an even num of backslashes into an odd one, so until we crossed + * this offset, backslashes are not treated specially. + */ + private int savedNotEscapeSpecialEnd = Integer.MAX_VALUE; + + + public BackslashEscapeTranslator(TextDocument builder) { + super(builder); + } + + @Override + protected int gobbleMaxWithoutEscape(final int maxOff) throws MalformedSourceException { + int off = this.bufpos; + boolean seenBackslash = true; + int notEscapeEnd = this.savedNotEscapeSpecialEnd; + while (off < maxOff) { + seenBackslash = input.charAt(off) == BACKSLASH && notEscapeEnd >= off; + if (seenBackslash) { + break; + } + off++; + } + + if (!seenBackslash || off == maxOff) { + this.bufpos = off; + return off; + } + + return handleBackslash(maxOff, off); + } + + protected abstract int handleBackslash(int maxOff, int firstBackslashOff) throws MalformedSourceException; + + @Override + protected int recordEscape(int startOffsetInclusive, int endOffsetExclusive, Chars translation) { + this.savedNotEscapeSpecialEnd = Integer.MAX_VALUE; + return super.recordEscape(startOffsetInclusive, endOffsetExclusive, translation); + } + + protected int abortEscape(int off, int maxOff) { + // not an escape sequence + int min = min(maxOff, off); + // save the number of backslashes that are part of the escape, + // might have been cut in half by the maxReadahead + this.savedNotEscapeSpecialEnd = min < off ? off : Integer.MAX_VALUE; + this.bufpos = min; + return min; + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java new file mode 100644 index 0000000000..d598bff263 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java @@ -0,0 +1,174 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + + +import java.io.EOFException; + +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextRegion; + +/** + * PMD flavour of character streams used by JavaCC parsers. + */ +public final class CharStream { + + private final JavaccTokenDocument tokenDoc; + private final TextDocument textDoc; + private final Chars chars; + private final boolean useMarkSuffix; + private int curOffset; + private int markOffset; + + private CharStream(JavaccTokenDocument tokenDoc) { + this.tokenDoc = tokenDoc; + this.textDoc = tokenDoc.getTextDocument(); + this.chars = textDoc.getText(); + this.useMarkSuffix = tokenDoc.useMarkSuffix(); + } + + /** + * Create a new char stream for the given document. This may create + * a new {@link TextDocument} view over the original, which reflects + * its character escapes. + */ + public static CharStream create(TextDocument doc, TokenDocumentBehavior behavior) throws MalformedSourceException { + TextDocument translated = behavior.translate(doc); + return new CharStream(new JavaccTokenDocument(translated, behavior)); + } + + /** + * Returns the next character from the input. After a {@link #backup(int)}, + * some of the already read chars must be spit out again. + * + * @return The next character + * + * @throws EOFException Upon EOF + */ + public char readChar() throws EOFException { + if (curOffset == chars.length()) { + throw new EOFException(); + } + return chars.charAt(curOffset++); + } + + + /** + * Calls {@link #readChar()} and returns its value, marking its position + * as the beginning of the next token. All characters must remain in + * the buffer between two successive calls to this method to implement + * backup correctly. + */ + public char markTokenStart() throws EOFException { + markOffset = curOffset; + return readChar(); + } + + + /** + * Returns a string made up of characters from the token mark up to + * to the current buffer position. + */ + public String getTokenImage() { + return getTokenImageCs().toString(); + } + + /** + * Returns a string made up of characters from the token mark up to + * to the current buffer position. + */ + public Chars getTokenImageCs() { + assert markOffset >= 0; + return chars.slice(markOffset, markLen()); + } + + private int markLen() { + return curOffset - markOffset; + } + + + /** + * Appends the suffix of length 'len' of the current token to the given + * string builder. This is used to build up the matched string + * for use in actions in the case of MORE. + * + * @param len Length of the returned array + * + * @throws IndexOutOfBoundsException If len is greater than the length of the current token + */ + public void appendSuffix(StringBuilder sb, int len) { + if (useMarkSuffix) { + assert len <= markLen() : "Suffix is greater than the mark length? " + len + " > " + markLen(); + chars.appendChars(sb, curOffset - len, len); + } // otherwise dead code, kept because Javacc's argument expressions do side effects + } + + + /** + * Pushes a given number of already read chars into the buffer. + * Subsequent calls to {@link #readChar()} will read those characters + * before proceeding to read the underlying char stream. + * + *

A lexer calls this method if it has already read some characters, + * but cannot use them to match a (longer) token. So, they will + * be used again as the prefix of the next token. + * + * @throws AssertionError If the requested amount is greater than the + * length of the mark + */ + public void backup(int amount) { + if (amount > markLen()) { + throw new IllegalArgumentException(); + } + curOffset -= amount; + } + + /** + * Returns the column number of the last character for the current token. + * This is only used for parse exceptions and is very inefficient. + */ + public int getEndColumn() { + return endLocation().getEndColumn(); + } + + + /** + * Returns the line number of the last character for current token. + * This is only used for parse exceptions and is very inefficient. + */ + public int getEndLine() { + return endLocation().getEndLine(); + } + + + private FileLocation endLocation() { + return textDoc.toLocation(TextRegion.caretAt(getEndOffset())); + } + + + /** Returns the start offset of the current token (in the translated source), inclusive. */ + public int getStartOffset() { + return markOffset; + } + + + /** Returns the end offset of the current token (in the translated source), exclusive. */ + public int getEndOffset() { + return curOffset; + } + + + /** + * Returns the token document for the tokens being built. Having it + * here is the most convenient place for the time being. + */ + public JavaccTokenDocument getTokenDocument() { + return tokenDoc; + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamFactory.java deleted file mode 100644 index 333cb1cd9b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.impl.javacc; - -import java.util.function.Function; - -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.document.TextDocument; - -public final class CharStreamFactory { - - private CharStreamFactory() { - // util class - } - - /** - * A char stream that doesn't perform any escape translation. - */ - public static CharStream simpleCharStream(TextDocument input) { - return simpleCharStream(input, JavaccTokenDocument::new); - } - - /** - * A char stream that doesn't perform any escape translation. - */ - public static CharStream simpleCharStream(TextDocument input, - Function documentMaker) { - JavaccTokenDocument document = documentMaker.apply(input); - return new SimpleCharStream(document); - } - - /** - * A char stream that translates java unicode sequences. - */ - public static CharStream javaCharStream(TextDocument input) { - return javaCharStream(input, JavaccTokenDocument::new); - } - - /** - * A char stream that translates java unicode sequences. - */ - public static CharStream javaCharStream(TextDocument input, Function documentMaker) { - JavaccTokenDocument document = documentMaker.apply(input); - return new JavaCharStream(document); - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/EscapeTranslator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/EscapeTranslator.java new file mode 100644 index 0000000000..30431d995d --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/EscapeTranslator.java @@ -0,0 +1,155 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import static java.lang.Integer.min; + +import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.lang.document.FragmentedDocBuilder; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * An object that can translate an input document into an output document, + * typically by replacing escape sequences with the character they represent. + * + *

This is an abstract class because the default implementation does not + * perform any escape processing. Subclasses refine this behavior. + */ +@SuppressWarnings("PMD.AssignmentInOperand") +public abstract class EscapeTranslator { + // Note that this can easily be turned into a java.io.Reader with + // efficient block IO, optimized for the common case where there are + // few or no escapes. This is part of the history of this file, but + // was removed for simplicity. + + /** + * Source characters. When there is an escape, eg \ u00a0, the + * first backslash is replaced with the translated value of the + * escape. The bufpos is updated so that we read the next char + * after the escape. + */ + protected Chars input; + /** Position of the next char to read in the input. */ + protected int bufpos; + /** Keep track of adjustments to make to the offsets, caused by unicode escapes. */ + final FragmentedDocBuilder builder; + + private Chars curEscape; + private int offInEscape; + + /** + * Create a translator that will read from the given document. + * + * @param original Original document + * + * @throws NullPointerException If the parameter is null + */ + public EscapeTranslator(TextDocument original) { + AssertionUtil.requireParamNotNull("builder", original); + this.input = original.getText(); + this.bufpos = 0; + this.builder = new FragmentedDocBuilder(original); + } + + + /** + * Translate all the input in the buffer. This consumes this object. + * + * @return The translated text document. If there is no escape, returns the original text + * + * @throws IllegalStateException If this method is called more than once on the same object + * @throws MalformedSourceException If there are invalid escapes in the source + */ + public TextDocument translateDocument() throws MalformedSourceException { + ensureOpen(); + try { + return translateImpl(); + } finally { + close(); + } + } + + private TextDocument translateImpl() { + if (this.bufpos == input.length()) { + return builder.build(); + } + + final int len = input.length(); // remove Integer.MAX_VALUE + + int readChars = 0; + while (readChars < len && (this.bufpos < input.length() || curEscape != null)) { + if (curEscape != null) { + int toRead = min(len - readChars, curEscape.length() - offInEscape); + + readChars += toRead; + offInEscape += toRead; + + if (curEscape.length() == offInEscape) { + curEscape = null; + continue; + } else { + break; // len cut us off, we'll retry next time + } + } + + int bpos = this.bufpos; + int nextJump = gobbleMaxWithoutEscape(min(input.length(), bpos + len - readChars)); + int newlyReadChars = nextJump - bpos; + + assert newlyReadChars >= 0 && (readChars + newlyReadChars) <= len; + + if (newlyReadChars == 0 && nextJump == input.length()) { + // eof + break; + } + readChars += newlyReadChars; + } + return builder.build(); + } + + /** + * Returns the max offset, EXclusive, up to which we can cut the input + * array from the bufpos to dump it into the output array. + * + * @param maxOff Max offset up to which to read ahead + */ + protected int gobbleMaxWithoutEscape(int maxOff) throws MalformedSourceException { + this.bufpos = maxOff; + return maxOff; + } + + protected int recordEscape(final int startOffsetInclusive, int endOffsetExclusive, Chars translation) { + assert endOffsetExclusive > startOffsetInclusive && startOffsetInclusive >= 0; + this.builder.recordDelta(startOffsetInclusive, endOffsetExclusive, translation); + this.bufpos = endOffsetExclusive; + this.curEscape = translation; + this.offInEscape = 0; + return startOffsetInclusive; + } + + /** + * Closing a translator does not close the underlying document, it just + * clears the intermediary state. + */ + private void close() { + this.bufpos = -1; + this.input = null; + } + + + /** Check to make sure that the stream has not been closed */ + protected final void ensureOpen() { + if (input == null) { + throw new IllegalStateException("Closed"); + } + } + + protected FileLocation locationAt(int indexInInput) { + return builder.toLocation(indexInInput); + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaCharStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaCharStream.java deleted file mode 100644 index 0169d8a16a..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaCharStream.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.impl.javacc; - -import java.io.EOFException; -import java.io.IOException; - -import net.sourceforge.pmd.lang.document.Chars; - -/** - * This stream buffers the whole file in memory before parsing, - * and track start/end offsets of tokens. This allows building {@link JavaccToken}. - * The buffer is assumed to be composed of only ASCII characters, - * and the stream unescapes Unicode escapes. The {@link #getTokenDocument() token document} - * stores the original file with escapes and all. - */ -public class JavaCharStream extends JavaCharStreamBase { - - // full text with nothing escaped and all - private final Chars fullText; - private final JavaccTokenDocument document; - - private int[] startOffsets; - - public JavaCharStream(JavaccTokenDocument document) { - super(document.getTextDocument().newReader()); - this.fullText = document.getFullText(); - this.document = document; - this.startOffsets = new int[bufsize]; - maxNextCharInd = fullText.length(); - - nextCharBuf = null; - } - - @Override - protected void ExpandBuff(boolean wrapAround) { - int[] newStartOffsets = new int[bufsize + 2048]; - - if (wrapAround) { - System.arraycopy(startOffsets, tokenBegin, newStartOffsets, 0, bufsize - tokenBegin); - System.arraycopy(startOffsets, 0, newStartOffsets, bufsize - tokenBegin, bufpos); - startOffsets = newStartOffsets; - } else { - System.arraycopy(startOffsets, tokenBegin, newStartOffsets, 0, bufsize - tokenBegin); - startOffsets = newStartOffsets; - } - - super.ExpandBuff(wrapAround); - } - - @Override - protected void UpdateLineColumn(char c) { - startOffsets[bufpos] = nextCharInd; - super.UpdateLineColumn(c); - } - - @Override - public int getStartOffset() { - return startOffsets[tokenBegin]; - } - - @Override - public int getEndOffset() { - if (isAtEof()) { - return fullText.length(); - } else { - return startOffsets[bufpos] + 1; // + 1 for exclusive - } - } - - @Override - public JavaccTokenDocument getTokenDocument() { - return document; - } - - @Override - public String GetImage() { - if (bufpos >= tokenBegin) { - return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); - } else { - return new String(buffer, tokenBegin, bufsize - tokenBegin) - + new String(buffer, 0, bufpos + 1); - } - } - - @Override - protected char ReadByte() throws IOException { - ++nextCharInd; - - if (isAtEof()) { - if (bufpos != 0) { - --bufpos; - if (bufpos < 0) { - bufpos += bufsize; - } - } else { - bufline[bufpos] = line; - bufcolumn[bufpos] = column; - startOffsets[bufpos] = fullText.length(); - } - throw new EOFException(); - } - - return fullText.charAt(nextCharInd); - } - - private boolean isAtEof() { - return nextCharInd >= fullText.length(); - } - - - @Override - protected void FillBuff() { - throw new IllegalStateException("Buffer shouldn't be refilled"); - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeTranslator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeTranslator.java new file mode 100644 index 0000000000..f230b15daa --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeTranslator.java @@ -0,0 +1,94 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * An implementation of {@link EscapeTranslator} that translates Java + * unicode escapes. + */ +@SuppressWarnings("PMD.AssignmentInOperand") +public final class JavaEscapeTranslator extends BackslashEscapeTranslator { + + public JavaEscapeTranslator(TextDocument input) { + super(input); + } + + @Override + protected int handleBackslash(final int maxOff, final int firstBackslashOff) throws MalformedSourceException { + int off = firstBackslashOff; + while (off < input.length() && input.charAt(off) == '\\') { + off++; + } + + int bslashCount = off - firstBackslashOff; + // is there an escape at offset firstBslashOff? + if ((bslashCount & 1) == 1 // odd number of backslashes + && off < input.length() && input.charAt(off) == 'u') { // at least one 'u' + // this is enough to expect an escape or throw an exception + while (off < input.length() && input.charAt(off) == 'u') { + // consume all the 'u's + off++; + } + Chars value = escapeValue(firstBackslashOff, off - 1); + int endOffset = off + 4; // + 4 hex digits + return recordEscape(firstBackslashOff, endOffset, value); + } else { + return abortEscape(off, maxOff); + } + } + + private Chars escapeValue(int posOfFirstBackSlash, final int offOfTheU) throws MalformedSourceException { + int off = offOfTheU; + try { + char c = (char) + ( hexVal(input.charAt(++off)) << 12 // SUPPRESS CHECKSTYLE paren pad + | hexVal(input.charAt(++off)) << 8 + | hexVal(input.charAt(++off)) << 4 + | hexVal(input.charAt(++off)) + ); + + return Chars.wrap(Character.toString(c)); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + // cut off u and 4 digits + String escape = input.substring(offOfTheU, Math.min(input.length(), offOfTheU + 5)); + throw new MalformedSourceException("Invalid unicode escape \\" + escape, e, locationAt(posOfFirstBackSlash)); + } + } + + private static int hexVal(char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return c - ('A' - 10); + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return c - ('a' - 10); + default: + throw new NumberFormatException("Character '" + c + "' is not a valid hexadecimal digit"); + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccToken.java index 28707814fc..957bb8ad14 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccToken.java @@ -4,8 +4,8 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; -import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.GenericToken; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextRegion; @@ -76,6 +76,19 @@ public class JavaccToken implements GenericToken { public JavaccToken specialToken; + // common constructor, with a CharSequence parameter + JavaccToken(int kind, CharSequence image, int startInclusive, int endExclusive, JavaccTokenDocument document) { + assert document != null : "Null document"; + assert image instanceof String || image instanceof Chars : "Null image"; + assert TextRegion.isValidRegion(startInclusive, endExclusive, document.getTextDocument()); + + this.kind = kind; + this.image = image; + this.startOffset = startInclusive; + this.endOffset = endExclusive; + this.document = document; + } + /** * Builds a new token of the specified kind. * @@ -85,19 +98,15 @@ public class JavaccToken implements GenericToken { * @param endExclusive End of the token in the text file (before translating escapes) * @param document Document owning the token */ - public JavaccToken(int kind, - CharSequence image, - int startInclusive, - int endExclusive, - JavaccTokenDocument document) { - assert document != null : "Null document"; - assert TextRegion.isValidRegion(startInclusive, endExclusive, document.getTextDocument()); + public JavaccToken(int kind, Chars image, int startInclusive, int endExclusive, JavaccTokenDocument document) { + this(kind, (CharSequence) image, startInclusive, endExclusive, document); + } - this.kind = kind; - this.image = image; - this.startOffset = startInclusive; - this.endOffset = endExclusive; - this.document = document; + /** + * Constructor with a {@link String} image (see {@link #JavaccToken(int, Chars, int, int, JavaccTokenDocument) the other ctor}). + */ + public JavaccToken(int kind, String image, int startInclusive, int endExclusive, JavaccTokenDocument document) { + this(kind, (CharSequence) image, startInclusive, endExclusive, document); } /** @@ -128,12 +137,18 @@ public class JavaccToken implements GenericToken { } @Override - public CharSequence getImageCs() { - return image; + public Chars getImageCs() { + // wrap it: it's zero cost (images are either Chars or String) and Chars has a nice API + return Chars.wrap(image); } @Override - public TextRegion getRegion() { + public String getImage() { + return image.toString(); + } + + @Override + public final TextRegion getRegion() { return TextRegion.fromBothOffsets(startOffset, endOffset); } @@ -171,24 +186,13 @@ public class JavaccToken implements GenericToken { public JavaccToken replaceImage(CharStream charStream) { return new JavaccToken( this.kind, - charStream.GetImage(), + charStream.getTokenImageCs(), this.startOffset, charStream.getEndOffset(), this.document ); } - public JavaccToken withImage(String image) { - return new JavaccToken( - this.kind, - image, - this.startOffset, - this.endOffset, - this.document - ); - } - - /** * Returns a new token with the given kind, and all other parameters diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java index 1e9d1f6a8f..6b13d723d4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java @@ -4,23 +4,137 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; +import java.util.Collections; +import java.util.List; + import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.ast.impl.TokenDocument; import net.sourceforge.pmd.lang.document.TextDocument; /** * Token document for Javacc implementations. This is a helper object - * for generated token managers. + * for generated token managers. Note: the extension point is a custom + * implementation of {@link TokenDocumentBehavior}, see {@link JjtreeParserAdapter#tokenBehavior()}, + * {@link JavaCCTokenizer#tokenBehavior()} */ -public class JavaccTokenDocument extends TokenDocument { +public final class JavaccTokenDocument extends TokenDocument { + + private final TokenDocumentBehavior behavior; private JavaccToken first; - public JavaccTokenDocument(TextDocument textDocument) { + public JavaccTokenDocument(TextDocument textDocument, TokenDocumentBehavior behavior) { super(textDocument); + this.behavior = behavior; + } + + /** + * Overridable configuration of a token document. + */ + public static class TokenDocumentBehavior { + + public static final TokenDocumentBehavior DEFAULT = new TokenDocumentBehavior(Collections.emptyList()); + private final List tokenNames; + + public TokenDocumentBehavior(List tokenNames) { + this.tokenNames = tokenNames; + } + + /** + * Returns true if the lexer should accumulate the image of MORE + * tokens into the StringBuilder jjimage. This is useless in our + * current implementations, because the image of tokens can be cut + * out using text coordinates, so doesn't need to be put into a separate string. + * The default returns false, which makes {@link CharStream#appendSuffix(StringBuilder, int)} a noop. + */ + public boolean useMarkSuffix() { + return false; + } + + /** + * Translate the escapes of the source document. The default implementation + * does not perform any escaping. + * + * @param text Source doc + * + * @see EscapeTranslator + * + * TODO move that to LanguageVersionHandler once #3919 (Merge CPD and PMD language) is implemented + */ + public TextDocument translate(TextDocument text) throws MalformedSourceException { + return text; + } + + + /** + * Returns a string that describes the token kind. + * + * @param kind Kind of token + * + * @return A descriptive string + */ + public final @NonNull String describeKind(int kind) { + if (kind == JavaccToken.IMPLICIT_TOKEN) { + return ""; + } + String impl = describeKindImpl(kind); + if (impl != null) { + return impl; + } + return ""; + } + + /** + * Describe the given kind. If this returns a non-null value, then + * that's what {@link #describeKind(int)} will use. Otherwise a default + * implementation is used. + * + *

An implementation typically uses the JavaCC-generated array + * named {@code Constants.tokenImage}. Remember to + * check the bounds of the array. + * + * @param kind Kind of token + * + * @return A descriptive string, or null to use default + */ + protected @Nullable String describeKindImpl(int kind) { + if (kind >= 0 && kind < tokenNames.size()) { + return tokenNames.get(kind); + } + return null; + } + + + /** + * Creates a new token with the given kind. This is called back to + * by JavaCC-generated token managers (jjFillToken). Note that a + * created token is not guaranteed to end up in the final token chain. + * + * @param kind Kind of the token + * @param cs Char stream of the file. This can be used to get text + * coordinates and the image + * @param image Shared instance of the image token. If this is non-null, + * then no call to {@link CharStream#getTokenImage()} should be + * issued. + * + * @return A new token + */ + public JavaccToken createToken(JavaccTokenDocument self, int kind, CharStream cs, @Nullable String image) { + return new JavaccToken( + kind, + image == null ? cs.getTokenImageCs() : image, + cs.getStartOffset(), + cs.getEndOffset(), + self + ); + } + } + + boolean useMarkSuffix() { + return behavior.useMarkSuffix(); } /** @@ -52,62 +166,17 @@ public class JavaccTokenDocument extends TokenDocument { } /** - * Returns a string that describes the token kind. - * - * @param kind Kind of token - * - * @return A descriptive string + * @see TokenDocumentBehavior#describeKind(int) */ - public final @NonNull String describeKind(int kind) { - if (kind == JavaccToken.IMPLICIT_TOKEN) { - return ""; - } - String impl = describeKindImpl(kind); - if (impl != null) { - return impl; - } - return ""; + public @NonNull String describeKind(int kind) { + return behavior.describeKind(kind); } /** - * Describe the given kind. If this returns a non-null value, then - * that's what {@link #describeKind(int)} will use. Otherwise a default - * implementation is used. - * - *

An implementation typically uses the JavaCC-generated array - * named {@code Constants.tokenImage}. Remember to - * check the bounds of the array. - * - * @param kind Kind of token - * - * @return A descriptive string, or null to use default - */ - protected @Nullable String describeKindImpl(int kind) { - return null; - } - - - /** - * Creates a new token with the given kind. This is called back to - * by JavaCC-generated token managers (jjFillToken). Note that a - * created token is not guaranteed to end up in the final token chain. - * - * @param kind Kind of the token - * @param cs Char stream of the file. This can be used to get text - * coordinates and the image - * @param image Shared instance of the image token. If this is non-null, - * then no call to {@link CharStream#GetImage()} should be - * issued. - * - * @return A new token + * @see TokenDocumentBehavior#createToken(JavaccTokenDocument, int, CharStream, String) */ public JavaccToken createToken(int kind, CharStream cs, @Nullable String image) { - return new JavaccToken( - kind, - image == null ? cs.GetImage() : image, - cs.getStartOffset(), - cs.getEndOffset(), - this - ); + return behavior.createToken(this, kind, cs, image); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeNode.java index 68e8d63028..0af5cfe313 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeNode.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; import net.sourceforge.pmd.lang.ast.TextAvailableNode; import net.sourceforge.pmd.lang.ast.impl.GenericNode; -import net.sourceforge.pmd.lang.document.Chars; -import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.reporting.Reportable; /** @@ -19,12 +17,6 @@ import net.sourceforge.pmd.reporting.Reportable; */ public interface JjtreeNode> extends GenericNode, TextAvailableNode, Reportable { - @Override - Chars getText(); - - @Override - TextRegion getTextRegion(); - // todo token accessors should most likely be protected in PMD 7. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java index a25fc65089..60d701bba9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java @@ -4,12 +4,10 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.document.TextDocument; /** * Base implementation of the {@link Parser} interface for JavaCC language @@ -24,20 +22,19 @@ public abstract class JjtreeParserAdapter implements Parser // inheritance only } - protected abstract JavaccTokenDocument newDocumentImpl(TextDocument textDocument); - - protected CharStream newCharStream(JavaccTokenDocument tokenDocument) { - return new SimpleCharStream(tokenDocument); - } + protected abstract JavaccTokenDocument.TokenDocumentBehavior tokenBehavior(); @Override - public R parse(ParserTask task) throws ParseException { - JavaccTokenDocument doc = newDocumentImpl(task.getTextDocument()); - CharStream charStream = newCharStream(doc); - + public final R parse(ParserTask task) throws ParseException { try { + // First read the source file and interpret escapes + CharStream charStream = CharStream.create(task.getTextDocument(), tokenBehavior()); + // We replace the text document, so that it reflects escapes properly + // Escapes are processed by CharStream#create + task = task.withTextDocument(charStream.getTokenDocument().getTextDocument()); + // Finally, do the parsing return parseImpl(charStream, task); - } catch (TokenMgrError tme) { + } catch (FileAnalysisException tme) { throw tme.setFileName(task.getFileDisplayName()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java new file mode 100644 index 0000000000..bc3471861e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java @@ -0,0 +1,35 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import java.util.Objects; + +import net.sourceforge.pmd.lang.ast.FileAnalysisException; +import net.sourceforge.pmd.lang.document.FileLocation; + +/** + * A {@link FileAnalysisException} thrown when the source format is invalid, + * for example if some unicode escapes cannot be translated. + */ +public class MalformedSourceException extends FileAnalysisException { + + private final FileLocation location; + + public MalformedSourceException(String message, Throwable cause, FileLocation fileLocation) { + super(message, cause); + this.location = Objects.requireNonNull(fileLocation); + setFileName(fileLocation.getFileName()); + } + + @Override + protected String positionToString() { + return super.positionToString() + " at " + location.startPosToString(); + } + + @Override + protected String errorKind() { + return "Source format error"; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/SimpleCharStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/SimpleCharStream.java deleted file mode 100644 index d453b34797..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/SimpleCharStream.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ast.impl.javacc; - -/** - * A char stream that does not perform unicode escaping. - */ -public class SimpleCharStream extends JavaCharStream { - - public SimpleCharStream(JavaccTokenDocument document) { - super(document); - } - - @Override - protected boolean doEscape() { - return false; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/BaseMappedDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/BaseMappedDocument.java new file mode 100644 index 0000000000..d6e8fc9e8e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/BaseMappedDocument.java @@ -0,0 +1,113 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.document; + +import java.io.IOException; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Base class for documents that apply a transform to their output offsets. + * This includes translated documents, and slices (subdocument views). + */ +abstract class BaseMappedDocument implements TextDocument { + + protected final TextDocument base; + + BaseMappedDocument(TextDocument base) { + this.base = base; + } + + @Override + public long getCheckSum() { + return base.getCheckSum(); + } + + @Override + public String getPathId() { + return base.getPathId(); + } + + @Override + public String getDisplayName() { + return base.getDisplayName(); + } + + @Override + public Chars sliceOriginalText(TextRegion region) { + return base.sliceOriginalText(inputRegion(region)); + } + + @Override + public FileLocation toLocation(TextRegion region) { + return base.toLocation(inputRegion(region)); + } + + @Override + public TextRegion createLineRange(int startLineInclusive, int endLineInclusive) { + // see the doc, lines do not need to be translated + return base.createLineRange(startLineInclusive, endLineInclusive); + } + + @Override + public TextPos2d lineColumnAtOffset(int offset, boolean inclusive) { + return base.lineColumnAtOffset(inputOffset(offset, inclusive)); + } + + /** + * Translate a region given in the coordinate system of this + * document, to the coordinate system of the base document. + * This works as if creating a new region with both start and end + * offsets translated through {@link #inputOffset(int, boolean)}. The + * returned region may have a different length. + * + * @param outputRegion Output region + * + * @return Input region + */ + protected @NonNull TextRegion inputRegion(TextRegion outputRegion) { + return TextRegion.fromBothOffsets(inputOffset(outputRegion.getStartOffset(), true), + inputOffset(outputRegion.getEndOffset(), false)); + } + + /** + * Returns the input offset for the given output offset. This maps + * back an offset in the coordinate system of this document, to the + * coordinate system of the base document. This includes the + * length of any unicode escapes. + * + *

+     * input:      "a\u00a0b"   (original document)
+     * translated: "a b"        (this document)
+     *
+     * translateOffset(0) = 0
+     * translateOffset(1) = 1
+     * translateOffset(2) = 7 // includes the length of the escape
+     * 
+ * + * @param outOffset Output offset + * @param inclusive Whether the offset is to be interpreted as the index of a character (true), + * or the position after a character (false) + * + * @return Input offset + */ + protected final int inputOffset(int outOffset, boolean inclusive) { + if (outOffset < 0 || outOffset > getLength()) { + throw new IndexOutOfBoundsException(); + } + return localOffsetTransform(outOffset, inclusive); + } + + /** + * Output offset to input offset. + */ + protected abstract int localOffsetTransform(int outOffset, boolean inclusive); + + + @Override + public void close() throws IOException { + base.close(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java index 1e32d8ea44..a50fb5dd58 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java @@ -581,6 +581,16 @@ public final class Chars implements CharSequence { return StreamSupport.stream(lines().spliterator(), false); } + /** + * Returns a new stringbuilder containing the whole contents of this + * char sequence. + */ + public StringBuilder toStringBuilder() { + StringBuilder sb = new StringBuilder(length()); + appendChars(sb); + return sb; + } + /** * Returns a new reader for the whole contents of this char sequence. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java index 31b0482b5a..6cdf18785c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java @@ -138,7 +138,7 @@ public final class FileLocation { } /** - * Creates a new location from the given parameters. + * Creates a new location for a range of text. * * @throws IllegalArgumentException If the file name is null * @throws IllegalArgumentException If any of the line/col parameters are strictly less than 1 @@ -155,6 +155,21 @@ public final class FileLocation { end.getColumn()); } + /** + * Returns a new location that starts and ends at the same position. + * + * @param fileName File name + * @param line Line number + * @param column Column number + * + * @return A new location + * + * @throws IllegalArgumentException See {@link #range(String, int, int, int, int)} + */ + public static FileLocation caret(String fileName, int line, int column) { + return new FileLocation(fileName, line, column, line, column); + } + @Override public String toString() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedDocBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedDocBuilder.java new file mode 100644 index 0000000000..52d98c05a0 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedDocBuilder.java @@ -0,0 +1,77 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.document; + + +import net.sourceforge.pmd.lang.document.FragmentedTextDocument.Fragment; + +public final class FragmentedDocBuilder { + + private final Chars mainBuf; + private final TextDocument original; + + private Fragment lastFragment; + private Fragment firstFragment; + + private int curOffInInput; + + public FragmentedDocBuilder(TextDocument original) { + this.mainBuf = original.getText(); + this.original = original; + } + + public FileLocation toLocation(int indexInInput) { + return original.toLocation(TextRegion.caretAt(indexInInput)); + } + + /** + * Add a new fragment. + * + * @param startInInput Start (inclusive) of the overwritten text in the source + * @param endInInput End (exclusive) ... + * @param translation Characters with which the range startInInput..endInInput are overwritten. + * This may be empty. + */ + public void recordDelta(int startInInput, int endInInput, Chars translation) { + assert curOffInInput <= startInInput : "Already moved past " + curOffInInput + ", cannot add delta at " + startInInput; + assert startInInput <= endInInput : "Offsets must be ordered"; + assert translation != null : "Translation cannot be null"; + + int inLength = endInInput - startInInput; + if (firstFragment == null) { + assert lastFragment == null; + firstFragment = new Fragment(null, startInInput, mainBuf.slice(0, startInInput)); + lastFragment = new Fragment(firstFragment, inLength, translation); + curOffInInput = endInInput; + return; + } + + Fragment last = lastFragment; + int prevLen = startInInput - curOffInInput; + if (prevLen != 0) { + last = new Fragment(last, prevLen, mainBuf.slice(curOffInInput, prevLen)); + } + last = new Fragment(last, inLength, translation); + this.lastFragment = last; + this.curOffInInput = endInInput; + } + + public TextDocument build() { + if (firstFragment == null) { + // No deltas in whole document, there's a single fragment + // This is the case for > 97% of Java files (source: OpenJDK) + return original; + } else { + if (curOffInInput < mainBuf.length()) { + // there's some text left between the last fragment and the end of the doc + int remLen = mainBuf.length() - curOffInInput; + Chars remainder = mainBuf.slice(curOffInInput, remLen); + lastFragment = new Fragment(lastFragment, remLen, remainder); + } + return new FragmentedTextDocument(original, firstFragment, lastFragment); + } + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedTextDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedTextDocument.java new file mode 100644 index 0000000000..d92404eeb4 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FragmentedTextDocument.java @@ -0,0 +1,161 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.document; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.LanguageVersion; + +/** + * A text document built as a set of deltas over another document. + */ +final class FragmentedTextDocument extends BaseMappedDocument implements TextDocument { + + private final Chars text; + + private Fragment lastAccessedFragment; + + FragmentedTextDocument(TextDocument base, Fragment firstFragment, Fragment lastFragment) { + super(base); + assert firstFragment != lastFragment; // NOPMD + this.text = toChars(firstFragment, lastFragment); + this.lastAccessedFragment = firstFragment; + } + + private static Chars toChars(Fragment firstFragment, Fragment lastFragment) { + StringBuilder sb = new StringBuilder(lastFragment.outEnd()); + Fragment f = firstFragment; + while (f != null) { + f.getChars().appendChars(sb); + f = f.next; + } + return Chars.wrap(sb); + } + + @Override + public Chars getText() { + return text; + } + + + @Override + public LanguageVersion getLanguageVersion() { + return base.getLanguageVersion(); + } + + @Override + protected int localOffsetTransform(int outOffset, boolean inclusive) { + // caching the last accessed fragment instead of doing + // a linear search is critical for performance. + Fragment f = this.lastAccessedFragment; + if (f == null) { + return outOffset; + } + + // Whether the fragment contains the offset we're looking for. + // Will be true most of the time. + boolean containsOffset = + f.outStart() <= outOffset && outOffset < f.outEnd(); + + if (!containsOffset) { + // Slow path, we must search for the fragment + // This optimisation is important, otherwise we have + // to search for very long times in some files + + if (f.outEnd() < outOffset) { // search forward + while (f.next != null && f.outEnd() < outOffset) { + f = f.next; + } + } else { // search backwards + while (f.prev != null && outOffset <= f.outStart()) { + f = f.prev; + } + } + lastAccessedFragment = f; + } + + if (inclusive && f.outEnd() == outOffset && f.next != null) { + // Inclusive means, the offset must correspond to a character in the source document. + // Here we have to skip forward to the fragment that contains the character, because + // it's not this one. + do { + f = f.next; + } while (f.next != null && f.outLen() == 0); + } + return f.outToIn(outOffset); + } + + + /** + * A delta from the original text to the translated text. This maps + * a region of the original document to some new characters. + */ + static final class Fragment { + + private final Chars chars; + + final @Nullable Fragment prev; + @Nullable Fragment next; + + private final int inStart; + private final int inLength; + private final int outStart; + + Fragment(@Nullable Fragment prev, int inLength, Chars chars) { + this.chars = chars; + this.prev = prev; + this.inLength = inLength; + if (prev != null) { + prev.next = this; + this.outStart = prev.outEnd(); + this.inStart = prev.inEnd(); + } else { + this.outStart = 0; + this.inStart = 0; + } + } + + public Chars getChars() { + return chars; + } + + int outStart() { + return outStart; + } + + int outLen() { + return chars.length(); + } + + int outEnd() { + return outStart() + outLen(); + } + + int inStart() { + return inStart; + } + + int inLen() { + return inLength; + } + + int inEnd() { + return inStart() + inLen(); + } + + int outToIn(int outOffset) { + return inStart() + outOffset - outStart(); + } + + int inToOut(int inOffset) { + return inOffset - inStart() + outStart(); + } + + @Override + public String toString() { + return "Fragment[" + inStart() + ".." + inEnd() + " -> " + outStart() + ".." + outEnd() + "]" + chars; + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/RootTextDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/RootTextDocument.java index 873fba75c5..be09e85419 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/RootTextDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/RootTextDocument.java @@ -120,12 +120,13 @@ final class RootTextDocument extends BaseCloseable implements TextDocument { } @Override - public Chars sliceText(TextRegion region) { + public Chars sliceOriginalText(TextRegion region) { return getText().subSequence(region.getStartOffset(), region.getEndOffset()); } private static final String NOT_IN_RANGE = "Region [start=%d, end=%d[ is not in range of this document (length %d)"; private static final String INVALID_LINE_RANGE = "Line range %d..%d is not in range of this document (%d lines) (line numbers are 1-based)"; + private static final String INVALID_OFFSET = "Offset %d is not in range of this document (length %d) (offsets are 0-based)"; static IndexOutOfBoundsException invalidLineRange(int start, int end, int numLines) { return new IndexOutOfBoundsException(String.format(INVALID_LINE_RANGE, start, end, numLines)); @@ -134,4 +135,8 @@ final class RootTextDocument extends BaseCloseable implements TextDocument { static IndexOutOfBoundsException regionOutOfBounds(int start, int end, int maxLen) { return new IndexOutOfBoundsException(String.format(NOT_IN_RANGE, start, end, maxLen)); } + + static IndexOutOfBoundsException invalidOffset(int offset, int maxLen) { + return new IndexOutOfBoundsException(String.format(INVALID_OFFSET, offset, maxLen)); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java index 6bfc817b09..153f43e360 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java @@ -73,6 +73,8 @@ import net.sourceforge.pmd.util.datasource.DataSource; public interface TextDocument extends Closeable { // todo logical sub-documents, to support embedded languages // ideally, just slice the text, and share the positioner + // a problem with document slices becomes reference counting for the close routine + // todo text edition (there are some reverted commits in the branch // with part of this, including a lot of tests) @@ -105,9 +107,33 @@ public interface TextDocument extends Closeable { Chars getText(); /** - * Returns a region of the {@linkplain #getText() text} as a character sequence. + * Returns a slice of the original text. Note that this is not the + * same as {@code getText().subsequence}, as if this document has + * translated escapes, the returned char slice will contain the + * untranslated escapes, whereas {@link #getText()} would return + * the translated characters. + * + * @param region A region, in the coordinate system of this document + * + * @return The slice of the original text that corresponds to the region + * + * @throws IndexOutOfBoundsException If the region is not a valid range */ - Chars sliceText(TextRegion region); + Chars sliceOriginalText(TextRegion region); + + /** + * Returns a slice of the source text. This is always equal to + * {@code getText().slice(region)}, as the text is the translated text. + * + * @param region A region, in the coordinate system of this document + * + * @return The slice of the original text that corresponds to the region + * + * @throws IndexOutOfBoundsException If the region is not a valid range + */ + default Chars sliceTranslatedText(TextRegion region) { + return getText().slice(region); + } /** @@ -117,6 +143,7 @@ public interface TextDocument extends Closeable { */ long getCheckSum(); + /** * Returns a reader over the text of this document. */ @@ -124,7 +151,6 @@ public interface TextDocument extends Closeable { return getText().newReader(); } - /** * Returns the length in characters of the {@linkplain #getText() text}. */ @@ -133,7 +159,8 @@ public interface TextDocument extends Closeable { } /** - * Returns a text region that corresponds to the entire document. + * Returns a text region that corresponds to the entire document, + * in the coordinate system of this document. */ default TextRegion getEntireRegion() { return TextRegion.fromOffsetLength(0, getLength()); @@ -143,11 +170,16 @@ public interface TextDocument extends Closeable { * Returns a region that spans the text of all the given lines. * This is intended to provide a replacement for {@link SourceCode#getSlice(int, int)}. * + *

Note that, as line numbers may only be obtained from {@link #toLocation(TextRegion)}, + * and hence are line numbers of the original source, both parameters + * must be line numbers of the source text and not the translated text + * that this represents. + * * @param startLineInclusive Inclusive start line number (1-based) * @param endLineInclusive Inclusive end line number (1-based) * * @throws IndexOutOfBoundsException If the arguments do not identify - * a valid region in this document + * a valid region in the source document */ TextRegion createLineRange(int startLineInclusive, int endLineInclusive); @@ -181,6 +213,8 @@ public interface TextDocument extends Closeable { /** * Returns the line and column at the given offset. + * Both the input offset and the output range are in the coordinates + * of this document. * * @param offset A source offset (0-based), can range in {@code [0, length]}. * @param inclusive If the offset falls right after a line terminator, diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileBuilder.java index f240b51b81..e23f3264b5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileBuilder.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.LanguageVersion; * A builder for a new text file. * See static methods on {@link TextFile}. */ +@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") public abstract class TextFileBuilder { protected final LanguageVersion languageVersion; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java index 4a79f9e516..3ae36944bd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java @@ -137,6 +137,7 @@ abstract class PmdRunnable implements Runnable { handler.declareParserTaskProperties(task.getProperties()); task.getProperties().setProperty(ParserTask.COMMENT_MARKER, configuration.getSuppressMarker()); + assert task.getCommentMarker().equals(configuration.getSuppressMarker()); Parser parser = handler.getParser(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index a67cdfe4b8..1a08e64952 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -62,12 +62,15 @@ import com.github.oowekyala.ooxml.messages.XmlException; public class RuleFactory { private final ResourceLoader resourceLoader; + private final LanguageRegistry languageRegistry; /** * @param resourceLoader The resource loader to load the rule from jar */ - public RuleFactory(final ResourceLoader resourceLoader) { + public RuleFactory(ResourceLoader resourceLoader, + LanguageRegistry languageRegistry) { this.resourceLoader = resourceLoader; + this.languageRegistry = languageRegistry; } /** @@ -250,7 +253,7 @@ public class RuleFactory { private void setLanguage(Element ruleElement, PmdXmlReporter err, Rule rule) { String langId = SchemaConstants.LANGUAGE.getNonBlankAttribute(ruleElement, err); - Language lang = LanguageRegistry.findLanguageByTerseName(langId); + Language lang = languageRegistry.getLanguageById(langId); if (lang == null) { Attr node = SchemaConstants.LANGUAGE.getAttributeNode(ruleElement); throw err.at(node) @@ -260,7 +263,7 @@ public class RuleFactory { } private @NonNull String supportedLanguages() { - return LanguageRegistry.getLanguages().stream().map(Language::getTerseName).map(StringUtil::inSingleQuotes).collect(Collectors.joining(", ")); + return languageRegistry.commaSeparatedList(l -> StringUtil.inSingleQuotes(l.getId())); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java index b708538400..af98854f0f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java @@ -8,6 +8,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyIterator; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; import java.util.ArrayList; @@ -511,12 +512,12 @@ public final class CollectionUtil { /** * A collector that returns a mutable set. This contrasts with * {@link Collectors#toSet()}, which makes no guarantee about the - * mutability of the set. + * mutability of the set. The set preserves insertion order. * * @param Type of accumulated values */ public static Collector> toMutableSet() { - return Collectors.toCollection(HashSet::new); + return Collectors.toCollection(LinkedHashSet::new); } /** @@ -531,6 +532,18 @@ public final class CollectionUtil { return Collectors.collectingAndThen(toMutableList(), Collections::unmodifiableList); } + /** + * A collector that returns an unmodifiable set. This contrasts with + * {@link Collectors#toSet()}, which makes no guarantee about the + * mutability of the set. {@code Collectors::toUnmodifiableSet} was + * only added in JDK 9. The set preserves insertion order. + * + * @param Type of accumulated values + */ + public static Collector> toUnmodifiableSet() { + return Collectors.collectingAndThen(toMutableSet(), Collections::unmodifiableSet); + } + /** * A collectors that accumulates into a persistent set. * @@ -634,6 +647,13 @@ public final class CollectionUtil { return Collections.unmodifiableList(new ArrayList<>(list)); } + public static Set defensiveUnmodifiableCopyToSet(Collection list) { + if (list.isEmpty()) { + return emptySet(); + } + return Collections.unmodifiableSet(new LinkedHashSet<>(list)); + } + /** * Like {@link String#join(CharSequence, Iterable)}, except it appends * on a preexisting {@link StringBuilder}. The result value is that StringBuilder. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index 825c891757..ba989da286 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.AssertionUtil; @@ -550,6 +551,9 @@ public final class StringUtil { return str.replaceAll("'", "''"); } + public static @NonNull String inDoubleQuotes(String expected) { + return "\"" + expected + "\""; + } public enum CaseConvention { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java index e8381171a1..9cd086f186 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringEscapeUtils; +import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; @@ -43,8 +44,8 @@ public class TreeExportCli { @Parameter(names = { "--format", "-f" }, description = "The output format.") private String format = "xml"; - @Parameter(names = { "--language", "-l" }, description = "Specify the language to use.") - private String language = LanguageRegistry.getDefaultLanguage().getTerseName(); + @Parameter(names = { "--language", "-l" }, description = "Specify the language to use.", required = true) + private @Nullable String language = null; @Parameter(names = { "--encoding", "-e" }, description = "Encoding of the source file.") private String encoding = StandardCharsets.UTF_8.name(); @DynamicParameter(names = "-P", description = "Properties for the renderer.") @@ -130,7 +131,7 @@ public class TreeExportCli { sb.append(System.lineSeparator()); sb.append("Available languages: "); - for (Language l : LanguageRegistry.getLanguages()) { + for (Language l : LanguageRegistry.PMD) { sb.append(l.getTerseName()).append(' '); } sb.append(System.lineSeparator()); @@ -176,14 +177,24 @@ public class TreeExportCli { } private void run(TreeRenderer renderer) throws IOException { + run(LanguageRegistry.PMD, renderer); + } + + private void run(LanguageRegistry registry, TreeRenderer renderer) throws IOException { printWarning(); - LanguageVersion langVersion = LanguageRegistry.findLanguageByTerseName(language).getDefaultVersion(); + Language lang = registry.getLanguageById(language); + if (lang == null) { + throw bail("Unknown language '" + language + "', one of [" + + registry.commaSeparatedList(Language::getId) + + "] was expected"); + } + + LanguageVersion langVersion = lang.getDefaultVersion(); LanguageVersionHandler languageHandler = langVersion.getLanguageVersionHandler(); Parser parser = languageHandler.getParser(); - @SuppressWarnings("PMD.CloseResource") - TextFile textFile; + @SuppressWarnings("PMD.CloseResource") TextFile textFile; if (file == null && !readStdin) { throw bail("One of --file or --read-stdin must be mentioned"); } else if (readStdin) { diff --git a/pmd-core/src/main/resources/rulesets/releases/35.xml b/pmd-core/src/main/resources/rulesets/releases/35.xml index debffe92c2..70263d0435 100644 --- a/pmd-core/src/main/resources/rulesets/releases/35.xml +++ b/pmd-core/src/main/resources/rulesets/releases/35.xml @@ -25,7 +25,9 @@ This ruleset contains links to rules that are new in PMD v3.5 + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java index 67bde85b0e..c8cc392240 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java @@ -4,7 +4,8 @@ package net.sourceforge.pmd; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; @@ -12,7 +13,6 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.LanguageFilenameFilter; -import net.sourceforge.pmd.lang.LanguageRegistry; /** * Tests on FileSelector. @@ -26,13 +26,12 @@ class FileSelectorTest { */ @Test void testWantedFile() { - LanguageFilenameFilter fileSelector = new LanguageFilenameFilter( - LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(DummyLanguageModule.getInstance()); File javaFile = new File("/path/to/myFile.dummy"); boolean selected = fileSelector.accept(javaFile.getParentFile(), javaFile.getName()); - assertEquals(true, selected, "This file should be selected !"); + assertTrue(selected, "This file should be selected !"); } /** @@ -40,13 +39,12 @@ class FileSelectorTest { */ @Test void testUnwantedFile() { - LanguageFilenameFilter fileSelector = new LanguageFilenameFilter( - LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(DummyLanguageModule.getInstance()); File javaFile = new File("/path/to/myFile.txt"); boolean selected = fileSelector.accept(javaFile.getParentFile(), javaFile.getName()); - assertEquals(false, selected, "Not-source file must not be selected!"); + assertFalse(selected, "Not-source file must not be selected!"); } /** @@ -54,12 +52,11 @@ class FileSelectorTest { */ @Test void testUnwantedJavaFile() { - LanguageFilenameFilter fileSelector = new LanguageFilenameFilter( - LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(DummyLanguageModule.getInstance()); File javaFile = new File("/path/to/MyClass.java"); boolean selected = fileSelector.accept(javaFile.getParentFile(), javaFile.getName()); - assertEquals(false, selected, "Unwanted java file must not be selected!"); + assertFalse(selected, "Unwanted java file must not be selected!"); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/FooRule.java b/pmd-core/src/test/java/net/sourceforge/pmd/FooRule.java index 3b5f151fbd..540575da39 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/FooRule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/FooRule.java @@ -9,7 +9,6 @@ import static net.sourceforge.pmd.util.CollectionUtil.setOf; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; @@ -20,9 +19,9 @@ import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class FooRule extends AbstractRule { public FooRule() { - setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); setName("Foo"); setDescription("Description with Unicode Character U+2013: \u2013 ."); + setLanguage(DummyLanguageModule.getInstance()); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java index f38250e95e..9ca21e38a8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java @@ -15,11 +15,17 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.IOException; +import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import net.sourceforge.pmd.RuleSetTest.MockRule; +import net.sourceforge.pmd.lang.Dummy2LanguageModule; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.document.SimpleTestTextFile; +import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.processor.PmdRunnableTest; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.ReportStats; @@ -27,7 +33,7 @@ import net.sourceforge.pmd.reporting.ReportStats; /** * @author Clément Fournier */ -class PmdAnalysisTest { +public class PmdAnalysisTest { @Test void testPmdAnalysisWithEmptyConfig() { @@ -90,4 +96,54 @@ class PmdAnalysisTest { assertEquals("Violations", 0, stats.getNumViolations()); } } + + @Test + public void testFileWithSpecificLanguage() { + final Language language = Dummy2LanguageModule.getInstance(); + PMDConfiguration config = new PMDConfiguration(); + config.setIgnoreIncrementalAnalysis(true); + RuleSet ruleset = RuleSet.forSingleRule(new TestRule()); + + try (PmdAnalysis pmd = PmdAnalysis.create(config)) { + pmd.addRuleSet(ruleset); + pmd.files().addFile(Paths.get("src", "test", "resources", "sample-source", "dummy", "foo.txt"), language); + Report report = pmd.performAnalysisAndCollectReport(); + for (Report.ProcessingError error : report.getProcessingErrors()) { + System.out.println("error = " + error.getMsg() + ": " + error.getDetail()); + } + assertEquals(0, report.getProcessingErrors().size()); + assertEquals(1, report.getViolations().size()); + } + } + + @Test + public void testTextFileWithSpecificLanguage() { + final Language language = Dummy2LanguageModule.getInstance(); + PMDConfiguration config = new PMDConfiguration(); + config.setIgnoreIncrementalAnalysis(true); + RuleSet ruleset = RuleSet.forSingleRule(new TestRule()); + + try (PmdAnalysis pmd = PmdAnalysis.create(config)) { + pmd.addRuleSet(ruleset); + pmd.files().addFile(new SimpleTestTextFile("test content foo", "foo.txt", language.getDefaultVersion())); + Report report = pmd.performAnalysisAndCollectReport(); + for (Report.ProcessingError error : report.getProcessingErrors()) { + System.out.println("error = " + error.getMsg() + ": " + error.getDetail()); + } + assertEquals(0, report.getProcessingErrors().size()); + assertEquals(1, report.getViolations().size()); + } + } + + public static class TestRule extends AbstractRule { + public TestRule() { + setLanguage(Dummy2LanguageModule.getInstance()); + setMessage("dummy 2 test rule"); + } + + @Override + public void apply(Node node, RuleContext ctx) { + ctx.addViolation(node); + } + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdCoreTestUtils.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdCoreTestUtils.java new file mode 100644 index 0000000000..45af7d3594 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdCoreTestUtils.java @@ -0,0 +1,36 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import net.sourceforge.pmd.lang.Dummy2LanguageModule; +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.LanguageVersion; + +/** + * Helper methods. + */ +public final class PmdCoreTestUtils { + + private PmdCoreTestUtils() { + } + + public static DummyLanguageModule dummyLanguage() { + return DummyLanguageModule.getInstance(); + } + + public static Dummy2LanguageModule dummyLanguage2() { + return Dummy2LanguageModule.getInstance(); + } + + public static T setDummyLanguage(T rule) { + rule.setLanguage(dummyLanguage()); + return rule; + } + + public static LanguageVersion dummyVersion() { + return dummyLanguage().getDefaultVersion(); + } +} + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ReportTest.java index 417d1b9fc7..0eaece814a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ReportTest.java @@ -15,7 +15,7 @@ import java.util.function.Consumer; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextFile; @@ -113,15 +113,12 @@ public class ReportTest { private static FileLocation getNode(int line, int column, String filename) { - return FileLocation.range( - filename, - TextRange2d.range2d(line, column, line, column) - ); + return FileLocation.range(filename, TextRange2d.range2d(line, column, line, column)); } public static String render(Renderer renderer, Consumer listenerEffects) { return renderGlobal(renderer, globalListener -> { - LanguageVersion dummyVersion = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); + LanguageVersion dummyVersion = DummyLanguageModule.getInstance().getDefaultVersion(); TextFile dummyFile = TextFile.forCharSeq("dummyText", "file", dummyVersion); try (FileAnalysisListener fal = globalListener.startFileAnalysis(dummyFile)) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java index ea0255adac..aea8def421 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyLanguage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -13,9 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.Dummy2LanguageModule; -import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -36,7 +35,7 @@ class RuleReferenceTest { final PropertyDescriptor PROPERTY1_DESCRIPTOR = PropertyFactory.stringProperty("property1").desc("Test property").defaultValue("").build(); MockRule rule = new MockRule(); rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR); - Language dummyLang = LanguageRegistry.getLanguage(DummyLanguageModule.NAME); + Language dummyLang = dummyLanguage(); rule.setLanguage(dummyLang); rule.setName("name1"); rule.setProperty(PROPERTY1_DESCRIPTOR, "value1"); @@ -68,17 +67,17 @@ class RuleReferenceTest { @Test void testLanguageOverrideDisallowed() { MockRule rule = new MockRule(); - Language dummyLang = LanguageRegistry.getLanguage(DummyLanguageModule.NAME); + Language dummyLang = dummyLanguage(); rule.setLanguage(dummyLang); RuleReference ruleReference = new RuleReference(); ruleReference.setRule(rule); - assertThrows(UnsupportedOperationException.class, () -> ruleReference.setLanguage(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME))); + assertThrows(UnsupportedOperationException.class, () -> ruleReference.setLanguage(Dummy2LanguageModule.getInstance())); assertEquals(dummyLang, ruleReference.getLanguage()); - assertThrows(IllegalArgumentException.class, () -> ruleReference.setMaximumLanguageVersion(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME).getVersion("1.0"))); + assertThrows(IllegalArgumentException.class, () -> ruleReference.setMaximumLanguageVersion(Dummy2LanguageModule.getInstance().getVersion("1.0"))); assertEquals(rule.getMaximumLanguageVersion(), ruleReference.getOverriddenMaximumLanguageVersion()); - assertThrows(IllegalArgumentException.class, () -> ruleReference.setMinimumLanguageVersion(LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME).getVersion("1.0"))); + assertThrows(IllegalArgumentException.class, () -> ruleReference.setMinimumLanguageVersion(Dummy2LanguageModule.getInstance().getVersion("1.0"))); assertEquals(rule.getMinimumLanguageVersion(), ruleReference.getMinimumLanguageVersion()); } @@ -87,7 +86,7 @@ class RuleReferenceTest { final PropertyDescriptor PROPERTY1_DESCRIPTOR = PropertyFactory.stringProperty("property1").desc("Test property").defaultValue("").build(); MockRule rule = new MockRule(); rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR); - Language dummyLang = LanguageRegistry.getLanguage(DummyLanguageModule.NAME); + Language dummyLang = dummyLanguage(); rule.setLanguage(dummyLang); rule.setName("name1"); rule.setProperty(PROPERTY1_DESCRIPTOR, "value1"); @@ -119,18 +118,18 @@ class RuleReferenceTest { private void validateOverriddenValues(final PropertyDescriptor propertyDescriptor1, final PropertyDescriptor propertyDescriptor2, RuleReference ruleReference) { - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME), ruleReference.getLanguage(), - "Override failed"); + assertEquals(dummyLanguage(), ruleReference.getLanguage(), + "Override failed"); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"), ruleReference.getMinimumLanguageVersion(), - "Override failed"); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"), ruleReference.getOverriddenMinimumLanguageVersion(), - "Override failed"); + assertEquals(dummyLanguage().getVersion("1.3"), ruleReference.getMinimumLanguageVersion(), + "Override failed"); + assertEquals(dummyLanguage().getVersion("1.3"), ruleReference.getOverriddenMinimumLanguageVersion(), + "Override failed"); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), ruleReference.getMaximumLanguageVersion(), - "Override failed"); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), ruleReference.getOverriddenMaximumLanguageVersion(), - "Override failed"); + assertEquals(dummyLanguage().getVersion("1.7"), ruleReference.getMaximumLanguageVersion(), + "Override failed"); + assertEquals(dummyLanguage().getVersion("1.7"), ruleReference.getOverriddenMaximumLanguageVersion(), + "Override failed"); assertEquals(false, ruleReference.getRule().isDeprecated(), "Override failed"); assertEquals(true, ruleReference.isDeprecated(), "Override failed"); @@ -177,9 +176,9 @@ class RuleReferenceTest { final PropertyDescriptor PROPERTY1_DESCRIPTOR = PropertyFactory.stringProperty("property1").desc("Test property").defaultValue("").build(); MockRule rule = new MockRule(); rule.definePropertyDescriptor(PROPERTY1_DESCRIPTOR); - rule.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); - rule.setMinimumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3")); - rule.setMaximumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7")); + rule.setLanguage(dummyLanguage()); + rule.setMinimumLanguageVersion(dummyLanguage().getVersion("1.3")); + rule.setMaximumLanguageVersion(dummyLanguage().getVersion("1.7")); rule.setName("name1"); rule.setProperty(PROPERTY1_DESCRIPTOR, "value1"); rule.setMessage("message1"); @@ -191,9 +190,9 @@ class RuleReferenceTest { RuleReference ruleReference = new RuleReference(); ruleReference.setRule(rule); ruleReference - .setMinimumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3")); + .setMinimumLanguageVersion(dummyLanguage().getVersion("1.3")); ruleReference - .setMaximumLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7")); + .setMaximumLanguageVersion(dummyLanguage().getVersion("1.7")); ruleReference.setDeprecated(false); ruleReference.setName("name1"); ruleReference.setProperty(PROPERTY1_DESCRIPTOR, "value1"); @@ -204,12 +203,12 @@ class RuleReferenceTest { ruleReference.setPriority(RulePriority.HIGH); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.3"), ruleReference.getMinimumLanguageVersion(), - "Override failed"); + assertEquals(dummyLanguage().getVersion("1.3"), ruleReference.getMinimumLanguageVersion(), + "Override failed"); assertNull(ruleReference.getOverriddenMinimumLanguageVersion(), "Override failed"); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), ruleReference.getMaximumLanguageVersion(), - "Override failed"); + assertEquals(dummyLanguage().getVersion("1.7"), ruleReference.getMaximumLanguageVersion(), + "Override failed"); assertNull(ruleReference.getOverriddenMaximumLanguageVersion(), "Override failed"); assertEquals(false, ruleReference.isDeprecated(), "Override failed"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 1bc73cc21e..58b9112454 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyLanguage; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.DEPRECATED; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.NAME; import static org.hamcrest.MatcherAssert.assertThat; @@ -24,8 +25,6 @@ import java.util.Set; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -631,7 +630,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase { attrs -> attrs.put(SchemaConstants.LANGUAGE, "dummy") ) )); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME), r.getLanguage()); + assertEquals(dummyLanguage(), r.getLanguage()); } @Test @@ -660,7 +659,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase { attrs -> attrs.put(SchemaConstants.MINIMUM_LANGUAGE_VERSION, "1.4") ) )); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.4"), + assertEquals(dummyLanguage().getVersion("1.4"), r.getMinimumLanguageVersion()); } @@ -703,7 +702,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase { Rule r = loadFirstRule(rulesetXml( dummyRule(attrs -> attrs.put(SchemaConstants.MAXIMUM_LANGUAGE_VERSION, "1.7")) )); - assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), + assertEquals(dummyLanguage().getVersion("1.7"), r.getMaximumLanguageVersion()); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index c3cfc2bf62..81396be626 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyLanguage; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyLanguage2; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyVersion; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.hamcrest.CoreMatchers.containsString; @@ -37,10 +40,7 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.Report.ProcessingError; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; -import net.sourceforge.pmd.lang.Dummy2LanguageModule; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; @@ -51,8 +51,6 @@ import net.sourceforge.pmd.util.IOUtil; class RuleSetTest { - private final Language dummyLang = DummyLanguageModule.getInstance(); - @Test void testRuleSetRequiresName() { assertThrows(NullPointerException.class, () -> @@ -257,22 +255,22 @@ class RuleSetTest { Rule rule = new MockRule(); - assertFalse(RuleSet.applies(rule, LanguageRegistry.getLanguage(Dummy2LanguageModule.NAME).getDefaultVersion()), + assertFalse(RuleSet.applies(rule, dummyLanguage2().getDefaultVersion()), "Different languages should not apply"); - assertTrue(RuleSet.applies(rule, dummyLang.getVersion("1.5")), + assertTrue(RuleSet.applies(rule, dummyLanguage().getVersion("1.5")), "Same language with no min/max should apply"); - rule.setMinimumLanguageVersion(dummyLang.getVersion("1.5")); - assertTrue(RuleSet.applies(rule, dummyLang.getVersion("1.5")), + rule.setMinimumLanguageVersion(dummyLanguage().getVersion("1.5")); + assertTrue(RuleSet.applies(rule, dummyLanguage().getVersion("1.5")), "Same language with valid min only should apply"); - rule.setMaximumLanguageVersion(dummyLang.getVersion("1.6")); - assertTrue(RuleSet.applies(rule, dummyLang.getVersion("1.5")), + rule.setMaximumLanguageVersion(dummyLanguage().getVersion("1.6")); + assertTrue(RuleSet.applies(rule, dummyLanguage().getVersion("1.5")), "Same language with valid min and max should apply"); - assertFalse(RuleSet.applies(rule, dummyLang.getVersion("1.4")), + assertFalse(RuleSet.applies(rule, dummyLanguage().getVersion("1.4")), "Same language with outside range of min/max should not apply"); - assertFalse(RuleSet.applies(rule, dummyLang.getVersion("1.7")), + assertFalse(RuleSet.applies(rule, dummyLanguage().getVersion("1.7")), "Same language with outside range of min/max should not apply"); } @@ -384,7 +382,7 @@ class RuleSetTest { @Test void testIncludeExcludeApplies() { - TextFile file = TextFile.forPath(Paths.get("C:\\myworkspace\\project\\some\\random\\package\\RandomClass.java"), Charset.defaultCharset(), dummyLang.getDefaultVersion()); + TextFile file = TextFile.forPath(Paths.get("C:\\myworkspace\\project\\some\\random\\package\\RandomClass.java"), Charset.defaultCharset(), dummyVersion()); RuleSet ruleSet = createRuleSetBuilder("ruleset").build(); assertTrue(ruleSet.applies(file), "No patterns"); @@ -419,7 +417,7 @@ class RuleSetTest { void testIncludeExcludeMultipleRuleSetWithRuleChainApplies() throws Exception { Rule rule = new FooRule(); rule.setName("FooRule1"); - rule.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + rule.setLanguage(dummyLanguage()); RuleSet ruleSet1 = createRuleSetBuilder("RuleSet1").addRule(rule).build(); RuleSet ruleSet2 = createRuleSetBuilder("RuleSet2").addRule(rule).build(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationComparatorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationComparatorTest.java index 851c07d075..cba8090be8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationComparatorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationComparatorTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.PmdCoreTestUtils.setDummyLanguage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -24,8 +25,8 @@ class RuleViolationComparatorTest { @Test void testComparator() { - Rule rule1 = new MockRule("name1", "desc", "msg", "rulesetname1"); - Rule rule2 = new MockRule("name2", "desc", "msg", "rulesetname2"); + Rule rule1 = setDummyLanguage(new MockRule("name1", "desc", "msg", "rulesetname1")); + Rule rule2 = setDummyLanguage(new MockRule("name2", "desc", "msg", "rulesetname2")); // RuleViolations created in pre-sorted order RuleViolation[] expectedOrder = new RuleViolation[12]; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java index bad9754ed2..43bc6f0958 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java @@ -83,8 +83,7 @@ public class RulesetFactoryTestBase { protected RuleSet loadRuleSetInDir(String resourceDir, String ruleSetFilename) { - RuleSetLoader loader = new RuleSetLoader(); - loader.setReporter(mockReporter); + RuleSetLoader loader = new RuleSetLoader().withReporter(mockReporter); return loader.loadFromResource(resourceDir + "/" + ruleSetFilename); } @@ -99,8 +98,7 @@ public class RulesetFactoryTestBase { } protected RuleSet loadRuleSet(String fileName, String ruleSetXml) { - RuleSetLoader loader = new RuleSetLoader(); - loader.setReporter(mockReporter); + RuleSetLoader loader = new RuleSetLoader().withReporter(mockReporter); return loader.loadFromString(fileName, ruleSetXml); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java index 1328f7c765..d6b231df6b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java @@ -33,10 +33,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; +import net.sourceforge.pmd.PmdCoreTestUtils; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; @@ -56,7 +56,7 @@ class FileAnalysisCacheTest { private TextDocument sourceFile; private TextFile sourceFileBackend; - private final LanguageVersion dummyVersion = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); + private final LanguageVersion dummyVersion = PmdCoreTestUtils.dummyVersion(); @BeforeEach diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java index 83b16fe69f..77ce16574f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.cache.NoopAnalysisCache; +import net.sourceforge.pmd.lang.LanguageRegistry; import com.github.stefanbirkner.systemlambda.SystemLambda; @@ -58,7 +59,7 @@ class PMDCommandLineInterfaceTest { PMDCommandLineInterface.extractParameters(params, args, "PMD"); assertTrue(params.isIgnoreIncrementalAnalysis()); - PMDConfiguration config = params.toConfiguration(); + PMDConfiguration config = params.toConfiguration(LanguageRegistry.PMD); assertTrue(config.isIgnoreIncrementalAnalysis()); assertTrue(config.getAnalysisCache() instanceof NoopAnalysisCache); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java index eedfca8adb..c107ad0212 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java @@ -8,7 +8,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.hasSize; -import java.io.IOException; import java.util.List; import org.checkerframework.checker.nullness.qual.NonNull; @@ -16,6 +15,7 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.internal.util.FileCollectionUtil; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.lang.document.TextFile; @@ -23,12 +23,12 @@ import net.sourceforge.pmd.util.log.internal.NoopReporter; class PMDFilelistTest { - private static @NonNull FileCollector newCollector() { - return FileCollector.newCollector(new LanguageVersionDiscoverer(), new NoopReporter()); + private @NonNull FileCollector newCollector() { + return FileCollector.newCollector(new LanguageVersionDiscoverer(LanguageRegistry.PMD), new NoopReporter()); } @Test - void testGetApplicableFiles() throws IOException { + void testGetApplicableFiles() { FileCollector collector = newCollector(); FileCollectionUtil.collectFileList(collector, "src/test/resources/net/sourceforge/pmd/cli/filelist.txt"); @@ -40,7 +40,7 @@ class PMDFilelistTest { } @Test - void testGetApplicableFilesMultipleLines() throws IOException { + void testGetApplicableFilesMultipleLines() { FileCollector collector = newCollector(); FileCollectionUtil.collectFileList(collector, "src/test/resources/net/sourceforge/pmd/cli/filelist2.txt"); @@ -53,7 +53,7 @@ class PMDFilelistTest { } @Test - void testGetApplicableFilesWithIgnores() throws IOException { + void testGetApplicableFilesWithIgnores() { FileCollector collector = newCollector(); PMDConfiguration configuration = new PMDConfiguration(); @@ -68,7 +68,7 @@ class PMDFilelistTest { } @Test - void testGetApplicableFilesWithDirAndIgnores() throws IOException { + void testGetApplicableFilesWithDirAndIgnores() { PMDConfiguration configuration = new PMDConfiguration(); configuration.setInputPaths("src/test/resources/net/sourceforge/pmd/cli/src"); 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 58a2860538..4aecf6e9d9 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 @@ -9,24 +9,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.PMDConfiguration; class PMDParametersTest { - @Test - void testVersion() throws Exception { - PMDParameters parameters = new PMDParameters(); - // no language set, uses default language - assertEquals("1.7", parameters.getVersion()); - - // now set language - FieldUtils.writeDeclaredField(parameters, "language", "dummy2", true); - assertEquals("1.0", parameters.getVersion()); - } - @Test void testMultipleDirsAndRuleSets() { PmdParametersParseResult result = PmdParametersParseResult.extractParameters( diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 9c4109e95e..8ed63b71ed 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.cpd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -17,10 +20,13 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; + import com.github.stefanbirkner.systemlambda.SystemLambda; import com.google.common.collect.ImmutableMap; @@ -36,6 +42,12 @@ class CPDCommandLineInterfaceTest { @TempDir private Path tempDir; + @AfterEach + void resetLogging() { + // reset logging in case "--debug" changed the logging properties + // See also Slf4jSimpleConfigurationForAnt + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(null); + } @BeforeEach void setup() { @@ -70,7 +82,6 @@ class CPDCommandLineInterfaceTest { return expectedFilesXmlBuilder.toString(); } - @Test void testDeprecatedOptionsWarning() throws Exception { final List filepaths = Arrays.asList( @@ -94,4 +105,27 @@ class CPDCommandLineInterfaceTest { assertFalse(stderr.contains("Some deprecated options were used on the command-line, including --filelist")); assertFalse(stderr.contains("Consider replacing it with --file-list")); } + + @Test + void testDebugLogging() throws Exception { + // restoring system properties: --debug might change logging properties + SystemLambda.restoreSystemProperties(() -> { + String stderr = SystemLambda.tapSystemErr(() -> { + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files", + SRC_DIR, "--debug"); + assertEquals(CPD.StatusCode.OK, statusCode); + }); + assertThat(stderr, containsString("Tokenizing ")); // this is a debug logging + }); + } + + @Test + void testNormalLogging() throws Exception { + String stderr = SystemLambda.tapSystemErr(() -> { + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files", + SRC_DIR); + assertEquals(CPD.StatusCode.OK, statusCode); + }); + assertThat(stderr, not(containsString("Tokenizing "))); // this is a debug logging + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index f89b45dd3f..2baf9c8b9a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -216,6 +216,37 @@ class XMLRendererTest { assertEquals("888", attributes.getNamedItem("totalNumberOfTokens").getNodeValue()); } + @Test + public void testGetDuplicationStartEnd() throws IOException, ParserConfigurationException, SAXException { + TokenEntry.clearImages(); + final CPDReportRenderer renderer = new XMLRenderer(); + final List matches = new ArrayList<>(); + final String filename = "/var/Foo.java"; + final int lineCount = 6; + final String codeFragment = "code\nfragment"; + final Mark mark1 = createMark("public", filename, 1, lineCount, codeFragment, 2, 3); + final Mark mark2 = createMark("stuff", filename, 73, lineCount, codeFragment, 4, 5); + final Match match = new Match(75, mark1, mark2); + matches.add(match); + final Map numberOfTokensPerFile = new HashMap<>(); + numberOfTokensPerFile.put(filename, 888); + final CPDReport report = new CPDReport(matches, numberOfTokensPerFile); + final StringWriter writer = new StringWriter(); + renderer.render(report, writer); + final String xmlOutput = writer.toString(); + final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() + .parse(new ByteArrayInputStream(xmlOutput.getBytes(ENCODING))); + final NodeList files = doc.getElementsByTagName("file"); + final Node dup_1 = files.item(1); + final NamedNodeMap attrs_1 = dup_1.getAttributes(); + assertEquals("0", attrs_1.getNamedItem("begintoken").getNodeValue()); + assertEquals("1", attrs_1.getNamedItem("endtoken").getNodeValue()); + + final Node dup_2 = files.item(2); + final NamedNodeMap attrs_2 = dup_2.getAttributes(); + assertEquals("2", attrs_2.getNamedItem("begintoken").getNodeValue()); + assertEquals("3", attrs_2.getNamedItem("endtoken").getNodeValue()); + } @Test void testRendererXMLEscaping() throws IOException { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java index baf638e123..114fd8ccc4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java @@ -44,6 +44,11 @@ class BaseTokenFilterTest { return null; } + @Override + public TextRegion getRegion() { + return TextRegion.fromBothOffsets(0, text.length()); + } + @Override public boolean isEof() { return text == null; @@ -54,11 +59,6 @@ class BaseTokenFilterTest { return text; } - @Override - public TextRegion getRegion() { - return TextRegion.fromBothOffsets(0, text.length()); - } - @Override public FileLocation getReportLocation() { return FileLocation.range(TextFile.UNKNOWN_FILENAME, TextRange2d.range2d(1, 1, 1, 1)); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java index e5a8de77cb..a19056242a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang; +import java.util.Objects; + /** * A second dummy language used for testing PMD. */ @@ -16,4 +18,9 @@ public class Dummy2LanguageModule extends BaseLanguageModule { super(NAME, null, TERSE_NAME, "dummy2"); addVersion("1.0", new DummyLanguageModule.Handler(), true); } + + public static Dummy2LanguageModule getInstance() { + return (Dummy2LanguageModule) Objects.requireNonNull(LanguageRegistry.PMD.getLanguageByFullName(NAME)); + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index ee50a2f297..4e639fb5e1 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -49,7 +49,7 @@ public class DummyLanguageModule extends BaseLanguageModule { } public static DummyLanguageModule getInstance() { - return (DummyLanguageModule) Objects.requireNonNull(LanguageRegistry.getLanguage(NAME)); + return (DummyLanguageModule) Objects.requireNonNull(LanguageRegistry.PMD.getLanguageByFullName(NAME)); } public static DummyRootNode parse(String code) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java index 6aac4f0d58..b6bdf2bbd9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java @@ -13,18 +13,11 @@ import org.junit.jupiter.api.Test; class LanguageRegistryTest { - @Test - void getDefaultLanguageTest() { - Language defaultLanguage = LanguageRegistry.getDefaultLanguage(); - assertNotNull(defaultLanguage); - // as we don't have java language in this test, we get the first - // available language now -> DummyLanguage - assertSame(DummyLanguageModule.class, defaultLanguage.getClass()); - } + private final LanguageRegistry languageRegistry = LanguageRegistry.PMD; @Test - void getDefaultVersionLanguageTest() { - Language dummy = LanguageRegistry.findLanguageByTerseName("dummy"); + public void getDefaultVersionLanguageTest() { + Language dummy = languageRegistry.getLanguageById("dummy"); LanguageVersion dummy12 = dummy.getVersion("1.2"); assertNotNull(dummy12); @@ -35,8 +28,8 @@ class LanguageRegistryTest { } @Test - void getLanguageVersionByAliasTest() { - Language dummy = LanguageRegistry.findLanguageByTerseName("dummy"); + public void getLanguageVersionByAliasTest() { + Language dummy = languageRegistry.getLanguageById("dummy"); LanguageVersion dummy17 = dummy.getVersion("1.7"); assertNotNull(dummy17); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamTest.java new file mode 100644 index 0000000000..6e4662e5cd --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStreamTest.java @@ -0,0 +1,152 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.EOFException; +import java.io.IOException; +import java.util.Collections; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; +import net.sourceforge.pmd.lang.document.TextDocument; + +public class CharStreamTest { + + private LanguageVersion dummyVersion = DummyLanguageModule.getInstance().getDefaultVersion(); + + @Test + public void testReadZeroChars() throws IOException { + + CharStream stream = simpleCharStream(""); + + assertThrows(EOFException.class, stream::readChar); + + assertEquals(stream.getStartOffset(), 0); + assertEquals(stream.getEndOffset(), 0); + } + + @Test + public void testMultipleEofReads() throws IOException { + + CharStream stream = simpleCharStream(""); + + for (int i = 0; i < 3; i++) { + assertThrows(EOFException.class, stream::readChar); + } + + } + + @Test + public void testReadStuff() throws IOException { + + CharStream stream = simpleCharStream("abcd"); + + assertEquals('a', stream.readChar()); + assertEquals('b', stream.readChar()); + assertEquals('c', stream.readChar()); + assertEquals('d', stream.readChar()); + + assertThrows(EOFException.class, stream::readChar); + } + + @Test + public void testReadBacktrack() throws IOException { + + CharStream stream = simpleCharStream("abcd"); + + assertEquals('a', stream.markTokenStart()); + assertEquals('b', stream.readChar()); + assertEquals('c', stream.readChar()); + assertEquals('d', stream.readChar()); + + assertEquals("abcd", stream.getTokenImage()); + + stream.backup(2); + assertEquals('c', stream.readChar()); + assertEquals('d', stream.readChar()); + + + assertThrows(EOFException.class, stream::readChar); + } + + @Test + public void testReadBacktrackWithEscapes() throws IOException { + + CharStream stream = javaCharStream("__\\u00a0_\\u00a0_"); + + assertEquals('_', stream.markTokenStart()); + assertEquals('_', stream.readChar()); + assertEquals('\u00a0', stream.readChar()); + assertEquals('_', stream.readChar()); + + assertEquals("__\u00a0_", stream.getTokenImage()); + + stream.backup(2); + assertEquals('\u00a0', stream.readChar()); + assertEquals('_', stream.readChar()); + assertEquals('\u00a0', stream.readChar()); + + assertEquals("__\u00a0_\u00a0", stream.getTokenImage()); + assertEquals('_', stream.readChar()); + stream.backup(2); + assertEquals('\u00a0', stream.markTokenStart()); + assertEquals('_', stream.readChar()); + + assertEquals("\u00a0_", stream.getTokenImage()); + + assertThrows(EOFException.class, stream::readChar); + } + + @Test + public void testBacktrackTooMuch() throws IOException { + + CharStream stream = simpleCharStream("abcd"); + + assertEquals('a', stream.readChar()); + assertEquals('b', stream.readChar()); + assertEquals('c', stream.markTokenStart()); + assertEquals('d', stream.readChar()); + + stream.backup(2); // ok + + assertThrows(IllegalArgumentException.class, () -> stream.backup(1)); + } + + @Test + public void testBacktrackTooMuch2() throws IOException { + + CharStream stream = simpleCharStream("abcd"); + + assertEquals('a', stream.markTokenStart()); + assertEquals('b', stream.readChar()); + assertEquals('c', stream.readChar()); + assertEquals('d', stream.readChar()); + + assertThrows(IllegalArgumentException.class, () -> stream.backup(10)); + } + + + public CharStream simpleCharStream(String abcd) { + return CharStream.create(TextDocument.readOnlyString(abcd, dummyVersion), TokenDocumentBehavior.DEFAULT); + } + + public CharStream javaCharStream(String abcd) { + return CharStream.create( + TextDocument.readOnlyString(abcd, dummyVersion), + new TokenDocumentBehavior(Collections.emptyList()) { + @Override + public TextDocument translate(TextDocument text) throws MalformedSourceException { + return new JavaEscapeTranslator(text).translateDocument(); + } + }); + } + +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeReaderTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeReaderTest.java new file mode 100644 index 0000000000..6c0f5ec6e9 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaEscapeReaderTest.java @@ -0,0 +1,79 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.impl.javacc; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + + +public class JavaEscapeReaderTest { + + public TextDocument readString(String input) { + TextDocument intext = TextDocument.readOnlyString(Chars.wrap(input), DummyLanguageModule.getInstance().getDefaultVersion()); + return new JavaEscapeTranslator(intext).translateDocument(); + } + + + @Test + public void testSimpleRead() throws IOException { + + String input = "abcdede"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap(input), r.getText()); + } + } + + @Test + public void testNotAnEscape1Read() throws IOException { + + String input = "abc\\dede"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap(input), r.getText()); + } + } + + @Test + public void testNotAnEscape1Read2() throws IOException { + + String input = "abc\\\\\\dede"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap(input), r.getText()); + } + } + + @Test + public void testAnEscapeStopAtEnd() throws IOException { + + String input = "abc\\\\\\u00a0dede"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap("abc\u00a0dede"), r.getText()); + } + } + + @Test + public void testSeveralEscapes() throws IOException { + + String input = "abc\\\\\\u00a0d\\uu00a0ede"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap("abc\u00a0d\u00a0ede"), r.getText()); + } + } + + @Test + public void testAnEscapeInsideBlock() throws IOException { + + String input = "abc\\\\\\u00a0dede\\u00a0"; + try (TextDocument r = readString(input)) { + assertEquals(Chars.wrap("abc\u00a0dede\u00a0"), r.getText()); + } + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/CharsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/CharsTest.java index 02b3e62539..67b7976579 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/CharsTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/CharsTest.java @@ -60,6 +60,14 @@ public class CharsTest { assertEquals("b", sb.toString()); } + @Test + public void toStringBuilder() { + Chars bc = Chars.wrap("abcd").slice(1, 2); + assertEquals("bc", bc.toString()); + + assertEquals("bc", bc.toStringBuilder().toString()); + } + @Test public void write() throws IOException { StringWriter writer = new StringWriter(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java index a72fc194d1..db4eef2608 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java @@ -22,6 +22,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -52,7 +53,7 @@ class FileCollectorTest { void testAddFileForceLanguage() throws IOException { Path bar = newFile(tempFolder, "bar.unknown"); - Language dummy = LanguageRegistry.findLanguageByTerseName("dummy"); + Language dummy = DummyLanguageModule.getInstance(); FileCollector collector = newCollector(dummy.getDefaultVersion()); @@ -130,7 +131,7 @@ class FileCollectorTest { } private FileCollector newCollector(LanguageVersion forcedVersion) { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(forcedVersion); + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD, forcedVersion); FileCollector collector = FileCollector.newCollector(discoverer, new TestMessageReporter()); collector.relativizeWith(tempFolder.toAbsolutePath().toString()); return collector; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java new file mode 100644 index 0000000000..69aa3cd196 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java @@ -0,0 +1,17 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.document; + +import net.sourceforge.pmd.lang.LanguageVersion; + +/** + * Makes {@link StringTextFile} publicly available for unit tests. + */ +public class SimpleTestTextFile extends StringTextFile { + + public SimpleTestTextFile(String content, String pathId, LanguageVersion languageVersion) { + super(content, pathId, languageVersion); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java index f4ae24306a..78e85c4503 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.document; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyVersion; import static net.sourceforge.pmd.lang.document.TextPos2d.pos2d; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; @@ -14,7 +15,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.util.IOUtil; @@ -24,11 +24,9 @@ import junitparams.Parameters; @RunWith(JUnitParamsRunner.class) public class TextDocumentTest { - private final LanguageVersion dummyVersion = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); - @Test public void testSingleLineRegion() { - TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion()); TextRegion region = TextRegion.fromOffsetLength(0, "bonjour".length()); @@ -47,10 +45,10 @@ public class TextDocumentTest { @Test public void testRegionAtEol() { - TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion()); TextRegion region = TextRegion.fromOffsetLength(0, "bonjour\n".length()); - assertEquals("bonjour\n", doc.sliceText(region).toString()); + assertEquals("bonjour\n", doc.sliceOriginalText(region).toString()); FileLocation withLines = doc.toLocation(region); assertEquals(1, withLines.getStartLine()); @@ -62,12 +60,12 @@ public class TextDocumentTest { @Test public void testEmptyRegionAtEol() { - TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion()); // ^ The caret position right after the \n // We consider it's part of the next line TextRegion region = TextRegion.fromOffsetLength("bonjour\n".length(), 0); - assertEquals("", doc.sliceText(region).toString()); + assertEquals("", doc.sliceOriginalText(region).toString()); FileLocation withLines = doc.toLocation(region); @@ -79,13 +77,13 @@ public class TextDocumentTest { @Test public void testRegionForEol() { - TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\ntristesse", dummyVersion()); // [ [ The region containing the \n // We consider it ends on the same line, not the next one TextRegion region = TextRegion.fromOffsetLength("bonjour".length(), 1); - assertEquals("\n", doc.sliceText(region).toString()); + assertEquals("\n", doc.sliceOriginalText(region).toString()); FileLocation withLines = doc.toLocation(region); @@ -97,10 +95,10 @@ public class TextDocumentTest { @Test public void testRegionAtEndOfFile() { - TextDocument doc = TextDocument.readOnlyString("flemme", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("flemme", dummyVersion()); TextRegion region = TextRegion.fromOffsetLength(0, doc.getLength()); - assertEquals(doc.getText(), doc.sliceText(region)); + assertEquals(doc.getText(), doc.sliceOriginalText(region)); FileLocation withLines = doc.toLocation(region); @@ -112,7 +110,7 @@ public class TextDocumentTest { @Test public void testMultiLineRegion() { - TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion()); TextRegion region = TextRegion.fromOffsetLength("bonjou".length(), "r\noha\ntri".length()); @@ -131,7 +129,7 @@ public class TextDocumentTest { @Test public void testLineColumnFromOffset() { - TextDocument doc = TextDocument.readOnlyString("ab\ncd\n", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("ab\ncd\n", dummyVersion()); assertPos2dEqualsAt(doc, 0, "a", pos2d(1, 1), true); assertPos2dEqualsAt(doc, 0, "a", pos2d(1, 1), false); @@ -147,14 +145,14 @@ public class TextDocumentTest { } private void assertPos2dEqualsAt(TextDocument doc, int offset, String c, TextPos2d pos, boolean inclusive) { - Chars slicedChar = doc.sliceText(TextRegion.fromOffsetLength(offset, 1)); + Chars slicedChar = doc.sliceTranslatedText(TextRegion.fromOffsetLength(offset, 1)); assertEquals(c, slicedChar.toString()); assertEquals(pos, doc.lineColumnAtOffset(offset, inclusive)); } @Test public void testEmptyRegion() { - TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion()); TextRegion region = TextRegion.fromOffsetLength("bonjour".length(), 0); @@ -173,12 +171,12 @@ public class TextDocumentTest { @Test public void testLineRange() { - TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion()); - assertEquals(Chars.wrap("bonjour\n"), doc.sliceText(doc.createLineRange(1, 1))); - assertEquals(Chars.wrap("bonjour\noha\n"), doc.sliceText(doc.createLineRange(1, 2))); - assertEquals(Chars.wrap("oha\n"), doc.sliceText(doc.createLineRange(2, 2))); - assertEquals(Chars.wrap("oha\ntristesse"), doc.sliceText(doc.createLineRange(2, 3))); + assertEquals(Chars.wrap("bonjour\n"), doc.sliceTranslatedText(doc.createLineRange(1, 1))); + assertEquals(Chars.wrap("bonjour\noha\n"), doc.sliceTranslatedText(doc.createLineRange(1, 2))); + assertEquals(Chars.wrap("oha\n"), doc.sliceTranslatedText(doc.createLineRange(2, 2))); + assertEquals(Chars.wrap("oha\ntristesse"), doc.sliceTranslatedText(doc.createLineRange(2, 3))); assertThrows(IndexOutOfBoundsException.class, () -> doc.createLineRange(2, 1)); assertThrows(IndexOutOfBoundsException.class, () -> doc.createLineRange(1, 5)); assertThrows(IndexOutOfBoundsException.class, () -> doc.createLineRange(0, 2)); @@ -205,7 +203,7 @@ public class TextDocumentTest { @Test public void testRegionOutOfBounds() { - TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion); + TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion()); assertThrows(AssertionError.class, () -> TextRegion.isValidRegion(0, 40, doc)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java index 1aafb3404b..0d0effe48b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.document; +import static net.sourceforge.pmd.PmdCoreTestUtils.dummyVersion; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -25,7 +26,6 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.FileDataSource; @@ -34,15 +34,13 @@ public class TextFilesTest { @Rule public TemporaryFolder tempDir = TemporaryFolder.builder().build(); - private LanguageVersion dummyVersion = DummyLanguageModule.getInstance().getDefaultVersion(); - @Test public void testNioFile() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content"); - try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { + try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { assertEquals(file.toAbsolutePath().toString(), tf.getPathId()); assertEquals(file.toString(), tf.getDisplayName()); - assertEquals(dummyVersion, tf.getLanguageVersion()); + assertEquals(dummyVersion(), tf.getLanguageVersion()); assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); } } @@ -50,9 +48,9 @@ public class TextFilesTest { @Test public void testEquals() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content").toAbsolutePath(); - try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { - try (TextFile tfPrime = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { - try (TextFile stringTf = TextFile.forCharSeq("some content", file.toString(), dummyVersion)) { + try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { + try (TextFile tfPrime = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { + try (TextFile stringTf = TextFile.forCharSeq("some content", file.toString(), dummyVersion())) { assertEquals(tf.getPathId(), stringTf.getPathId()); // despite same path id, they are different implementations @@ -60,7 +58,7 @@ public class TextFilesTest { assertNotEquals(stringTf, tf); // identical, but string text files use identity - assertNotEquals(stringTf, TextFile.forCharSeq("some content", file.toString(), dummyVersion)); + assertNotEquals(stringTf, TextFile.forCharSeq("some content", file.toString(), dummyVersion())); // those are identical so are equals assertNotSame(tf, tfPrime); @@ -90,7 +88,7 @@ public class TextFilesTest { DataSource ds = new FileDataSource(file.toFile()); PMDConfiguration config = new PMDConfiguration(); - config.setForceLanguageVersion(dummyVersion); + config.setForceLanguageVersion(DummyLanguageModule.getInstance().getDefaultVersion()); try (TextFile tf = TextFile.dataSourceCompat(ds, config)) { assertEquals(ds.getNiceFileName(false, null), tf.getPathId()); assertEquals(ds.getNiceFileName(false, null), tf.getDisplayName()); @@ -104,6 +102,7 @@ public class TextFilesTest { DataSource ds = new FileDataSource(file.toFile()); PMDConfiguration config = new PMDConfiguration(); + config.setForceLanguageVersion(DummyLanguageModule.getInstance().getDefaultVersion()); config.setSourceEncoding(StandardCharsets.UTF_16BE.name()); try (TextFile tf = TextFile.dataSourceCompat(ds, config)) { assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); @@ -119,7 +118,7 @@ public class TextFilesTest { @Test public void testNioFileWrite() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content"); - try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { + try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); assertFalse("readonly", tf.isReadOnly()); @@ -145,7 +144,7 @@ public class TextFilesTest { @Test public void testNioFileExplicitReadOnly() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content"); - try (TextFile tf = TextFile.builderForPath(file, StandardCharsets.UTF_8, dummyVersion) + try (TextFile tf = TextFile.builderForPath(file, StandardCharsets.UTF_8, dummyVersion()) .asReadOnly().build()) { assertTrue("readonly", tf.isReadOnly()); @@ -158,7 +157,7 @@ public class TextFilesTest { @Test public void testNioFileCanBeReadMultipleTimes() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content"); - try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { + try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); } @@ -167,12 +166,12 @@ public class TextFilesTest { @Test public void testNioFileBuilder() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some content"); - try (TextFile tf = TextFile.builderForPath(file, StandardCharsets.UTF_8, dummyVersion) + try (TextFile tf = TextFile.builderForPath(file, StandardCharsets.UTF_8, dummyVersion()) .withDisplayName("aname") .build()) { assertEquals(file.toAbsolutePath().toString(), tf.getPathId()); assertEquals("aname", tf.getDisplayName()); - assertEquals(dummyVersion, tf.getLanguageVersion()); + assertEquals(dummyVersion(), tf.getLanguageVersion()); assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); } } @@ -180,7 +179,7 @@ public class TextFilesTest { @Test public void testNioFileEscape() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some\r\ncontent"); - try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion)) { + try (TextFile tf = TextFile.forPath(file, StandardCharsets.UTF_8, dummyVersion())) { assertEquals(Chars.wrap("some\ncontent"), tf.readContents().getNormalizedText()); } } @@ -188,10 +187,10 @@ public class TextFilesTest { @Test public void testReaderFile() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some\r\ncontent"); - try (TextFile tf = TextFile.forReader(Files.newBufferedReader(file, StandardCharsets.UTF_8), "filename", dummyVersion)) { + try (TextFile tf = TextFile.forReader(Files.newBufferedReader(file, StandardCharsets.UTF_8), "filename", dummyVersion())) { assertEquals("filename", tf.getPathId()); assertEquals("filename", tf.getDisplayName()); - assertEquals(dummyVersion, tf.getLanguageVersion()); + assertEquals(dummyVersion(), tf.getLanguageVersion()); assertEquals(Chars.wrap("some\ncontent"), tf.readContents().getNormalizedText()); } } @@ -199,7 +198,7 @@ public class TextFilesTest { @Test public void testReaderFileIsReadOnly() throws IOException { Path file = makeTmpFile(StandardCharsets.UTF_8, "some\r\ncontent"); - try (TextFile tf = TextFile.forReader(Files.newBufferedReader(file, StandardCharsets.UTF_8), "filename", dummyVersion)) { + try (TextFile tf = TextFile.forReader(Files.newBufferedReader(file, StandardCharsets.UTF_8), "filename", dummyVersion())) { assertTrue("readonly", tf.isReadOnly()); assertThrows(ReadOnlyFileException.class, () -> tf.writeContents( TextFileContent.fromCharSeq("new content") @@ -209,10 +208,10 @@ public class TextFilesTest { @Test public void testStringFileEscape() throws IOException { - try (TextFile tf = TextFile.forCharSeq("cont\r\nents", "filename", dummyVersion)) { + try (TextFile tf = TextFile.forCharSeq("cont\r\nents", "filename", dummyVersion())) { assertEquals("filename", tf.getPathId()); assertEquals("filename", tf.getDisplayName()); - assertEquals(dummyVersion, tf.getLanguageVersion()); + assertEquals(dummyVersion(), tf.getLanguageVersion()); assertEquals(Chars.wrap("cont\nents"), tf.readContents().getNormalizedText()); assertThrows(ReadOnlyFileException.class, () -> tf.writeContents( TextFileContent.fromCharSeq("new content") @@ -222,7 +221,7 @@ public class TextFilesTest { @Test public void testStringFileCanBeReadMultipleTimes() throws IOException { - try (TextFile tf = TextFile.forCharSeq("contents", "filename", dummyVersion)) { + try (TextFile tf = TextFile.forCharSeq("contents", "filename", dummyVersion())) { assertEquals(Chars.wrap("contents"), tf.readContents().getNormalizedText()); assertEquals(Chars.wrap("contents"), tf.readContents().getNormalizedText()); assertEquals(Chars.wrap("contents"), tf.readContents().getNormalizedText()); @@ -231,7 +230,7 @@ public class TextFilesTest { @Test public void testStringFileIsReadonly() throws IOException { - try (TextFile tf = TextFile.forCharSeq("contents", "filename", dummyVersion)) { + try (TextFile tf = TextFile.forCharSeq("contents", "filename", dummyVersion())) { assertTrue("readonly", tf.isReadOnly()); assertThrows(ReadOnlyFileException.class, () -> tf.writeContents( TextFileContent.fromCharSeq("new content") diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java index fa94fc20a2..56621fc092 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.rule; +import static net.sourceforge.pmd.PmdCoreTestUtils.setDummyLanguage; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,7 +15,6 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleContextTest; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute; @@ -80,7 +80,7 @@ class XPathRuleTest { XPathRule makeRule(XPathVersion version, String name) { XPathRule xpr = new XPathRule(version, "//dummyNode[@Size >= 2 and @Name='foo']"); xpr.setName(name); - xpr.setLanguage(LanguageRegistry.getLanguage("Dummy")); + setDummyLanguage(xpr); xpr.setMessage("gotcha"); return xpr; } @@ -88,7 +88,7 @@ class XPathRuleTest { XPathRule makeXPath(String xpathExpr) { XPathRule xpr = new XPathRule(XPathVersion.XPATH_2_0, xpathExpr); - xpr.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + setDummyLanguage(xpr); xpr.setName("name"); xpr.setMessage("gotcha"); return xpr; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java index 4211a38698..15ab5abe2e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java @@ -18,7 +18,7 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.document.TextFile; @@ -37,7 +37,7 @@ class MultiThreadProcessorTest { RuleSets setUpForTest(final String ruleset) { configuration = new PMDConfiguration(); configuration.setThreads(2); - LanguageVersion lv = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); + LanguageVersion lv = DummyLanguageModule.getInstance().getDefaultVersion(); files = listOf( TextFile.forCharSeq("abc", "file1-violation.dummy", lv), TextFile.forCharSeq("DEF", "file2-foo.dummy", lv) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java index 400a217104..415a7f2845 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java @@ -38,7 +38,6 @@ import net.sourceforge.pmd.internal.util.ContextedAssertionError; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.DummyLanguageModule.Handler; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.Parser; @@ -175,7 +174,7 @@ public class PmdRunnableTest { private static class RuleThatThrows extends AbstractRule { RuleThatThrows() { - Language dummyLanguage = LanguageRegistry.findLanguageByTerseName(DummyLanguageModule.TERSE_NAME); + Language dummyLanguage = DummyLanguageModule.getInstance(); setLanguage(dummyLanguage); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java index 0f2e1e84c2..c2c13b7c83 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java @@ -92,8 +92,7 @@ class XMLRendererTest extends AbstractRendererTest { } private RuleViolation createRuleViolation(String description) { - FileLocation loc = FileLocation.range(getSourceCodeFilename(), - TextRange2d.range2d(1, 1, 1, 1)); + FileLocation loc = FileLocation.range(getSourceCodeFilename(), TextRange2d.range2d(1, 1, 1, 1)); return new ParametricRuleViolation(new FooRule(), loc, description); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/FooRuleWithLanguageSetInJava.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/FooRuleWithLanguageSetInJava.java index 0dbd92b89c..38c748645f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/FooRuleWithLanguageSetInJava.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/FooRuleWithLanguageSetInJava.java @@ -6,14 +6,13 @@ package net.sourceforge.pmd.util; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; public class FooRuleWithLanguageSetInJava extends AbstractRule { public FooRuleWithLanguageSetInJava() { - setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); + setLanguage(DummyLanguageModule.getInstance()); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java index 6f7226a6d6..71f8449ab9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java @@ -34,7 +34,7 @@ class TreeExportCliTest { @Test void testReadStandardInput() { IoSpy spy = IoSpy.withStdin("(a(b))"); - int status = spy.runMain("-i", "-f", "xml", "-PlineSeparator=LF"); + int status = spy.runMain("-i", "-f", "xml", "-PlineSeparator=LF", "-l", "dummy"); assertEquals(0, status); spy.assertThatStdout(containsString("\n" + "\n" @@ -48,7 +48,7 @@ class TreeExportCliTest { void testReadFile() throws IOException { File file = newFileWithContents("(a(b))"); IoSpy spy = new IoSpy(); - int status = spy.runMain("--file", file.getAbsolutePath(), "-f", "xml", "-PlineSeparator=LF"); + int status = spy.runMain("--file", file.getAbsolutePath(), "-f", "xml", "-PlineSeparator=LF", "-l", "dummy"); assertEquals(0, status); spy.assertThatStdout(containsString("\n" + "\n" diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset2.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset2.xml index dfaa96ecea..1e4f587102 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset2.xml +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset2.xml @@ -29,4 +29,4 @@ Just for test ]]> - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset3.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset3.xml index 7b3f1a8924..dc0e72f4f5 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset3.xml +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset3.xml @@ -29,4 +29,4 @@ Just for test ]]> - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset4.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset4.xml index 0923d45e34..9901cbbd17 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset4.xml +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRuleset4.xml @@ -29,4 +29,4 @@ Just for test ]]> - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/external-reference-ruleset.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/external-reference-ruleset.xml index 3f6b18bb10..6515a57737 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/external-reference-ruleset.xml +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/external-reference-ruleset.xml @@ -18,4 +18,4 @@ Just for test - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/rulesets/dummy/basic2.xml b/pmd-core/src/test/resources/rulesets/dummy/basic2.xml index bdbb5444b1..34cf83ae03 100644 --- a/pmd-core/src/test/resources/rulesets/dummy/basic2.xml +++ b/pmd-core/src/test/resources/rulesets/dummy/basic2.xml @@ -22,4 +22,4 @@ Just for test - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/rulesets/dummy/unusedcode.xml b/pmd-core/src/test/resources/rulesets/dummy/unusedcode.xml index fd0b7670dc..d0a5bf97c1 100644 --- a/pmd-core/src/test/resources/rulesets/dummy/unusedcode.xml +++ b/pmd-core/src/test/resources/rulesets/dummy/unusedcode.xml @@ -17,4 +17,4 @@ Just for test ]]> - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/rulesets/dummy2/basic.xml b/pmd-core/src/test/resources/rulesets/dummy2/basic.xml index b03bdf0b50..f87efc6718 100644 --- a/pmd-core/src/test/resources/rulesets/dummy2/basic.xml +++ b/pmd-core/src/test/resources/rulesets/dummy2/basic.xml @@ -17,4 +17,4 @@ Just for test ]]> - \ No newline at end of file + diff --git a/pmd-core/src/test/resources/sample-source/dummy/foo.txt b/pmd-core/src/test/resources/sample-source/dummy/foo.txt new file mode 100644 index 0000000000..adaad87177 --- /dev/null +++ b/pmd-core/src/test/resources/sample-source/dummy/foo.txt @@ -0,0 +1 @@ +A dummy file with file extension txt. diff --git a/pmd-cpp/etc/grammar/Cpp.jj b/pmd-cpp/etc/grammar/Cpp.jj index 95640555eb..85aca73e42 100644 --- a/pmd-cpp/etc/grammar/Cpp.jj +++ b/pmd-cpp/etc/grammar/Cpp.jj @@ -32,7 +32,7 @@ options { PARSER_BEGIN(CppParserImpl) package net.sourceforge.pmd.lang.cpp.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; public final class CppParserImpl { diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index 8cd90dcaee..0728e9afbf 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -4,18 +4,17 @@ package net.sourceforge.pmd.cpd; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; import java.util.Properties; +import java.util.regex.Pattern; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.cpp.ast.CppCharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException; import net.sourceforge.pmd.lang.cpp.ast.CppTokenKinds; import net.sourceforge.pmd.lang.document.TextDocument; @@ -25,8 +24,8 @@ import net.sourceforge.pmd.lang.document.TextDocument; public class CPPTokenizer extends JavaCCTokenizer { private boolean skipBlocks; - private String skipBlocksStart; - private String skipBlocksEnd; + private Pattern skipBlocksStart; + private Pattern skipBlocksEnd; private boolean ignoreLiteralSequences = false; public CPPTokenizer() { @@ -46,51 +45,30 @@ public class CPPTokenizer extends JavaCCTokenizer { if (skipBlocks) { String skipBlocksPattern = properties.getProperty(OPTION_SKIP_BLOCKS_PATTERN, DEFAULT_SKIP_BLOCKS_PATTERN); String[] split = skipBlocksPattern.split("\\|", 2); - skipBlocksStart = split[0]; + skipBlocksStart = CppBlockSkipper.compileSkipMarker(split[0]); if (split.length == 1) { - skipBlocksEnd = split[0]; + skipBlocksEnd = skipBlocksStart; } else { - skipBlocksEnd = split[1]; + skipBlocksEnd = CppBlockSkipper.compileSkipMarker(split[1]); } } ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.FALSE.toString())); } - /** - * Unused method, will be fixed in followup branch. - * FIXME un-ignore tests - */ - @Deprecated - public String maybeSkipBlocks(String test) throws IOException { - if (!skipBlocks) { - return test; - } - - try (BufferedReader reader = new BufferedReader(new StringReader(test))) { - StringBuilder filtered = new StringBuilder(test.length()); - String line; - boolean skip = false; - while ((line = reader.readLine()) != null) { - if (skipBlocksStart.equalsIgnoreCase(line.trim())) { - skip = true; - } else if (skip && skipBlocksEnd.equalsIgnoreCase(line.trim())) { - skip = false; - } - if (!skip) { - filtered.append(line); - } - // always add a new line to keep the line-numbering - filtered.append(System.lineSeparator()); - } - return filtered.toString(); - } - } - @Override - protected CharStream makeCharStream(TextDocument sourceCode) { - return CppCharStream.newCppCharStream(sourceCode); + protected TokenDocumentBehavior tokenBehavior() { + return new TokenDocumentBehavior(CppTokenKinds.TOKEN_NAMES) { + + @Override + public TextDocument translate(TextDocument text) throws MalformedSourceException { + if (skipBlocks) { + text = new CppBlockSkipper(text, skipBlocksStart, skipBlocksEnd).translateDocument(); + } + return new CppEscapeTranslator(text).translateDocument(); + } + }; } @Override @@ -98,13 +76,6 @@ public class CPPTokenizer extends JavaCCTokenizer { return CppTokenKinds.newTokenManager(sourceCode); } - @SuppressWarnings("PMD.CloseResource") - @Override - protected TokenManager getLexerForSource(TextDocument sourceCode) { - CharStream charStream = makeCharStream(sourceCode); - return makeLexerImpl(charStream); - } - @Override protected TokenFilter getTokenFilter(final TokenManager tokenManager) { return new CppTokenFilter(tokenManager, ignoreLiteralSequences); diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java new file mode 100644 index 0000000000..35857ef375 --- /dev/null +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java @@ -0,0 +1,44 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sourceforge.pmd.lang.ast.impl.javacc.EscapeTranslator; +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * + */ +class CppBlockSkipper extends EscapeTranslator { + + private final Pattern skipStart; + private final Pattern skipEnd; + + static Pattern compileSkipMarker(String marker) { + return Pattern.compile("^(?i)" + Pattern.quote(marker), Pattern.MULTILINE); + } + + CppBlockSkipper(TextDocument original, Pattern skipStartMarker, Pattern skipEndMarker) { + super(original); + skipStart = skipStartMarker; + skipEnd = skipEndMarker; + } + + @Override + protected int gobbleMaxWithoutEscape(int maxOff) throws MalformedSourceException { + Matcher start = skipStart.matcher(input).region(this.bufpos, maxOff); + if (start.find()) { + Matcher end = skipEnd.matcher(input).region(start.end(), maxOff); + if (end.find()) { + return recordEscape(start.start(), end.end(), Chars.EMPTY); + } + } + return super.gobbleMaxWithoutEscape(maxOff); + } +} diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java new file mode 100644 index 0000000000..b89904ffaf --- /dev/null +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java @@ -0,0 +1,35 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.ast.impl.javacc.BackslashEscapeTranslator; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + +public class CppEscapeTranslator extends BackslashEscapeTranslator { + + private static final char NEWLINE = '\n'; + private static final char CARRIAGE_RETURN = '\r'; + + public CppEscapeTranslator(TextDocument input) { + super(input); + } + + @Override + protected int handleBackslash(int maxOff, final int backSlashOff) { + int off = backSlashOff + 1; + + if (input.charAt(off) == NEWLINE) { + return recordEscape(backSlashOff, off + 1, Chars.EMPTY); + } else if (input.charAt(off) == CARRIAGE_RETURN) { + off++; + if (input.charAt(off) == NEWLINE) { + return recordEscape(backSlashOff, off + 1, Chars.EMPTY); + } + } + + return abortEscape(off, maxOff); + } +} diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStream.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStream.java deleted file mode 100644 index c1f4f2836c..0000000000 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStream.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.cpp.ast; - -import java.io.IOException; -import java.util.regex.Pattern; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; -import net.sourceforge.pmd.lang.ast.impl.javacc.SimpleCharStream; -import net.sourceforge.pmd.lang.document.TextDocument; - -/** - * A SimpleCharStream, that supports the continuation of lines via backslash+newline, - * which is used in C/C++. - * - * @author Andreas Dangel - */ -public class CppCharStream extends SimpleCharStream { - - private static final Pattern CONTINUATION = Pattern.compile("\\\\\\n|\\\\\\r\\n"); - private static final char BACKSLASH = '\\'; - private static final char NEWLINE = '\n'; - private static final char CARRIAGE_RETURN = '\r'; - - CppCharStream(JavaccTokenDocument document) { - super(document); - } - - - @Override - public char readChar() throws IOException { - char c = super.readChar(); - if (c == BACKSLASH) { - char c1 = super.readChar(); - if (c1 == NEWLINE) { - c = super.readChar(); - } else if (c1 == CARRIAGE_RETURN) { - char c2 = super.readChar(); - if (c2 == NEWLINE) { - c = super.readChar(); - } else { - backup(2); - } - } else { - backup(1); - } - } - return c; - } - - @Override - public char[] GetSuffix(int len) { - String image = GetImage(); - return image.substring(image.length() - len, image.length()).toCharArray(); - } - - @Override - public String GetImage() { - String image = super.GetImage(); - return CONTINUATION.matcher(image).replaceAll(""); - } - - public static CppCharStream newCppCharStream(TextDocument file) { - JavaccTokenDocument document = new JavaccTokenDocument(file) { - @Override - protected @Nullable String describeKindImpl(int kind) { - return CppTokenKinds.describe(kind); - } - }; - return new CppCharStream(document); - } -} diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java index d70d576d69..b4746b7f64 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java @@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Properties; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -84,13 +83,11 @@ class CPPTokenizerTest extends CpdTextComparisonTest { } @Test - @Disabled void testTokenizerWithSkipBlocks() { doTest("simpleSkipBlocks", "_skipDefault", skipBlocks()); } @Test - @Disabled void testTokenizerWithSkipBlocksPattern() { doTest("simpleSkipBlocks", "_skipDebug", skipBlocks("#if debug|#endif")); } diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStreamTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java similarity index 57% rename from pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStreamTest.java rename to pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java index ca6dd12ffb..3546db9e6c 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/ast/CppCharStreamTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.cpp.ast; +package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -11,45 +11,49 @@ import java.io.IOException; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.document.CpdCompat; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; class CppCharStreamTest { - private @NonNull CppCharStream newCharStream(String code) { - TextDocument tf = TextDocument.readOnlyString(code, TextFile.UNKNOWN_FILENAME, CpdCompat.dummyVersion()); - return CppCharStream.newCppCharStream(tf); + @NonNull + public CharStream charStreamFor(String source) throws IOException { + TextDocument textDoc = TextDocument.readOnlyString(source, TextFile.UNKNOWN_FILENAME, CpdCompat.dummyVersion()); + return CharStream.create(textDoc, new CPPTokenizer().tokenBehavior()); } @Test void testContinuationUnix() throws IOException { - CppCharStream stream = newCharStream("a\\\nb"); + CharStream stream = charStreamFor("a\\\nb"); assertStream(stream, "ab"); } @Test void testContinuationWindows() throws IOException { // note that the \r is normalized to a \n by the TextFile - CppCharStream stream = newCharStream("a\\\r\nb"); + CharStream stream = charStreamFor("a\\\r\nb"); assertStream(stream, "ab"); } @Test void testBackup() throws IOException { // note that the \r is normalized to a \n by the TextFile - CppCharStream stream = newCharStream("a\\b\\qc"); + CharStream stream = charStreamFor("a\\b\\qc"); assertStream(stream, "a\\b\\qc"); } - private void assertStream(CppCharStream stream, String token) throws IOException { - char c = stream.BeginToken(); + private void assertStream(CharStream stream, String token) throws IOException { + char c = stream.markTokenStart(); assertEquals(token.charAt(0), c); for (int i = 1; i < token.length(); i++) { c = stream.readChar(); assertEquals(token.charAt(i), c, token + " char at " + i + ": " + token.charAt(i) + " != " + c); } - assertEquals(token, stream.GetImage()); - assertEquals(token, new String(stream.GetSuffix(token.length()))); + assertEquals(token, stream.getTokenImage()); + // StringBuilder sb = new StringBuilder(); + // stream.appendSuffix(sb, token.length()); + // assertEquals(token, sb.toString()); } } diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/continuation_intra_token.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/continuation_intra_token.txt index 84845c906f..df9e43234b 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/continuation_intra_token.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/continuation_intra_token.txt @@ -1,13 +1,14 @@ [Image] or [Truncated image[ Bcol Ecol L1 - [void] 1 4 -L5 - [main] 2 4 + [void] 1 2 +L6 + [main] 1 2 L10 [(] 1 2 - [)] 2 2 -L12 - [{] 2 2 -L14 - [}] 2 2 +L11 + [)] 1 2 +L13 + [{] 1 2 +L15 + [}] 1 2 EOF diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index 2b3b19c424..56cdad1a81 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -48,17 +48,17 @@ public final class GenerateRuleDocsCmd { System.out.println("Generated docs in " + (System.currentTimeMillis() - start) + " ms"); } + static final Pattern ADDITIONAL_RULESET_PATTERN = Pattern.compile("^.+" + Pattern.quote(File.separator) + "pmd-\\w+" + + Pattern.quote(IOUtil.normalizePath(File.separator + Paths.get("src", "main", "resources", "rulesets").toString()) + File.separator) + + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); public static List findAdditionalRulesets(Path basePath) { try { List additionalRulesets = new ArrayList<>(); - Pattern rulesetPattern = Pattern.compile("^.+" + Pattern.quote(File.separator) + "pmd-\\w+" - + Pattern.quote(IOUtil.normalizePath(File.separator + Paths.get("src", "main", "resources", "rulesets").toString() + File.separator)) - + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); Files.walkFileTree(basePath, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (rulesetPattern.matcher(file.toString()).matches()) { + if (ADDITIONAL_RULESET_PATTERN.matcher(file.toString()).matches()) { additionalRulesets.add(file.toString()); } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index d603abccf0..953723d610 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.docs; import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.nio.file.FileSystems; import java.nio.file.Path; @@ -28,11 +30,19 @@ public class RuleSetResolverTest { filterRuleSets(additionalRulesets); + assertFalse(additionalRulesets.isEmpty()); + for (String filename : additionalRulesets) { new RuleSetLoader().warnDeprecated(false).loadFromResource(filename); // will throw if invalid } } + @Test + public void testAdditionalRulesetPattern() { + String filePath = IOUtil.normalizePath("/home/foo/pmd/pmd-java/src/main/resources/rulesets/java/quickstart.xml"); + assertTrue(GenerateRuleDocsCmd.ADDITIONAL_RULESET_PATTERN.matcher(filePath).matches()); + } + private void filterRuleSets(List additionalRulesets) { additionalRulesets.removeIf(this::isExcluded); } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java index 0ae7e05323..3ad66fe4bf 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java @@ -41,8 +41,8 @@ public class SidebarGeneratorTest { Map> rulesets = new HashMap<>(); RuleSet ruleSet1 = RuleSet.create("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); RuleSet ruleSet2 = RuleSet.create("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - rulesets.put(LanguageRegistry.findLanguageByTerseName("java"), Arrays.asList(ruleSet1, ruleSet2)); - rulesets.put(LanguageRegistry.findLanguageByTerseName("ecmascript"), Arrays.asList(ruleSet1)); + rulesets.put(LanguageRegistry.PMD.getLanguageById("java"), Arrays.asList(ruleSet1, ruleSet2)); + rulesets.put(LanguageRegistry.PMD.getLanguageById("ecmascript"), Arrays.asList(ruleSet1)); SidebarGenerator generator = new SidebarGenerator(writer, FileSystems.getDefault().getPath("..")); List> result = generator.generateRuleReferenceSection(rulesets); diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/rule/AbstractHtmlRule.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/rule/AbstractHtmlRule.java index 246b3dc0eb..3740d51245 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/rule/AbstractHtmlRule.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/rule/AbstractHtmlRule.java @@ -5,18 +5,12 @@ package net.sourceforge.pmd.lang.html.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.html.HtmlLanguageModule; import net.sourceforge.pmd.lang.html.ast.HtmlVisitor; import net.sourceforge.pmd.lang.rule.AbstractRule; public abstract class AbstractHtmlRule extends AbstractRule implements HtmlVisitor { - public AbstractHtmlRule() { - super.setLanguage(LanguageRegistry.getLanguage(HtmlLanguageModule.NAME)); - } - @Override public Object visitNode(Node node, Object param) { node.children().forEach(c -> c.acceptVisitor(this, param)); diff --git a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlJavaRuleTest.java b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlJavaRuleTest.java index 1859effd6e..6341761f43 100644 --- a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlJavaRuleTest.java +++ b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlJavaRuleTest.java @@ -51,6 +51,7 @@ class HtmlJavaRuleTest { return super.visit(node, data); } }; + rule.setLanguage(HtmlParsingHelper.DEFAULT.getLanguage()); List violations = runRule(LIGHTNING_WEB_COMPONENT, rule); assertEquals(2, violations.size()); assertEquals(4, violations.get(0).getBeginLine()); diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 909f411e31..97fa94a9c2 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -280,7 +280,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.Map; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.ast.Node; @@ -2659,14 +2659,14 @@ void AssertStatement() : void RUNSIGNEDSHIFT() #void: {} { - LOOKAHEAD({ JavaTokenDocument.getRealKind(getToken(1)) == RUNSIGNEDSHIFT}) + LOOKAHEAD({ JavaTokenDocumentBehavior.getRealKind(getToken(1)) == RUNSIGNEDSHIFT}) ">" ">" ">" } void RSIGNEDSHIFT() #void: {} { - LOOKAHEAD({ JavaTokenDocument.getRealKind(getToken(1)) == RSIGNEDSHIFT}) + LOOKAHEAD({ JavaTokenDocumentBehavior.getRealKind(getToken(1)) == RSIGNEDSHIFT}) ">" ">" } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index a05404e510..525d1731b2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -13,10 +13,9 @@ import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStreamFactory; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.JavaTokenKinds; @@ -44,8 +43,8 @@ public class JavaTokenizer extends JavaCCTokenizer { } @Override - protected CharStream makeCharStream(TextDocument sourceCode) { - return CharStreamFactory.javaCharStream(sourceCode, InternalApiBridge::javaTokenDoc); + protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { + return InternalApiBridge.javaTokenDoc(); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index 88e6129c17..25085f84b4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.java; import net.sourceforge.pmd.lang.BaseLanguageModule; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.java.internal.JavaLanguageHandler; /** @@ -38,4 +40,7 @@ public class JavaLanguageModule extends BaseLanguageModule { addVersion("19-preview", new JavaLanguageHandler(19, true)); } + public static Language getInstance() { + return LanguageRegistry.PMD.getLanguageByFullName(NAME); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBlock.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBlock.java index 3d9e998072..71cbc72dc0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBlock.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBlock.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.java.ast.ASTList.ASTMaybeEmptyListOf; +import net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils; /** * A block of code. This is a {@linkplain ASTStatement statement} that @@ -33,7 +34,7 @@ public final class ASTBlock extends ASTMaybeEmptyListOf implements public boolean containsComment() { JavaccToken t = getLastToken().getPreviousComment(); while (t != null) { - if (JavaTokenDocument.isComment(t)) { + if (JavaAstUtils.isComment(t)) { return true; } t = t.getPreviousComment(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTStringLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTStringLiteral.java index 5b49e497d2..651eefe415 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTStringLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTStringLiteral.java @@ -10,6 +10,7 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; import net.sourceforge.pmd.util.StringUtil; /** @@ -28,6 +29,8 @@ public final class ASTStringLiteral extends AbstractLiteral implements ASTLitera } + // todo deprecate this + // it's ambiguous whether it returns getOriginalText or getTranslatedText @Override public String getImage() { return getText().toString(); @@ -56,6 +59,17 @@ public final class ASTStringLiteral extends AbstractLiteral implements ASTLitera return getConstValue().length(); } + /** + * Returns a string where non-printable characters have been escaped + * using Java-like escape codes (eg \n, \t, \u005cu00a0). + */ + // ^^^^^^ + // this is a backslash, it's printed as \u00a0 + @NoAttribute + public @NonNull String toPrintableString() { + return StringUtil.inDoubleQuotes(StringUtil.escapeJava(getConstValue())); + } + @Override protected R acceptVisitor(JavaVisitor visitor, P data) { return visitor.visit(this, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Comment.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Comment.java index 6ef9eaa644..172f6c4e05 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Comment.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Comment.java @@ -75,7 +75,7 @@ public abstract class Comment implements Reportable { * @return List of lines of the comments */ private List multiLinesIn() { - String[] lines = NEWLINES_PATTERN.split(getText()); + String[] lines = NEWLINES_PATTERN.split(token.getImageCs()); List filteredLines = new ArrayList<>(lines.length); for (String rawLine : lines) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java index d46e64ec4c..ba98cec0dc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java @@ -11,7 +11,6 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; @@ -192,8 +191,8 @@ public final class InternalApiBridge { CommentAssignmentPass.assignCommentsToDeclarations(root); } - public static JavaccTokenDocument javaTokenDoc(TextDocument fullText) { - return new JavaTokenDocument(fullText); + public static JavaccTokenDocument.TokenDocumentBehavior javaTokenDoc() { + return JavaTokenDocumentBehavior.INSTANCE; } public static void setStandaloneTernary(ASTConditionalExpression node) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java index d8f8a5122a..41971020dc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java @@ -5,12 +5,10 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.ast.AstInfo; -import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaCharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor; @@ -32,13 +30,8 @@ public class JavaParser extends JjtreeParserAdapter { @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument textDocument) { - return new JavaTokenDocument(textDocument); - } - - @Override - protected CharStream newCharStream(JavaccTokenDocument tokenDocument) { - return new JavaCharStream(tokenDocument); + protected TokenDocumentBehavior tokenBehavior() { + return JavaTokenDocumentBehavior.INSTANCE; } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocument.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocument.java deleted file mode 100644 index 2254639caa..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocument.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.FORMAL_COMMENT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.GT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.MULTI_LINE_COMMENT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.RSIGNEDSHIFT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.RUNSIGNEDSHIFT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.SINGLE_LINE_COMMENT; -import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.WHITESPACE; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; -import net.sourceforge.pmd.lang.document.TextDocument; - -/** - * {@link JavaccTokenDocument} for Java. - */ -final class JavaTokenDocument extends JavaccTokenDocument { - - JavaTokenDocument(TextDocument fullText) { - super(fullText); - } - - /** - * Returns true if the given token is a Java comment. - */ - public static boolean isComment(JavaccToken t) { - switch (t.kind) { - case FORMAL_COMMENT: - case MULTI_LINE_COMMENT: - case SINGLE_LINE_COMMENT: - return true; - default: - return false; - } - } - - - @Override - protected @Nullable String describeKindImpl(int kind) { - return JavaTokenKinds.describe(kind); - } - - @Override - public JavaccToken createToken(int kind, CharStream jcs, @Nullable String image) { - switch (kind) { - case RUNSIGNEDSHIFT: - case RSIGNEDSHIFT: - case GT: - return new GTToken( - GT, - kind, - ">", - jcs.getStartOffset(), - jcs.getEndOffset(), - jcs.getTokenDocument() - ); - - case WHITESPACE: - // We don't create a new string for the image of whitespace tokens eagerly - - // It's unlikely that anybody cares about that, and since - // they're still 30% of all tokens this is advantageous - return new LazyImageToken( - kind, - jcs.getStartOffset(), - jcs.getEndOffset(), - jcs.getTokenDocument() - ); - - default: - return super.createToken(kind, jcs, image); - } - } - - static int getRealKind(JavaccToken token) { - return token instanceof GTToken ? ((GTToken) token).realKind : token.kind; - } - - private static final class LazyImageToken extends JavaccToken { - - LazyImageToken(int kind, int startInclusive, int endExclusive, JavaccTokenDocument document) { - super(kind, null, startInclusive, endExclusive, document); - } - - @Override - public String getImage() { - return getDocument().getTextDocument().sliceText(getRegion()).toString(); - } - } - - private static final class GTToken extends JavaccToken { - - final int realKind; - - GTToken(int kind, int realKind, CharSequence image, int startOffset, int endOffset, JavaccTokenDocument doc) { - super(kind, image, startOffset, endOffset, doc); - this.realKind = realKind; - } - - } - - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocumentBehavior.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocumentBehavior.java new file mode 100644 index 0000000000..c5b69c688a --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaTokenDocumentBehavior.java @@ -0,0 +1,72 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.GT; +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.RSIGNEDSHIFT; +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.RUNSIGNEDSHIFT; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaEscapeTranslator; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * {@link JavaccTokenDocument} for Java. + */ +final class JavaTokenDocumentBehavior extends JavaccTokenDocument.TokenDocumentBehavior { + + static final JavaTokenDocumentBehavior INSTANCE = new JavaTokenDocumentBehavior(); + + private JavaTokenDocumentBehavior() { + super(JavaTokenKinds.TOKEN_NAMES); + } + + + + @Override + public TextDocument translate(TextDocument text) throws MalformedSourceException { + return new JavaEscapeTranslator(text).translateDocument(); + } + + + @Override + public JavaccToken createToken(JavaccTokenDocument self, int kind, CharStream jcs, @Nullable String image) { + switch (kind) { + case RUNSIGNEDSHIFT: + case RSIGNEDSHIFT: + case GT: + return new GTToken( + GT, + kind, + ">", + jcs.getStartOffset(), + jcs.getEndOffset(), + jcs.getTokenDocument() + ); + default: + return super.createToken(self, kind, jcs, image); + } + } + + static int getRealKind(JavaccToken token) { + return token instanceof GTToken ? ((GTToken) token).realKind : token.kind; + } + + private static final class GTToken extends JavaccToken { + + final int realKind; + + GTToken(int kind, int realKind, String image, int startOffset, int endOffset, JavaccTokenDocument doc) { + super(kind, image, startOffset, endOffset, doc); + this.realKind = realKind; + } + + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TokenUtils.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TokenUtils.java index 430f0e102d..07b562fa83 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TokenUtils.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TokenUtils.java @@ -69,7 +69,7 @@ final class TokenUtils { */ // test only public static > T nthPrevious(T startHint, T anchor, int n) { - if (compare(startHint, anchor) >= 0) { + if (startHint.compareTo(anchor) >= 0) { throw new IllegalStateException("Wrong left hint, possibly not left enough"); } if (n <= 0) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/JavaAstUtils.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/JavaAstUtils.java index 36030e8252..465710b374 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/JavaAstUtils.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/JavaAstUtils.java @@ -4,6 +4,10 @@ package net.sourceforge.pmd.lang.java.ast.internal; +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.FORMAL_COMMENT; +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.MULTI_LINE_COMMENT; +import static net.sourceforge.pmd.lang.java.ast.JavaTokenKinds.SINGLE_LINE_COMMENT; + import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; @@ -711,4 +715,18 @@ public final class JavaAstUtils { } return false; } + + /** + * Returns true if the given token is a Java comment. + */ + public static boolean isComment(JavaccToken t) { + switch (t.kind) { + case FORMAL_COMMENT: + case MULTI_LINE_COMMENT: + case SINGLE_LINE_COMMENT: + return true; + default: + return false; + } + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index 166e515cb3..f230d40b2a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -5,9 +5,7 @@ package net.sourceforge.pmd.lang.java.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -21,10 +19,6 @@ import net.sourceforge.pmd.lang.rule.AbstractRule; */ public abstract class AbstractJavaRule extends AbstractRule implements JavaParserVisitor { - public AbstractJavaRule() { - super.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index b780b45a4c..3723278ed1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -88,8 +88,7 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRulechainRule { SortedSet occurrences = entry.getValue(); if (occurrences.size() >= threshold) { ASTStringLiteral first = occurrences.first(); - String rawImage = first.getImage(); - Object[] args = {rawImage, occurrences.size(), first.getBeginLine(), }; + Object[] args = { first.toPrintableString(), occurrences.size(), first.getBeginLine(), }; addViolation(data, first, args); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java index 148db33b6e..b7d85d5e05 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java @@ -12,7 +12,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTSwitchBranch; import net.sourceforge.pmd.lang.java.ast.ASTSwitchFallthroughBranch; import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.ast.JavaTokenKinds; +import net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass; import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.DataflowResult; @@ -55,9 +55,8 @@ public class ImplicitSwitchFallThroughRule extends AbstractJavaRulechainRule { return false; } for (JavaccToken special : GenericToken.previousSpecials(nextBranch.getFirstToken())) { - if ((JavaTokenKinds.SINGLE_LINE_COMMENT == special.kind - || JavaTokenKinds.MULTI_LINE_COMMENT == special.kind) - && IGNORED_COMMENT.matcher(special.getImage()).find()) { + if (JavaAstUtils.isComment(special) + && IGNORED_COMMENT.matcher(special.getImageCs()).find()) { return true; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java index 9da6aa61ae..4b190e8689 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java @@ -4,37 +4,39 @@ package net.sourceforge.pmd; -import static org.junit.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; import net.sourceforge.pmd.lang.java.JavaLanguageModule; -public class LanguageVersionDiscovererTest { +class LanguageVersionDiscovererTest { /** * Test on Java file with default options. * Always the latest non-preview version will be the default version. */ @Test - public void testJavaFileUsingDefaults() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); + void testJavaFileUsingDefaults() { + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD); File javaFile = new File("/path/to/MyClass.java"); LanguageVersion latest = determineLatestNonPreviewVersion(); LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(javaFile); - assertEquals("Latest language version must be default", latest, languageVersion); + assertEquals(latest, languageVersion, "Latest language version must be default"); } private LanguageVersion determineLatestNonPreviewVersion() { LanguageVersion latest = null; - for (LanguageVersion lv : LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersions()) { + for (LanguageVersion lv : JavaLanguageModule.getInstance().getVersions()) { if (!lv.getName().endsWith("preview")) { latest = lv; } @@ -46,27 +48,28 @@ public class LanguageVersionDiscovererTest { * Test on Java file with Java version set to 1.4. */ @Test - public void testJavaFileUsing14() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); - discoverer.setDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.4")); + void testJavaFileUsing14() { + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD); + Language java = JavaLanguageModule.getInstance(); + discoverer.setDefaultLanguageVersion(java.getVersion("1.4")); File javaFile = new File("/path/to/MyClass.java"); LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(javaFile); - assertEquals("LanguageVersion must be Java 1.4!", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.4"), languageVersion); + assertEquals(java.getVersion("1.4"), languageVersion); } @Test - public void testLanguageVersionDiscoverer() { + void testLanguageVersionDiscoverer() { PMDConfiguration configuration = new PMDConfiguration(); LanguageVersionDiscoverer languageVersionDiscoverer = configuration.getLanguageVersionDiscoverer(); - assertEquals("Default Java version", determineLatestNonPreviewVersion(), - languageVersionDiscoverer - .getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME))); + Language java = JavaLanguageModule.getInstance(); + assertEquals(determineLatestNonPreviewVersion(), + languageVersionDiscoverer.getDefaultLanguageVersion(java), + "Default Java version"); configuration - .setDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5")); - assertEquals("Modified Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"), - languageVersionDiscoverer - .getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME))); + .setDefaultLanguageVersion(java.getVersion("1.5")); + assertEquals(java.getVersion("1.5"), + languageVersionDiscoverer.getDefaultLanguageVersion(java), + "Modified Java version"); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 286f5bc959..993d4abd6d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,7 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.java.JavaLanguageModule; @@ -21,41 +21,42 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { + Language java = getLanguage(JavaLanguageModule.NAME); return Arrays.asList(new Object[][] { { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.3", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.3"), }, + java.getVersion("1.3"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.4", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.4"), }, + java.getVersion("1.4"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.5", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"), }, + java.getVersion("1.5"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.6", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.6"), }, + java.getVersion("1.6"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.7", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.7"), }, + java.getVersion("1.7"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.8", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.8"), }, + java.getVersion("1.8"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "9", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("9"), }, + java.getVersion("9"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "10", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"), }, + java.getVersion("10"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "11", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), }, + java.getVersion("11"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "12", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("12"), }, + java.getVersion("12"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "13", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("13"), }, + java.getVersion("13"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "14", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14"), }, + java.getVersion("14"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), }, + java.getVersion("15"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16"), }, + java.getVersion("16"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16-preview"), }, + java.getVersion("16-preview"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17"), }, + java.getVersion("17"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17-preview"), }, + java.getVersion("17-preview"), }, // this one won't be found: case sensitive! { "JAVA", "JAVA", "1.7", null, }, diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java index baa95be954..0e03f83cbe 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java @@ -10,8 +10,6 @@ import static org.junit.Assert.assertFalse; import org.junit.Test; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.JavaParsingHelper; public class ReportTest { @@ -45,7 +43,6 @@ public class ReportTest { @Test public void testExclusionsInReportWithAnnotations() { - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"); Report rpt = java.executeRule(new FooRule(), TEST2); assertSize(rpt, 0); @@ -54,7 +51,6 @@ public class ReportTest { @Test public void testExclusionsInReportWithAnnotationsFullName() { - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"); Report rpt = java.executeRule(new FooRule(), TEST2_FULL); assertSize(rpt, 0); assertSuppressed(rpt, 1); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java index fb66b5c51d..da1ca356c1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -21,6 +21,8 @@ import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; * @author Romain Pelisse <belaran@gmail.com> */ public class CLITest extends BaseCLITest { + // note that the progress bar sometimes messes up the log so it is + // disabled here in most tests. // restoring system properties: -debug might change logging properties // See Slf4jSimpleConfigurationForAnt and resetLogging @@ -63,7 +65,7 @@ public class CLITest extends BaseCLITest { @Test public void changeJavaVersion() { - String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_NO_VIOLATION, "-version", "1.5", "-language", "java", "--debug" }; + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_NO_VIOLATION, "-version", "1.5", "-language", "java", "--debug", "--no-progress", }; String log = runTest(args); assertThat(log, containsPattern("Adding file .*\\.java \\(lang: java 1\\.5\\)")); } @@ -75,21 +77,21 @@ public class CLITest extends BaseCLITest { @Test public void exitStatusWithViolations() { - String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, }; + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, "--no-progress", }; String log = runTest(StatusCode.VIOLATIONS_FOUND, args); assertThat(log, containsString("Violation from test-rset-1.xml")); } @Test public void exitStatusWithViolationsAndWithoutFailOnViolations() { - String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, "-failOnViolation", "false", }; + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, "-failOnViolation", "false", "--no-progress", }; String log = runTest(StatusCode.OK, args); assertThat(log, containsString("Violation from test-rset-1.xml")); } @Test public void exitStatusWithViolationsAndWithoutFailOnViolationsLongOption() { - String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, "--fail-on-violation", "false", }; + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", RSET_WITH_VIOLATION, "--fail-on-violation", "false", "--no-progress", }; String log = runTest(StatusCode.OK, args); assertThat(log, containsString("Violation from test-rset-1.xml")); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java index c7cab54408..992d008467 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java @@ -23,7 +23,6 @@ import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.util.IOUtil; @@ -52,7 +51,7 @@ public class PMDCoverageTest { @Test public void runAllJavaPmdOnTestResourcesWithLatestJavaVersion() { - List versions = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersions(); + List versions = JavaLanguageModule.getInstance().getVersions(); LanguageVersion latest = versions.get(versions.size() - 1); runPmd("-d src/test/resources -f text -R rulesets/internal/all-java.xml -language java -version " + latest.getVersion()); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 3d7c2f8b54..0da64dd11f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -91,7 +91,8 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { public void testBrokenAndValidFile() { String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--format", "text", "--skip-lexical-errors"); - assertThat(out, containsPattern("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file")); + String stderr = getStderr(); + assertThat(stderr, containsPattern("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file")); assertThat(out, containsString("Found a 5 line (13 tokens) duplication")); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java index 3dcafd789f..f9a4f675e7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java @@ -9,38 +9,35 @@ import java.util.List; import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; public class JavaLanguageModuleTest { - private Language javaLanguage = LanguageRegistry.getLanguage(JavaLanguageModule.NAME); @Test public void java9IsSmallerThanJava10() { - LanguageVersion java9 = javaLanguage.getVersion("9"); - LanguageVersion java10 = javaLanguage.getVersion("10"); + LanguageVersion java9 = JavaLanguageModule.getInstance().getVersion("9"); + LanguageVersion java10 = JavaLanguageModule.getInstance().getVersion("10"); Assert.assertTrue("java9 should be smaller than java10", java9.compareTo(java10) < 0); } @Test public void previewVersionShouldBeGreaterThanNonPreview() { - LanguageVersion java18 = javaLanguage.getVersion("18"); - LanguageVersion java18p = javaLanguage.getVersion("18-preview"); + LanguageVersion java18 = JavaLanguageModule.getInstance().getVersion("18"); + LanguageVersion java18p = JavaLanguageModule.getInstance().getVersion("18-preview"); Assert.assertTrue("java18-preview should be greater than java18", java18p.compareTo(java18) > 0); } @Test public void testCompareToVersion() { - LanguageVersion java9 = javaLanguage.getVersion("9"); + LanguageVersion java9 = JavaLanguageModule.getInstance().getVersion("9"); Assert.assertTrue("java9 should be smaller than java10", java9.compareToVersion("10") < 0); } @Test public void allVersions() { - List versions = javaLanguage.getVersions(); + List versions = JavaLanguageModule.getInstance().getVersions(); for (int i = 1; i < versions.size(); i++) { LanguageVersion previous = versions.get(i - 1); LanguageVersion current = versions.get(i); 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 684662f3fd..c073a66b47 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 @@ -16,7 +16,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.java.BaseJavaTreeDumpTest; import net.sourceforge.pmd.lang.java.JavaParsingHelper; @@ -43,8 +43,8 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest { @Test public void testInvalidUnicodeEscape() { - expect.expect(TokenMgrError.class); // previously Error - expect.expectMessage("Lexical error in file x/filename.java at line 1, column 2. Encountered: Invalid unicode escape"); + expect.expect(MalformedSourceException.class); // previously Error + expect.expectMessage("Source format error in file 'x/filename.java' at line 1, column 1: Invalid unicode escape"); java.parse("\\u00k0", null, "x/filename.java"); } @@ -88,6 +88,45 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest { + "}"); } + @Test + public void testUnicodeEscapes() { + // todo i'd like to test the coordinates of the literals, but this has to wait for java-grammar to be merged + java8.parse("public class Foo { String[] s = { \"Ven\\u00E4j\\u00E4\" }; }"); + } + + @Test + public void testUnicodeEscapes2() { + java.parse("\n" + + "public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle {\n" + + "\n" + + " String ACT[] = new String[] {\"Acre \\u6642\\u9593\", \"ACT\",\n" + + " \"Acre \\u590f\\u4ee4\\u6642\\u9593\", \"ACST\",\n" + + " \"Acre \\u6642\\u9593\", \"ACT\"};" + + "}"); + } + + @Test + public void testUnicodeEscapesInComment() { + java.parse("class Foo {" + + "\n" + + " /**\n" + + " * The constant value of this field is the smallest value of type\n" + + " * {@code char}, {@code '\\u005Cu0000'}.\n" + + " *\n" + + " * @since 1.0.2\n" + + " */\n" + + " public static final char MIN_VALUE = '\\u0000';\n" + + "\n" + + " /**\n" + + " * The constant value of this field is the largest value of type\n" + + " * {@code char}, {@code '\\u005C\\uFFFF'}.\n" + + " *\n" + + " * @since 1.0.2\n" + + " */\n" + + " public static final char MAX_VALUE = '\\uFFFF';" + + "}"); + } + @Test public final void testGetFirstASTNameImageNull() { java4.parse("public class Test {\n" diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java index 1fb74b3099..1a40b50605 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java @@ -7,11 +7,11 @@ package net.sourceforge.pmd.lang.java.ast; import static net.sourceforge.pmd.lang.java.ast.ASTStringLiteral.determineTextBlockContent; import static org.junit.Assert.assertEquals; -import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.Test; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.java.BaseParserTest; +import net.sourceforge.pmd.util.StringUtil; public class TextBlockEscapeTest extends BaseParserTest { @@ -42,15 +42,11 @@ public class TextBlockEscapeTest extends BaseParserTest { // note the argument order private void testStringEscape(String actual, String expected) { - actual = inDoubleQuotes(actual); + actual = StringUtil.inDoubleQuotes(actual); assertEquals(expected, ASTStringLiteral.determineStringContent(Chars.wrap(actual))); } - private static @NonNull String inDoubleQuotes(String expected) { - return "\"" + expected + "\""; - } - @Test public void testTextBlockContent() { assertEquals("single line text block", "winter", diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java index ef00d1de9d..26d645eb60 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java @@ -16,9 +16,7 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.JavaParsingHelper; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.JavaNode; @@ -29,22 +27,18 @@ import net.sourceforge.pmd.lang.rule.xpath.internal.DeprecatedAttrLogger; import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.testframework.RuleTst; /** * @author daniels */ -public class XPathRuleTest extends RuleTst { +public class XPathRuleTest { private XPathRule makeXPath(String expression) { - XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, expression); - rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); - rule.setMessage("XPath Rule Failed"); - return rule; + return JavaParsingHelper.DEFAULT.newXpathRule(expression); } @Test - public void testPluginname() throws Exception { + public void testPluginname() { XPathRule rule = makeXPath("//VariableDeclaratorId[string-length(@Name) < 3]"); rule.setMessage("{0}"); Report report = getReportForTestString(rule, TEST1); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryimport/package2/C.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryimport/package2/C.java index 19710af874..92104cbbb1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryimport/package2/C.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryimport/package2/C.java @@ -7,5 +7,9 @@ package net.sourceforge.pmd.lang.java.rule.codestyle.unnecessaryimport.package2; public class C { private C() { } + public class IC { } + + public static class ISC { } + public static final String V = ""; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index b0e8901217..69c06cc770 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -15,7 +15,6 @@ import org.junit.Assert; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.BaseParserTest; @@ -42,7 +41,7 @@ public class BaseXPathFunctionTest extends BaseParserTest { XPathRule rule = new XPathRule(XPathVersion.DEFAULT, xpath); rule.setName("$rule_name"); rule.setMessage(VIOLATION_MESSAGE); - rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); + rule.setLanguage(JavaLanguageModule.getInstance()); return rule; } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt index dfe6acc411..cdaae0c7d7 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.ast +import io.kotest.matchers.shouldBe import net.sourceforge.pmd.lang.ast.test.NodeSpec import net.sourceforge.pmd.lang.ast.test.ValuedNodeSpec import net.sourceforge.pmd.lang.ast.test.shouldBe @@ -166,12 +167,12 @@ $delim } } - // todo fixed in followup branch - // "\"abc\\u1234abc\"" should parseAs { - // stringLit("\"abc\\u1234abc\"") { - // it::getConstValue shouldBe "abc\u1234abc" - // } - // } + "\"abc\\u1234abc\"" should parseAs { + stringLit("\"abc\u1234abc\"") { + it::getConstValue shouldBe "abc\u1234abc" + it.originalText.toString() shouldBe "\"abc\\u1234abc\"" + } + } "\"abcüabc\"" should parseAs { stringLit("\"abcüabc\"") { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaUnicodeEscapesTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaUnicodeEscapesTest.kt new file mode 100644 index 0000000000..b84c3ba443 --- /dev/null +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaUnicodeEscapesTest.kt @@ -0,0 +1,58 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.string.shouldContain +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException +import net.sourceforge.pmd.lang.ast.test.IntelliMarker +import net.sourceforge.pmd.lang.ast.test.shouldBeA +import net.sourceforge.pmd.lang.document.TextDocument +import net.sourceforge.pmd.lang.java.JavaParsingHelper + +fun makeJavaTranslatedDocument( + code: String, +): TextDocument { + val base = TextDocument.readOnlyString( + code, + JavaParsingHelper.DEFAULT.defaultVersion + ) + return InternalApiBridge.javaTokenDoc().translate(base) +} + +class JavaUnicodeEscapesTest : IntelliMarker, FunSpec({ + + test("Test java invalid unicode escapes") { + + val comment = """\u002F\u0k2a\u002a\u002a\u002F""" + + val exception = shouldThrow { + makeJavaTranslatedDocument(comment) + } + + exception.message!!.shouldContain(Regex("line \\d+, column \\d+")) + exception.message!!.shouldContain("\\u0k2a") + + exception.cause!!.shouldBeA { + it.message!!.shouldContain("valid hexadecimal digit") + } + } + + test("Test incomplete unicode escape ") { + + val comment = """\u00""" + + val mse = shouldThrow { + makeJavaTranslatedDocument(comment) + } + mse.message!!.shouldContain(Regex("line \\d+, column \\d+")) + mse.message!!.shouldContain("\\u00") + mse.cause!!.shouldBeA() + } + +}) + + diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt index 08ef90f37f..766fe93695 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt @@ -36,7 +36,7 @@ enum class JavaVersion : Comparable { /** Name suitable for use with e.g. [JavaParsingHelper.parse] */ val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase() - val pmdVersion get() = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(pmdName) + val pmdVersion get() = JavaLanguageModule.getInstance().getVersion(pmdName) val parser: JavaParsingHelper = DEFAULT.withDefaultVersion(pmdName) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt index ae12f41d99..ae1a52f0b2 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt @@ -8,9 +8,9 @@ import com.github.oowekyala.treeutils.matchers.TreeNodeWrapper import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult import io.kotest.matchers.collections.shouldBeEmpty -import io.kotest.matchers.types.shouldBeInstanceOf import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.types.shouldBeInstanceOf import net.sourceforge.pmd.internal.util.IteratorUtil import net.sourceforge.pmd.lang.ast.Node import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidReassigningLoopVariables.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidReassigningLoopVariables.xml index d002c076ef..8180b410be 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidReassigningLoopVariables.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidReassigningLoopVariables.xml @@ -674,7 +674,7 @@ public class Foo { - + violation: various conditional reassignments of 'for' loop variable, skip allowed skip 4 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PreserveStackTrace.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PreserveStackTrace.xml index a20b8531d1..5292c61d64 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PreserveStackTrace.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PreserveStackTrace.xml @@ -738,7 +738,7 @@ public class SomeOtherUserDefinedException extends Exception { } ]]> - + FP with reassigned exception 1 @@ -755,7 +755,7 @@ public class SomeOtherUserDefinedException extends Exception { } ]]> - + FP with reassigned exception (branch) 1 @@ -1080,7 +1080,7 @@ public class SomeOtherUserDefinedException extends Exception { ]]> - + Branch in dataflow should be merged 2 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml index 7f4341aa5e..d9aad8af15 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml @@ -3289,7 +3289,7 @@ public class UnusedAssignmentNative { ]]> - + Explicit this ctor call 1 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml index 85f6c7871c..e5a1609bbb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml @@ -883,7 +883,7 @@ class Assert { } ]]> - + FN with static method imported through subtype (same file) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryImport.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryImport.xml index 1d5e88a07e..6ed1442107 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryImport.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryImport.xml @@ -158,7 +158,7 @@ public class Foo { ]]> - + Used static import 0 @@ -1066,6 +1066,33 @@ public class SubmissionPublisher implements Publisher { } ]]> + + [java] UnnecessaryImport false positive for on-demand imports of non-static nested classes + 0 + + + + + [java] UnnecessaryImport false positive for static on-demand imports of static nested classes + 0 + + Necessary imports for @snippet tags introduced with JEP 413 in Java 18 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml index 09a62b6d1b..e220540880 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml @@ -365,7 +365,7 @@ public class Foo { ]]> - + Ignore local class statements 1 - + #780 [java] BeanMembersShouldSerializeRule does not recognize lombok accessors - 3 0 data() { return Arrays.asList(new Object[][] { { EcmascriptLanguageModule.NAME, EcmascriptLanguageModule.TERSE_NAME, "ES6", - LanguageRegistry.getLanguage(EcmascriptLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(EcmascriptLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java index d0c036549b..463b396604 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -24,8 +24,7 @@ public class CLITest extends BaseCLITest { "xml", "-R", "rulesets/testing/js-rset1.xml", - "-l", - "ecmascript", + "--no-progress", "--debug"); assertThat(log, containsPattern("Adding file .*\\.js \\(lang: ecmascript ES6\\)")); } diff --git a/pmd-jsp/etc/grammar/Jsp.jjt b/pmd-jsp/etc/grammar/Jsp.jjt index c5400e8b6c..9e4483a441 100644 --- a/pmd-jsp/etc/grammar/Jsp.jjt +++ b/pmd-jsp/etc/grammar/Jsp.jjt @@ -30,7 +30,7 @@ options { PARSER_BEGIN(JspParserImpl) package net.sourceforge.pmd.lang.jsp.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java index 58fe488223..5617484d1b 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java @@ -6,10 +6,10 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStreamFactory; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.jsp.ast.JspParser; import net.sourceforge.pmd.lang.jsp.ast.JspTokenKinds; public class JSPTokenizer extends JavaCCTokenizer { @@ -20,7 +20,8 @@ public class JSPTokenizer extends JavaCCTokenizer { } @Override - protected CharStream makeCharStream(TextDocument sourceCode) { - return CharStreamFactory.javaCharStream(sourceCode); + protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { + return JspParser.getTokenBehavior(); } + } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/ast/JspParser.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/ast/JspParser.java index 6df68a9ad2..f508b21bc5 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/ast/JspParser.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/ast/JspParser.java @@ -4,27 +4,22 @@ package net.sourceforge.pmd.lang.jsp.ast; -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.document.TextDocument; /** * JSP language parser. */ public final class JspParser extends JjtreeParserAdapter { + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(JspTokenKinds.TOKEN_NAMES); + @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument fullText) { - return new JavaccTokenDocument(fullText) { - @Override - protected @Nullable String describeKindImpl(int kind) { - return JspTokenKinds.describe(kind); - } - }; + protected TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override @@ -32,4 +27,8 @@ public final class JspParser extends JjtreeParserAdapter { return new JspParserImpl(cs).CompilationUnit().makeTaskInfo(task); } + @InternalApi + public static TokenDocumentBehavior getTokenBehavior() { + return TOKEN_BEHAVIOR; + } } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java index 09aa1d202c..ffe3a7b4cc 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java @@ -5,18 +5,12 @@ package net.sourceforge.pmd.lang.jsp.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.jsp.JspLanguageModule; import net.sourceforge.pmd.lang.jsp.ast.JspParserVisitor; import net.sourceforge.pmd.lang.rule.AbstractRule; public abstract class AbstractJspRule extends AbstractRule implements JspParserVisitor { - public AbstractJspRule() { - super.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME)); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java index 54ac07edf0..4f2e0a60bb 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java @@ -1,31 +1,49 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.lang.jsp.JspLanguageModule; +import net.sourceforge.pmd.lang.jsp.ast.AbstractJspNodesTst; -class LanguageVersionDiscovererTest { +class LanguageVersionDiscovererTest extends AbstractJspNodesTst { - /** - * Test on JSP file. - */ @Test - void testJspFile() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); - File jspFile = new File("/path/to/MyPage.jsp"); - LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(jspFile); - assertEquals(LanguageRegistry.getLanguage(JspLanguageModule.NAME).getDefaultVersion(), languageVersion, - "LanguageVersion must be JSP!"); + void testParseJsp() { + testLanguageIsJsp("sample.jsp"); + testLanguageIsJsp("sample.jspx"); + } + + @Test + void testTag() { + testLanguageIsJsp("sample.tag"); + } + + + private void testLanguageIsJsp(String first) { + assertEquals(jsp.getLanguage().getDefaultVersion(), + getLanguageVersion(Paths.get(first))); + } + + @Test + void testParseWrong() { + assertNotEquals(jsp.getLanguage().getDefaultVersion(), + getLanguageVersion(Paths.get("sample.xxx"))); + } + + private LanguageVersion getLanguageVersion(Path jspFile) { + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD); + return discoverer.getDefaultLanguageVersionForFile(jspFile.toFile()); } } diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 6a3537c5b7..d146252127 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.jsp.JspLanguageModule; @@ -22,6 +21,6 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { return Arrays.asList(new Object[][] { { JspLanguageModule.NAME, JspLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(JspLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(JspLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java index da947ad0d5..fb0367c940 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java @@ -4,17 +4,8 @@ package net.sourceforge.pmd.lang.jsp; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.io.File; -import java.nio.file.Paths; - -import org.junit.Assert; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; import net.sourceforge.pmd.lang.jsp.ast.AbstractJspNodesTst; /** @@ -48,28 +39,4 @@ class JspParserTest extends AbstractJspNodesTst { void testParseBooleanAttribute() { jsp.parse(""); } - - @Test - void testParseJsp() { - testInternalJspFile(Paths.get("sample.jsp").toFile()); - testInternalJspFile(Paths.get("sample.jspx").toFile()); - } - - @Test - void testParseTag() { - testInternalJspFile(Paths.get("sample.tag").toFile()); - } - - @Test - void testParseWrong() { - assertThrows(AssertionError.class, () -> testInternalJspFile(Paths.get("sample.xxx").toFile())); - } - - private void testInternalJspFile(File jspFile) { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); - LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(jspFile); - Assert.assertEquals("LanguageVersion must be JSP!", - LanguageRegistry.getLanguage(JspLanguageModule.NAME).getDefaultVersion(), languageVersion); - } - } diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java index 15c736cb7e..0fed9e17ae 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java @@ -11,24 +11,16 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.jsp.JspLanguageModule; -import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; -import net.sourceforge.pmd.testframework.RuleTst; -class XPathJspRuleTest extends RuleTst { +class XPathJspRuleTest extends AbstractJspNodesTst { /** * Test matching a XPath expression against a JSP source. */ @Test void testExpressionMatching() { - Rule rule = new XPathRule(XPathVersion.XPATH_3_1, XPATH_EXPRESSION); - rule.setMessage("Test"); - rule.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME)); - - Report report = JspParsingHelper.DEFAULT.executeRule(rule, MATCH); + Rule rule = jsp.newXpathRule("//Element [@Name='hr']"); + Report report = jsp.executeRule(rule, "


"); assertEquals(1, report.getViolations().size(), "One violation expected!"); @@ -36,7 +28,4 @@ class XPathJspRuleTest extends RuleTst { assertEquals(1, rv.getBeginLine()); } - private static final String MATCH = "
"; - - private static final String XPATH_EXPRESSION = "//Element [@Name='hr']"; } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index a2201ae659..f3914a0e8b 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -9,10 +9,12 @@ import net.sourceforge.pmd.lang.LanguageRegistry import net.sourceforge.pmd.lang.LanguageVersion import net.sourceforge.pmd.lang.LanguageVersionHandler import net.sourceforge.pmd.lang.ast.* -import net.sourceforge.pmd.processor.AbstractPMDProcessor -import net.sourceforge.pmd.reporting.GlobalAnalysisListener import net.sourceforge.pmd.lang.document.TextDocument import net.sourceforge.pmd.lang.document.TextFile +import net.sourceforge.pmd.lang.rule.XPathRule +import net.sourceforge.pmd.lang.rule.xpath.XPathVersion +import net.sourceforge.pmd.processor.AbstractPMDProcessor +import net.sourceforge.pmd.reporting.GlobalAnalysisListener import net.sourceforge.pmd.util.IOUtil import java.io.InputStream import java.nio.charset.StandardCharsets @@ -30,11 +32,12 @@ abstract class BaseParsingHelper, T : RootNode ) { data class Params( - val doProcess: Boolean, - val defaultVerString: String?, - val resourceLoader: Class<*>?, - val resourcePrefix: String, - val suppressMarker: String = PMD.SUPPRESS_MARKER, + val doProcess: Boolean, + val defaultVerString: String?, + val resourceLoader: Class<*>?, + val resourcePrefix: String, + val languageRegistry: LanguageRegistry = LanguageRegistry.PMD, + val suppressMarker: String = PMD.SUPPRESS_MARKER, ) { companion object { @@ -63,8 +66,13 @@ abstract class BaseParsingHelper, T : RootNode } val language: Language - get() = LanguageRegistry.getLanguage(langName) - ?: throw AssertionError("'$langName' is not a supported language (available ${LanguageRegistry.getLanguages()})") + get() = + params.languageRegistry.getLanguageByFullName(langName) + ?: run { + val langNames = params.languageRegistry.commaSeparatedList { it.name } + throw AssertionError("'$langName' is not a supported language (available $langNames)") + } + val defaultVersion: LanguageVersion get() = getVersion(params.defaultVerString) @@ -74,7 +82,7 @@ abstract class BaseParsingHelper, T : RootNode @JvmOverloads fun withProcessing(doProcess: Boolean = true): Self = - clone(params.copy(doProcess = doProcess)) + clone(params.copy(doProcess = doProcess)) /** * Returns an instance of [Self] for which all parsing methods @@ -83,7 +91,7 @@ abstract class BaseParsingHelper, T : RootNode * defined by the language module is used instead. */ fun withDefaultVersion(version: String?): Self = - clone(params.copy(defaultVerString = version)) + clone(params.copy(defaultVerString = version)) /** * Returns an instance of [Self] for which [parseResource] uses @@ -94,6 +102,10 @@ abstract class BaseParsingHelper, T : RootNode clone(params.copy(resourceLoader = contextClass, resourcePrefix = resourcePrefix)) + fun withLanguageRegistry(languageRegistry: LanguageRegistry): Self = + clone(params.copy(languageRegistry = languageRegistry)) + + fun withSuppressMarker(marker: String): Self = clone(params.copy(suppressMarker = marker)) @@ -193,6 +205,12 @@ abstract class BaseParsingHelper, T : RootNode return consume(input) } + @JvmOverloads + fun newXpathRule(expr: String, version: XPathVersion = XPathVersion.DEFAULT) = + XPathRule(version, expr).apply { + language = this@BaseParsingHelper.language + message = "XPath Rule Failed" + } /** * Execute the given [rule] on the [code]. Produce a report with the violations @@ -204,6 +222,8 @@ abstract class BaseParsingHelper, T : RootNode code: String, fileName: String = "testfile.${language.extensions[0]}" ): Report { + if (rule.language == null) + rule.language = language val config = PMDConfiguration().apply { suppressMarker = params.suppressMarker setDefaultLanguageVersion(defaultVersion) diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt index a0a0ac071e..2a132ce149 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt @@ -24,7 +24,7 @@ inline fun Node.getDescendantsOfType(): List = descendants inline fun Node.getFirstDescendantOfType(): T = descendants(T::class.java).firstOrThrow() fun Node.textOfReportLocation(): String? = - reportLocation.regionInFile?.let(textDocument::sliceText)?.toString() + reportLocation.regionInFile?.let(textDocument::sliceOriginalText)?.toString() fun Node.assertTextRangeIsOk() { diff --git a/pmd-matlab/etc/grammar/Matlab.jj b/pmd-matlab/etc/grammar/Matlab.jj index 891a80f886..3ff1c8b27c 100644 --- a/pmd-matlab/etc/grammar/Matlab.jj +++ b/pmd-matlab/etc/grammar/Matlab.jj @@ -21,7 +21,7 @@ options { PARSER_BEGIN(MatlabParserImpl) package net.sourceforge.pmd.lang.matlab.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; public class MatlabParserImpl { diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java index 3273203072..9459c44696 100644 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.matlab.ast.MatlabTokenKinds; diff --git a/pmd-modelica/etc/grammar/Modelica.jjt b/pmd-modelica/etc/grammar/Modelica.jjt index 67039e9d1b..e2e7d63121 100644 --- a/pmd-modelica/etc/grammar/Modelica.jjt +++ b/pmd-modelica/etc/grammar/Modelica.jjt @@ -49,7 +49,7 @@ options { PARSER_BEGIN(ModelicaParserImpl) package net.sourceforge.pmd.lang.modelica.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; class ModelicaParserImpl { diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java index 71f1bb930f..3258a3cda7 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.modelica.ast.ModelicaTokenKinds; diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java index 30bc612edf..6e1ac343aa 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java @@ -5,19 +5,20 @@ package net.sourceforge.pmd.lang.modelica.ast; import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.modelica.resolver.ModelicaSymbolFacade; public class ModelicaParser extends JjtreeParserAdapter { + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(ModelicaTokenKinds.TOKEN_NAMES); + @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument textDocument) { - return new ModelicaTokenDocument(textDocument); + protected TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaTokenDocument.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaTokenDocument.java deleted file mode 100644 index caf8c1b7cb..0000000000 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaTokenDocument.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.modelica.ast; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; -import net.sourceforge.pmd.lang.document.TextDocument; - - -public class ModelicaTokenDocument extends JavaccTokenDocument { - - public ModelicaTokenDocument(TextDocument textDocument) { - super(textDocument); - } - - @Override - protected @Nullable String describeKindImpl(int kind) { - return ModelicaTokenKinds.describe(kind); - } -} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java index 5950b7fe9c..900cf1eea7 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java @@ -5,9 +5,7 @@ package net.sourceforge.pmd.lang.modelica.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitor; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -15,9 +13,6 @@ import net.sourceforge.pmd.lang.rule.AbstractRule; * Base class for rules for Modelica language. */ public abstract class AbstractModelicaRule extends AbstractRule implements ModelicaParserVisitor { - public AbstractModelicaRule() { - super.setLanguage(LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME)); - } @Override public void apply(Node target, RuleContext ctx) { diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 48e697c301..075ff57939 100644 --- a/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; @@ -22,7 +21,7 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { public static Collection data() { return Arrays.asList(new Object[][] { { ModelicaLanguageModule.NAME, ModelicaLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME).getDefaultVersion(), + getLanguage(ModelicaLanguageModule.NAME).getDefaultVersion(), }, }); } diff --git a/pmd-objectivec/etc/grammar/ObjectiveC.jj b/pmd-objectivec/etc/grammar/ObjectiveC.jj index e42313b6c2..171ca581fa 100644 --- a/pmd-objectivec/etc/grammar/ObjectiveC.jj +++ b/pmd-objectivec/etc/grammar/ObjectiveC.jj @@ -21,7 +21,7 @@ package net.sourceforge.pmd.lang.objectivec.ast; import java.io.*; import java.util.*; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; /** diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java index e101af905d..acccfcd24a 100644 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.objectivec.ast.ObjectiveCTokenKinds; diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index 07c2e7e3dd..41fbefea0c 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -159,6 +159,9 @@ package net.sourceforge.pmd.lang.plsql.ast; import java.util.List; import java.util.ArrayList; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.plsql.ast.internal.ParsingExclusion; import net.sourceforge.pmd.lang.ast.TokenMgrError; @@ -5306,6 +5309,7 @@ TOKEN : MORE : { <~["'"]> | <"'"> { + Chars image = input_stream.getTokenImageCs(); int quoteDelimiter = image.charAt(2); if (image.charAt(0) == 'n' || image.charAt(0) == 'N') { quoteDelimiter = image.charAt(3); @@ -5313,7 +5317,6 @@ TOKEN : int beforeQuote = image.charAt(image.length() - 2); if (quoteDelimiter == beforeQuote) { input_stream.backup(1); - image.setLength(image.length() - 1); SwitchTo(IN_STRING_LITERAL_TOKENIZE); } } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java index f302bc540e..77abbf8794 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java @@ -8,7 +8,7 @@ import java.util.Properties; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.plsql.ast.PLSQLTokenKinds; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java index 30ad481fc5..ac50bdc6a3 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java @@ -4,26 +4,20 @@ package net.sourceforge.pmd.lang.plsql.ast; -import org.checkerframework.checker.nullness.qual.Nullable; - import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.plsql.symboltable.SymbolFacade; public class PLSQLParser extends JjtreeParserAdapter { + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(PLSQLTokenKinds.TOKEN_NAMES); + @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument fullText) { - return new JavaccTokenDocument(fullText) { - @Override - protected @Nullable String describeKindImpl(int kind) { - return PLSQLTokenKinds.describe(kind); - } - }; + protected TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java index 97e226c28e..afa94e1f6c 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java @@ -6,9 +6,7 @@ package net.sourceforge.pmd.lang.plsql.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.plsql.PLSQLLanguageModule; import net.sourceforge.pmd.lang.plsql.ast.ASTInput; import net.sourceforge.pmd.lang.plsql.ast.ASTPackageBody; import net.sourceforge.pmd.lang.plsql.ast.ASTPackageSpecification; @@ -21,9 +19,6 @@ import net.sourceforge.pmd.lang.rule.AbstractRule; public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLParserVisitor { - public AbstractPLSQLRule() { - super.setLanguage(LanguageRegistry.getLanguage(PLSQLLanguageModule.NAME)); - } @Override public void apply(Node target, RuleContext ctx) { diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java index 8adf75d708..68dbb37432 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java @@ -13,20 +13,20 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.lang.plsql.PLSQLLanguageModule; +import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst; -class LanguageVersionDiscovererTest { +class LanguageVersionDiscovererTest extends AbstractPLSQLParserTst { /** * Test on PLSQL file with default version */ @Test void testPlsql() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD); File plsqlFile = new File("/path/to/MY_PACKAGE.sql"); LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(plsqlFile); - assertEquals(LanguageRegistry.getLanguage(PLSQLLanguageModule.NAME).getDefaultVersion(), languageVersion, + assertEquals(plsql.getLanguage().getDefaultVersion(), languageVersion, "LanguageVersion must be PLSQL!"); } } diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index cbaeb0a0a1..a2952609ec 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.plsql.PLSQLLanguageModule; @@ -22,6 +21,6 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { return Arrays.asList(new Object[][] { { PLSQLLanguageModule.NAME, PLSQLLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(PLSQLLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(PLSQLLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PLSQLXPathRuleTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PLSQLXPathRuleTest.java index fbfce7df24..09f9013008 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PLSQLXPathRuleTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PLSQLXPathRuleTest.java @@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; @@ -49,10 +48,7 @@ class PLSQLXPathRuleTest extends AbstractPLSQLParserTst { private void testOnVersion(XPathVersion xpath10) { - XPathRule rule = new XPathRule(xpath10, "//PrimaryPrefix"); - rule.setLanguage(LanguageRegistry.getLanguage(PLSQLLanguageModule.NAME)); - rule.setMessage("Test Violation"); - + XPathRule rule = plsql.newXpathRule("//PrimaryPrefix", xpath10); Report report = plsql.executeRule(rule, SOURCE); assertEquals(2, report.getViolations().size()); } diff --git a/pmd-python/etc/grammar/Python.jj b/pmd-python/etc/grammar/Python.jj index d1dd62cb0c..d68de80c23 100644 --- a/pmd-python/etc/grammar/Python.jj +++ b/pmd-python/etc/grammar/Python.jj @@ -17,7 +17,7 @@ PARSER_BEGIN(PythonParserImpl) package net.sourceforge.pmd.lang.python.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; public class PythonParserImpl { diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java b/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java index 99c3884ae8..c80d572f67 100644 --- a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java +++ b/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java @@ -6,15 +6,12 @@ package net.sourceforge.pmd.cpd; import java.util.regex.Pattern; -import org.checkerframework.checker.nullness.qual.Nullable; - import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStreamFactory; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; -import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.python.ast.PythonTokenKinds; /** @@ -24,27 +21,16 @@ public class PythonTokenizer extends JavaCCTokenizer { private static final Pattern STRING_NL_ESCAPE = Pattern.compile("\\\\\\r?\\n"); + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(PythonTokenKinds.TOKEN_NAMES); + @Override protected TokenManager makeLexerImpl(CharStream sourceCode) { return PythonTokenKinds.newTokenManager(sourceCode); } @Override - protected CharStream makeCharStream(TextDocument sourceCode) { - return CharStreamFactory.simpleCharStream(sourceCode, PythonTokenDocument::new); - } - - private static class PythonTokenDocument extends JavaccTokenDocument { - - PythonTokenDocument(TextDocument fullText) { - super(fullText); - } - - @Override - protected @Nullable String describeKindImpl(int kind) { - return PythonTokenKinds.describe(kind); - } - + protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java index 06853bbe71..3609cd8efb 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -44,7 +44,7 @@ public class ScalaTokenAdapter implements GenericToken { @Override public Chars getImageCs() { - return textDocument.sliceText(getRegion()); + return textDocument.sliceTranslatedText(getRegion()); } @Override diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index ef191cd85b..77f70b5236 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -10,7 +10,6 @@ import java.util.Properties; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.TokenMgrError; @@ -56,9 +55,9 @@ public class ScalaTokenizer implements Tokenizer { String scalaVersion = properties.getProperty(SCALA_VERSION_PROPERTY); LanguageVersion langVer; if (scalaVersion == null) { - langVer = LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getDefaultVersion(); + langVer = ScalaLanguageModule.getInstance().getDefaultVersion(); } else { - langVer = LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getVersion(scalaVersion); + langVer = ScalaLanguageModule.getInstance().getVersion(scalaVersion); } dialect = ((ScalaLanguageHandler) langVer.getLanguageVersionHandler()).getDialect(); } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java index a121da801d..6441aa7af5 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.scala; import net.sourceforge.pmd.lang.BaseLanguageModule; +import net.sourceforge.pmd.lang.LanguageRegistry; /** * Language Module for Scala. @@ -27,4 +28,8 @@ public class ScalaLanguageModule extends BaseLanguageModule { addVersion("2.11", new ScalaLanguageHandler(scala.meta.dialects.package$.MODULE$.Scala211()), false); addVersion("2.10", new ScalaLanguageHandler(scala.meta.dialects.package$.MODULE$.Scala210()), false); } + + public static ScalaLanguageModule getInstance() { + return (ScalaLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); + } } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/rule/ScalaRule.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/rule/ScalaRule.java index 972bd80ccb..a385df3465 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/rule/ScalaRule.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/rule/ScalaRule.java @@ -5,10 +5,8 @@ package net.sourceforge.pmd.lang.scala.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; import net.sourceforge.pmd.lang.scala.ast.ScalaNode; import net.sourceforge.pmd.lang.scala.ast.ScalaParserVisitor; @@ -18,14 +16,6 @@ import net.sourceforge.pmd.lang.scala.ast.ScalaParserVisitor; */ public class ScalaRule extends AbstractRule implements ScalaParserVisitor { - /** - * Create a new Scala Rule. - */ - public ScalaRule() { - super.setLanguage(LanguageRegistry.getLanguage(ScalaLanguageModule.NAME)); - } - - @Override public void apply(Node target, RuleContext ctx) { ((ScalaNode) target).accept(this, ctx); diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 8708cf8fd6..1b505a1889 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -23,12 +22,12 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { public static Collection data() { return Arrays.asList(new Object[][] { { ScalaLanguageModule.NAME, ScalaLanguageModule.TERSE_NAME, "2.13", - LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getVersion("2.13"), }, + getLanguage(ScalaLanguageModule.NAME).getVersion("2.13"), }, { ScalaLanguageModule.NAME, ScalaLanguageModule.TERSE_NAME, "2.12", - LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getVersion("2.12"), }, + getLanguage(ScalaLanguageModule.NAME).getVersion("2.12"), }, { ScalaLanguageModule.NAME, ScalaLanguageModule.TERSE_NAME, "2.11", - LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getVersion("2.11"), }, + getLanguage(ScalaLanguageModule.NAME).getVersion("2.11"), }, { ScalaLanguageModule.NAME, ScalaLanguageModule.TERSE_NAME, "2.10", - LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getVersion("2.10"), }, }); + getLanguage(ScalaLanguageModule.NAME).getVersion("2.10"), }, }); } } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java index a115be553c..38d23b5309 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java @@ -10,10 +10,7 @@ import org.junit.Test; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; -import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; import net.sourceforge.pmd.lang.scala.ast.BaseScalaTest; public class XPathRuleTest extends BaseScalaTest { @@ -28,9 +25,7 @@ public class XPathRuleTest extends BaseScalaTest { } private Report evaluate(String testSource, String xpath) { - XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, xpath); - rule.setLanguage(LanguageRegistry.getLanguage(ScalaLanguageModule.NAME)); - rule.setMessage("XPath Rule Failed"); + XPathRule rule = scala.newXpathRule(xpath); return scala.executeRuleOnResource(rule, testSource); } } diff --git a/pmd-test-schema/pom.xml b/pmd-test-schema/pom.xml index 1d5bed88fa..015d10f8a1 100644 --- a/pmd-test-schema/pom.xml +++ b/pmd-test-schema/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 pmd-test-schema PMD Test Schema diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java index f2f1f893bb..86467b06e8 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java @@ -103,7 +103,7 @@ class BaseTestParserImpl { parseBoolAttribute(testCode, "useAuxClasspath", true, err, "Attribute 'useAuxClasspath' is deprecated and ignored, assumed true"); boolean disabled = parseBoolAttribute(testCode, "disabled", false, err, null) - | !parseBoolAttribute(testCode, "regressionTest", true, err, "Attribute ''regressionTest'' is deprecated, use ''ignored'' with inverted value"); + | !parseBoolAttribute(testCode, "regressionTest", true, err, "Attribute ''regressionTest'' is deprecated, use ''disabled'' with inverted value"); descriptor.setDisabled(disabled); diff --git a/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java b/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java index 62956403a2..6a08821766 100644 --- a/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java +++ b/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java @@ -84,7 +84,7 @@ public class TestSchemaParserTest { assertEquals(1, parsed.getTests().size()); MatcherAssert.assertThat(errStreamCaptor.getLog(), containsString(" 6| \n" - + " ^^^^^^^^^^^^^^ Attribute 'regressionTest' is deprecated, use 'ignored' with inverted value\n")); + + " ^^^^^^^^^^^^^^ Attribute 'regressionTest' is deprecated, use 'disabled' with inverted value\n")); } @Test diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java index bf7b8d3230..dd99117639 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java @@ -69,6 +69,10 @@ public class AbstractLanguageVersionTest { this.expected = expected; } + protected static Language getLanguage(String name) { + return LanguageRegistry.PMD.getLanguageByFullName(name); + } + /** * Checks that the expected {@link LanguageVersion} can be found via * {@link #name} and {@link #version}. @@ -79,7 +83,7 @@ public class AbstractLanguageVersionTest { sourceLanguage.setName(name); sourceLanguage.setVersion(version); - Language language = LanguageRegistry.getLanguage(sourceLanguage.getName()); + Language language = getLanguage(sourceLanguage.getName()); LanguageVersion languageVersion = null; if (language != null) { languageVersion = language.getVersion(sourceLanguage.getVersion()); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index 08b5a96440..2825adbb4a 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -273,7 +273,7 @@ public abstract class AbstractRuleSetFactoryTest { private List getRuleSetFileNames() throws IOException { List result = new ArrayList<>(); - for (Language language : LanguageRegistry.getLanguages()) { + for (Language language : LanguageRegistry.PMD.getLanguages()) { if (this.languagesToSkip.contains(language.getTerseName())) { continue; } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java index 3380d7acc4..a5ef1450a7 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java @@ -17,6 +17,9 @@ import net.sourceforge.pmd.cpd.CPDCommandLineInterface; public abstract class BaseCPDCLITest { private ByteArrayOutputStream bufferStdout; + + private ByteArrayOutputStream bufferStderr; + private PrintStream originalStdout; private PrintStream originalStderr; @@ -26,7 +29,9 @@ public abstract class BaseCPDCLITest { originalStderr = System.err; bufferStdout = new ByteArrayOutputStream(); System.setOut(new PrintStream(bufferStdout, false, "UTF-8")); - System.setErr(System.out); + + bufferStderr = new ByteArrayOutputStream(); + System.setErr(new PrintStream(bufferStderr, false, "UTF-8")); } @After @@ -56,6 +61,14 @@ public abstract class BaseCPDCLITest { CPD.main(args); } + protected String getStderr() { + try { + return bufferStderr.toString("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + protected String runTest(CPD.StatusCode expectedStatusCode, String... args) { CPD.StatusCode statusCode = CPD.runCpd(args); Assert.assertEquals("Unexpected status code", expectedStatusCode, statusCode); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java index 4dfb091642..f5938c7454 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java @@ -46,7 +46,7 @@ public class DummyLanguageModule extends BaseLanguageModule { } public static DummyLanguageModule getInstance() { - return (DummyLanguageModule) LanguageRegistry.getLanguage(NAME); + return (DummyLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); } public static DummyRootNode parse(String code, String filename) { diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java index 6fad772396..ea8b0e5ed1 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java @@ -5,8 +5,8 @@ package net.sourceforge.pmd.testframework; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -17,15 +17,15 @@ import org.mockito.Mockito; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; +import net.sourceforge.pmd.test.lang.DummyLanguageModule; import net.sourceforge.pmd.test.lang.DummyLanguageModule.DummyRootNode; public class RuleTstTest { - private LanguageVersion dummyLanguage = LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion(); + private LanguageVersion dummyLanguage = DummyLanguageModule.getInstance().getDefaultVersion(); private Rule rule = mock(Rule.class); @@ -47,12 +47,12 @@ public class RuleTstTest { verify(rule).start(any(RuleContext.class)); verify(rule).end(any(RuleContext.class)); - verify(rule).getLanguage(); - verify(rule, times(2)).getTargetSelector(); + verify(rule, atLeastOnce()).getLanguage(); + verify(rule, atLeastOnce()).getTargetSelector(); verify(rule).getMinimumLanguageVersion(); verify(rule).getMaximumLanguageVersion(); verify(rule).apply(any(Node.class), any(RuleContext.class)); - verify(rule, times(4)).getName(); + verify(rule, atLeastOnce()).getName(); verify(rule).getPropertiesByPropertyDescriptor(); } diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/TestDescriptorTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/TestDescriptorTest.java index b3f4f308cb..d28756274c 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/TestDescriptorTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/TestDescriptorTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.PlainTextLanguage; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -32,7 +32,7 @@ public class TestDescriptorTest { private static final class MockRule extends AbstractRule { @Override public Language getLanguage() { - return LanguageRegistry.getDefaultLanguage(); + return PlainTextLanguage.getInstance(); } @Override diff --git a/pmd-visualforce/etc/grammar/Vf.jjt b/pmd-visualforce/etc/grammar/Vf.jjt index 2325155a9b..b15e8a8f03 100644 --- a/pmd-visualforce/etc/grammar/Vf.jjt +++ b/pmd-visualforce/etc/grammar/Vf.jjt @@ -13,7 +13,7 @@ options { PARSER_BEGIN(VfParserImpl) package net.sourceforge.pmd.lang.vf.ast; -import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; public class VfParserImpl { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java index 267c68175f..17926160b0 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java @@ -6,9 +6,12 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.CharStream; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStreamFactory; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaEscapeTranslator; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; +import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.vf.ast.VfTokenKinds; @@ -23,7 +26,13 @@ public class VfTokenizer extends JavaCCTokenizer { } @Override - protected CharStream makeCharStream(TextDocument sourceCode) { - return CharStreamFactory.javaCharStream(sourceCode); + protected TokenDocumentBehavior tokenBehavior() { + return new JavaccTokenDocument.TokenDocumentBehavior(VfTokenKinds.TOKEN_NAMES) { + @Override + public TextDocument translate(TextDocument text) throws MalformedSourceException { + return new JavaEscapeTranslator(text).translateDocument(); + } + }; } + } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java index 356c4d048a..60591c5ccc 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java @@ -15,7 +15,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.ast.Node; @@ -67,7 +66,7 @@ class ApexClassPropertyTypes extends SalesforceFieldTypes { } static Node parseApex(Path apexFilePath) { - LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + LanguageVersion languageVersion = ApexLanguageModule.getInstance().getDefaultVersion(); try (TextFile file = TextFile.forPath(apexFilePath, StandardCharsets.UTF_8, languageVersion); TextDocument textDocument = TextDocument.create(file)) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java index 5ea1875558..a5bce0febd 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java @@ -4,27 +4,21 @@ package net.sourceforge.pmd.lang.vf.ast; -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.document.TextDocument; /** * Parser for the VisualForce language. */ public final class VfParser extends JjtreeParserAdapter { + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(VfTokenKinds.TOKEN_NAMES); + @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument fullText) { - return new JavaccTokenDocument(fullText) { - @Override - protected @Nullable String describeKindImpl(int kind) { - return VfTokenKinds.describe(kind); - } - }; + protected TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java index 23cef3e9f0..4fb6e82f79 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java @@ -5,18 +5,12 @@ package net.sourceforge.pmd.lang.vf.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.vf.VfLanguageModule; import net.sourceforge.pmd.lang.vf.ast.VfParserVisitor; public abstract class AbstractVfRule extends AbstractRule implements VfParserVisitor { - public AbstractVfRule() { - super.setLanguage(LanguageRegistry.getLanguage(VfLanguageModule.NAME)); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java index f75a73083f..0093a8a2b7 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd; +import static java.util.Collections.singleton; import static org.junit.Assert.assertEquals; import java.io.File; @@ -13,32 +14,32 @@ import org.junit.Test; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.lang.vf.VfLanguageModule; +import net.sourceforge.pmd.lang.vf.ast.AbstractVfTest; /** * @author sergey.gorbaty * */ -public class LanguageVersionDiscovererTest { +public class LanguageVersionDiscovererTest extends AbstractVfTest { /** * Test on VF file. */ @Test public void testVFFile() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD); File vfFile = new File("/path/to/MyPage.page"); LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(vfFile); assertEquals("LanguageVersion must be VF!", - LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(), languageVersion); + vf.getLanguage().getDefaultVersion(), languageVersion); } @Test public void testComponentFile() { - LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(); + LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(new LanguageRegistry(singleton(vf.getLanguage()))); File vfFile = new File("/path/to/MyPage.component"); LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(vfFile); assertEquals("LanguageVersion must be VF!", - LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(), languageVersion); + vf.getLanguage().getDefaultVersion(), languageVersion); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 238c5839b4..571a6ecb7b 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.vf.VfLanguageModule; @@ -22,6 +21,6 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { return Arrays.asList(new Object[][] { { VfLanguageModule.NAME, VfLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(VfLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java index ab922a2abf..b81876d42a 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.vf.ast; public abstract class AbstractVfTest { - protected final VfParsingHelper vf = VfParsingHelper.DEFAULT.withResourceContext(getClass()); + protected final VfParsingHelper vf = + VfParsingHelper.DEFAULT + .withResourceContext(getClass()); } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java index bd9292b8bd..d94cc1500d 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java @@ -11,7 +11,7 @@ public final class VfParsingHelper extends BaseParsingHelper { + private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(VmTokenKinds.TOKEN_NAMES) { + + @Override + public JavaccToken createToken(JavaccTokenDocument self, int kind, CharStream cs, @Nullable String image) { + String realImage = image == null ? cs.getTokenImage() : image; + if (kind == VmTokenKinds.ESCAPE_DIRECTIVE) { + realImage = escapedDirective(realImage); + } + + return super.createToken(self, kind, cs, realImage); + } + + private String escapedDirective(String strImage) { + int iLast = strImage.lastIndexOf("\\"); + String strDirective = strImage.substring(iLast + 1); + return strImage.substring(0, iLast / 2) + strDirective; + } + }; + @Override - protected JavaccTokenDocument newDocumentImpl(TextDocument fullText) { - return new VmTokenDocument(fullText); + protected TokenDocumentBehavior tokenBehavior() { + return TOKEN_BEHAVIOR; } @Override @@ -29,33 +48,4 @@ public class VmParser extends JjtreeParserAdapter { } - private static class VmTokenDocument extends JavaccTokenDocument { - - VmTokenDocument(TextDocument fullText) { - super(fullText); - } - - @Override - protected @Nullable String describeKindImpl(int kind) { - return VmTokenKinds.describe(kind); - } - - @Override - public JavaccToken createToken(int kind, CharStream cs, @Nullable String image) { - String realImage = image == null ? cs.GetImage() : image; - if (kind == VmTokenKinds.ESCAPE_DIRECTIVE) { - realImage = escapedDirective(realImage); - } - - return super.createToken(kind, cs, realImage); - } - - private String escapedDirective(String strImage) { - int iLast = strImage.lastIndexOf("\\"); - String strDirective = strImage.substring(iLast + 1); - return strImage.substring(0, iLast / 2) + strDirective; - } - - } - } diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java index 9892992abd..b309c8edf1 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java @@ -5,18 +5,12 @@ package net.sourceforge.pmd.lang.vm.rule; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.vm.VmLanguageModule; import net.sourceforge.pmd.lang.vm.ast.VmParserVisitor; public abstract class AbstractVmRule extends AbstractRule implements VmParserVisitor { - public AbstractVmRule() { - super.setLanguage(LanguageRegistry.getLanguage(VmLanguageModule.NAME)); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-vm/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-vm/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 9fbd65f8a1..3b98787be2 100644 --- a/pmd-vm/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-vm/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.vm.VmLanguageModule; @@ -22,6 +21,6 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { @Parameters public static Collection data() { return Arrays.asList(new Object[][] { { VmLanguageModule.NAME, VmLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(VmLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(VmLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 38d993ed2c..f81d9dfc05 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.junit.runners.Parameterized.Parameters; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.pom.PomLanguageModule; import net.sourceforge.pmd.lang.wsdl.WsdlLanguageModule; @@ -26,12 +25,12 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { public static Collection data() { return Arrays.asList(new Object[][] { { XmlLanguageModule.NAME, XmlLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(XmlLanguageModule.NAME).getDefaultVersion(), }, + getLanguage(XmlLanguageModule.NAME).getDefaultVersion(), }, { XslLanguageModule.NAME, XslLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(XslLanguageModule.NAME).getDefaultVersion(), }, + getLanguage(XslLanguageModule.NAME).getDefaultVersion(), }, { WsdlLanguageModule.NAME, WsdlLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(WsdlLanguageModule.NAME).getDefaultVersion(), }, + getLanguage(WsdlLanguageModule.NAME).getDefaultVersion(), }, { PomLanguageModule.NAME, PomLanguageModule.TERSE_NAME, "", - LanguageRegistry.getLanguage(PomLanguageModule.NAME).getDefaultVersion(), }, }); + getLanguage(PomLanguageModule.NAME).getDefaultVersion(), }, }); } } diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java index 14e717b237..3e39ab2d9a 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java @@ -25,7 +25,8 @@ public class XmlCliTest extends BaseCLITest { List arguments = new ArrayList<>(listOf( "-f", "text", - "-no-cache", + "--no-cache", + "--no-progress", "-R", BASE_DIR + "/ruleset.xml", "-d", diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java index f7d4ff0d63..00acd53063 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java @@ -10,8 +10,6 @@ import org.junit.Test; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.xml.XmlLanguageModule; import net.sourceforge.pmd.lang.xml.XmlParsingHelper; public class XmlXPathRuleTest { @@ -36,7 +34,7 @@ public class XmlXPathRuleTest { private Rule makeXPath(String expression, String nsUri) { DomXPathRule rule = new DomXPathRule(expression, nsUri); - rule.setLanguage(LanguageRegistry.getLanguage(XmlLanguageModule.NAME)); + rule.setLanguage(xml.getLanguage()); rule.setMessage("XPath Rule Failed"); return rule; } diff --git a/pom.xml b/pom.xml index 100f52d713..74247f7145 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ - 2022-06-25T07:30:42Z + 2022-08-31T17:19:27Z 8 @@ -95,7 +95,7 @@ 3.0.0-M5 9.3 3.1.2 - 3.17.0 + 3.18.0 1.10.12 3.2.0 4.9.1 @@ -107,7 +107,7 @@ https://pmd.github.io/pmd -Xmx512m -Dfile.encoding=${project.build.sourceEncoding} ${extraArgLine} - + 18 @@ -427,22 +427,22 @@ net.sourceforge.pmd pmd-core - 6.47.0 + 6.48.0 net.sourceforge.pmd pmd-java - 6.47.0 + 6.48.0 net.sourceforge.pmd pmd-jsp - 6.47.0 + 6.48.0 net.sourceforge.pmd pmd-javascript - 6.47.0 + 6.48.0