diff --git a/.all-contributorsrc b/.all-contributorsrc index 898c8d6350..92b95c9c8c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -7591,6 +7591,7 @@ "bug" ] }, + { "login": "pablogomez2197", "name": "pablogomez2197", "avatar_url": "https://avatars.githubusercontent.com/u/110610165?v=4", @@ -7598,6 +7599,61 @@ "contributions": [ "bug" ] + }, + { + "login": "stephen-carter-at-sf", + "name": "Stephen Carter", + "avatar_url": "https://avatars.githubusercontent.com/u/123964848?v=4", + "profile": "https://github.com/stephen-carter-at-sf", + "contributions": [ + "bug" + ] + }, + { + "login": "Meijuh", + "name": "Jeroen Meijer", + "avatar_url": "https://avatars.githubusercontent.com/u/1567680?v=4", + "profile": "http://jmeijer.nl/", + "contributions": [ + "bug" + ] + }, + { + "login": "codefriar", + "name": "Kevin Poorman", + "avatar_url": "https://avatars.githubusercontent.com/u/642589?v=4", + "profile": "http://www.codefriar.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "szymanp23", + "name": "szymanp23", + "avatar_url": "https://avatars.githubusercontent.com/u/4140681?v=4", + "profile": "https://github.com/szymanp23", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "johnzhao9", + "name": "johnzhao9", + "avatar_url": "https://avatars.githubusercontent.com/u/13734035?v=4", + "profile": "https://github.com/johnzhao9", + "contributions": [ + "bug" + ] + }, + { + "login": "duursma", + "name": "duursma", + "avatar_url": "https://avatars.githubusercontent.com/u/9378973?v=4", + "profile": "https://github.com/duursma", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.ci/build.sh b/.ci/build.sh index 291a10b424..4e843d4311 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -83,6 +83,16 @@ function build() { ./mvnw clean verify -Dskip-cli-dist --show-version --errors --batch-mode "${PMD_MAVEN_EXTRA_OPTS[@]}" else # b) only pmd-cli and pmd-dist + # + # In the first stage build (without pmd-cli and pmd-dist), cyclonedx:makeAggregateBom tries to + # fetch the jars of the to-be-released modules, which don't exist yet. This is recorded in *.lastUpdated + # files in the local repo and might end up in the cache, that is used for this 2nd stage build. + # Trying to delete the files now, if they exist. + # Alternatively, we could run maven with flag "-U" to force update all dependencies... + pmd_ci_log_info "Cleanup local maven repo..." + find ~/.m2/repository -wholename "*/net/sourceforge/pmd/*/${PMD_CI_MAVEN_PROJECT_VERSION}/*.lastUpdated" | xargs rm -v + pmd_ci_log_info "Cleanup local maven repo finished." + ./mvnw clean verify -pl pmd-cli,pmd-dist --show-version --errors --batch-mode "${PMD_MAVEN_EXTRA_OPTS[@]}" fi else diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c27288ee6f..9f4bc12a42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,8 +48,6 @@ jobs: ~/.cache ~/work/pmd/target/repositories vendor/bundle - # avoid caching missed dependencies - !~/.m2/repository/**/*.lastUpdated key: v3-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | v3-${{ runner.os }}- diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f176..0000000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 4a95a1367b..f95f1ee807 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -5,14 +5,15 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip diff --git a/do-release.sh b/do-release.sh index ef34e1063a..269b2d8589 100755 --- a/do-release.sh +++ b/do-release.sh @@ -108,12 +108,12 @@ if [ "${BUILD_TOOLS_VERSION}" != "${BUILD_TOOLS_VERSION_RELEASE}" ]; then exit 1 fi -echo "* Update date info in **docs/_config.yml**." -echo " date: $(date -u +%d-%B-%Y)" -echo echo "* Update version info in **docs/_config.yml**." echo " remove the SNAPSHOT from site.pmd.version" echo +echo "* Update date info in **docs/_config.yml**." +echo " date: $(date -u +%Y-%m-%d)" +echo echo "* Update **pmd-apex/src/main/resources/rulesets/apex/quickstart.xml** and" echo " **pmd-java/src/main/resources/rulesets/java/quickstart.xml** with the new rules." echo @@ -204,7 +204,7 @@ echo echo "Tag has been pushed.... now check github actions: " echo echo "Now wait, until first stage of the release is finished successfully..." -echo "You don't need to wait until artifacts are in maven central, just the github action must be successful." +echo "You don't need to wait until artifacts are in maven central, just the GitHub Action must be successful." echo echo "If it is failing, you can fix the code/scripts and force push the tag via" echo @@ -214,7 +214,7 @@ echo " git push origin tag \"pmd_releases/${RELEASE_VERSION}\" --force" echo echo "However: This is only possible, if the artefacts have not been pushed to maven central yet..." echo -echo "Press enter to continue..." +echo "Press enter to continue, once the GitHub Action finished successfully..." read -r echo @@ -344,8 +344,8 @@ tweet="${tweet//#/%23}" tweet="${tweet//\//%2F}" tweet="${tweet//$'\r'/}" tweet="${tweet//$'\n'/%0A}" -echo "* Tweet about this release on https://twitter.com/pmd_analyzer:" -echo " " +echo "* Tweet about this release on https://x.com/pmd_analyzer:" +echo " " echo "* Post this also into :" echo " PMD ${RELEASE_VERSION} released: https://github.com/pmd/pmd/releases/tag/pmd_releases/${RELEASE_VERSION} #PMD" echo diff --git a/docs/_config.yml b/docs/_config.yml index 29e464a84f..3302938c99 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,9 @@ repository: pmd/pmd pmd: - version: 7.3.0-SNAPSHOT - previous_version: 7.2.0 - date: 2024-06-28 + version: 7.4.0-SNAPSHOT + previous_version: 7.3.0 + date: 2024-07-26 # release types: major, minor, bugfix release_type: minor diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md index 64bcacf81a..2cf623881e 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md @@ -3,7 +3,7 @@ title: How to add a new CPD language short_title: Adding a new CPD language tags: [devdocs, extending] summary: How to add a new language module with CPD support. -last_updated: April 2023 (7.0.0) +last_updated: June 2024 (7.3.0) permalink: pmd_devdocs_major_adding_new_cpd_language.html author: MatΓ­as Fraga, ClΓ©ment Fournier --- @@ -45,8 +45,15 @@ Use the following guide to set up a new language module that supports CPD. } ``` + - If your language is case-insensitive, then you might want to overwrite `getImage(AntlrToken)`. There you can + change each token e.g. into uppercase, so that CPD sees the same strings and can find duplicates even when + the casing differs. See {% jdoc tsql::lang.tsql.cpd.TSqlCpdLexer %} for an example. You will also need a + "CaseChangingCharStream", so that antlr itself is case-insensitive. - For JavaCC grammars, place your grammar in `etc/grammar` and edit the `pom.xml` like the [Python implementation](https://github.com/pmd/pmd/blob/master/pmd-python/pom.xml) does. You can then subclass {% jdoc core::cpd.impl.JavaccCpdLexer %} instead of AntlrCpdLexer. + - If your JavaCC based language is case-insensitive (option `IGNORE_CASE=true`), then you need to implement + {%jdoc core::lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior %}, which can change each token + e.g. into uppercase. See {%jdoc plsql::lang.plsql.ast.PLSQLParser %} for an example. - For any other scenario just implement the interface however you can. Look at the Scala or Apex module for existing implementations. 3. Create a {% jdoc core::lang.Language %} implementation, and make it implement {% jdoc core::cpd.CpdCapableLanguage %}. diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 0f97c47bec..d9cffe897d 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -363,680 +363,688 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Jeff May
Jeff May

πŸ› Jens Gerdes
Jens Gerdes

πŸ› Jeroen Borgers
Jeroen Borgers

πŸ› πŸ’» πŸ“’ - Jeroen van Wilgenburg
Jeroen van Wilgenburg

πŸ“– + Jeroen Meijer
Jeroen Meijer

πŸ› + Jeroen van Wilgenburg
Jeroen van Wilgenburg

πŸ“– Jerome Russ
Jerome Russ

πŸ› JerritEic
JerritEic

πŸ’» πŸ“– πŸ› Jiri Pejchal
Jiri Pejchal

πŸ› Jithin Sunny
Jithin Sunny

πŸ› JiΕ™Γ­ Ε korpil
JiΕ™Γ­ Ε korpil

πŸ› Joao Machado
Joao Machado

πŸ› - Jochen Krauss
Jochen Krauss

πŸ› + Jochen Krauss
Jochen Krauss

πŸ› Johan Hammar
Johan Hammar

πŸ› John Karp
John Karp

πŸ› John Zhang
John Zhang

πŸ› John-Teng
John-Teng

πŸ’» πŸ› Jon Moroney
Jon Moroney

πŸ’» πŸ› Jonas Geiregat
Jonas Geiregat

πŸ› - Jonas Keßler
Jonas Keßler

πŸ› + Jonas Keßler
Jonas Keßler

πŸ› Jonathan Wiesel
Jonathan Wiesel

πŸ’» πŸ› Jordan
Jordan

πŸ› Jordi Llach
Jordi Llach

πŸ› Jorge SolΓ³rzano
Jorge SolΓ³rzano

πŸ› JorneVL
JorneVL

πŸ› Jose Palafox
Jose Palafox

πŸ› - Jose Stovall
Jose Stovall

πŸ› + Jose Stovall
Jose Stovall

πŸ› Joseph
Joseph

πŸ’» Joseph Heenan
Joseph Heenan

πŸ› Josh Feingold
Josh Feingold

πŸ’» πŸ› Josh Holthaus
Josh Holthaus

πŸ› Joshua S Arquilevich
Joshua S Arquilevich

πŸ› JoΓ£o Dinis Ferreira
JoΓ£o Dinis Ferreira

πŸ“– - JoΓ£o Ferreira
JoΓ£o Ferreira

πŸ’» πŸ› + JoΓ£o Ferreira
JoΓ£o Ferreira

πŸ’» πŸ› JoΓ£o Pedro Schmitt
JoΓ£o Pedro Schmitt

πŸ› Juan MartΓ­n Sotuyo Dodero
Juan MartΓ­n Sotuyo Dodero

πŸ’» πŸ“– πŸ› 🚧 Juan Pablo Civile
Juan Pablo Civile

πŸ› Julian Voronetsky
Julian Voronetsky

πŸ› Julien
Julien

πŸ› Julius
Julius

πŸ› - JustPRV
JustPRV

πŸ› + JustPRV
JustPRV

πŸ› JΓΆrn Huxhorn
JΓΆrn Huxhorn

πŸ› KThompso
KThompso

πŸ› Kai Amundsen
Kai Amundsen

πŸ› Karel Vervaeke
Karel Vervaeke

πŸ› Karl-Andero Mere
Karl-Andero Mere

πŸ› Karl-Philipp Richter
Karl-Philipp Richter

πŸ› - Karsten Silz
Karsten Silz

πŸ› + Karsten Silz
Karsten Silz

πŸ› Kazuma Watanabe
Kazuma Watanabe

πŸ› Kev
Kev

πŸ› Keve MΓΌller
Keve MΓΌller

πŸ› Kevin Guerra
Kevin Guerra

πŸ’» Kevin Jones
Kevin Jones

πŸ› πŸ’» - Kevin Wayne
Kevin Wayne

πŸ› - Kieran Black
Kieran Black

πŸ› + Kevin Poorman
Kevin Poorman

πŸ› + Kevin Wayne
Kevin Wayne

πŸ› + Kieran Black
Kieran Black

πŸ› Kirill Zubov
Kirill Zubov

πŸ› Kirk Clemens
Kirk Clemens

πŸ’» πŸ› Klaus Hartl
Klaus Hartl

πŸ› Koen Van Looveren
Koen Van Looveren

πŸ› Kris Scheibe
Kris Scheibe

πŸ’» πŸ› - Krystian Dabrowski
Krystian Dabrowski

πŸ› πŸ’» - Kunal Thanki
Kunal Thanki

πŸ› + Krystian Dabrowski
Krystian Dabrowski

πŸ› πŸ’» + Kunal Thanki
Kunal Thanki

πŸ› LaLucid
LaLucid

πŸ’» Larry Diamond
Larry Diamond

πŸ’» πŸ› Lars Knickrehm
Lars Knickrehm

πŸ› Laurent Bovet
Laurent Bovet

πŸ› πŸ’» Leo Gutierrez
Leo Gutierrez

πŸ› - LiGaOg
LiGaOg

πŸ’» - Liam Sharp
Liam Sharp

πŸ› + LiGaOg
LiGaOg

πŸ’» + Liam Sharp
Liam Sharp

πŸ› Lintsi
Lintsi

πŸ› Linus Fernandes
Linus Fernandes

πŸ› Lixon Lookose
Lixon Lookose

πŸ› Logesh
Logesh

πŸ› Lorenzo Gabriele
Lorenzo Gabriele

πŸ› - LoΓ―c Ledoyen
LoΓ―c Ledoyen

πŸ› - Lucas
Lucas

πŸ› + LoΓ―c Ledoyen
LoΓ―c Ledoyen

πŸ› + Lucas
Lucas

πŸ› Lucas Silva
Lucas Silva

πŸ› Lucas Soncini
Lucas Soncini

πŸ’» πŸ› Luis Alcantar
Luis Alcantar

πŸ’» Lukasz Slonina
Lukasz Slonina

πŸ› Lukebray
Lukebray

πŸ› - Lynn
Lynn

πŸ’» πŸ› - Lyor Goldstein
Lyor Goldstein

πŸ› + Lynn
Lynn

πŸ’» πŸ› + Lyor Goldstein
Lyor Goldstein

πŸ› MCMicS
MCMicS

πŸ› Macarse
Macarse

πŸ› Machine account for PMD
Machine account for PMD

πŸ’» Maciek Siemczyk
Maciek Siemczyk

πŸ› Maikel Steneker
Maikel Steneker

πŸ’» πŸ› - Maksim Moiseikin
Maksim Moiseikin

πŸ› - Manfred Koch
Manfred Koch

πŸ› + Maksim Moiseikin
Maksim Moiseikin

πŸ› + Manfred Koch
Manfred Koch

πŸ› Manuel Moya Ferrer
Manuel Moya Ferrer

πŸ’» πŸ› Manuel Ryan
Manuel Ryan

πŸ› Marat Vyshegorodtsev
Marat Vyshegorodtsev

πŸ› Marcel HΓ€rle
Marcel HΓ€rle

πŸ› Marcello Fialho
Marcello Fialho

πŸ› - Marcin DΔ…browski
Marcin DΔ…browski

πŸ’» - Marcin Rataj
Marcin Rataj

πŸ› + Marcin DΔ…browski
Marcin DΔ…browski

πŸ’» + Marcin Rataj
Marcin Rataj

πŸ› Marcono1234
Marcono1234

πŸ› Mark Adamcin
Mark Adamcin

πŸ› Mark Hall
Mark Hall

πŸ’» πŸ› Mark Kolich
Mark Kolich

πŸ› Mark Pritchard
Mark Pritchard

πŸ› - Markus Rathgeb
Markus Rathgeb

πŸ› - Marquis Wang
Marquis Wang

πŸ› + Markus Rathgeb
Markus Rathgeb

πŸ› + Marquis Wang
Marquis Wang

πŸ› MartGit
MartGit

πŸ› Martin Feldsztejn
Martin Feldsztejn

πŸ› Martin Lehmann
Martin Lehmann

πŸ› Martin Spamer
Martin Spamer

πŸ› Martin TarjΓ‘nyi
Martin TarjΓ‘nyi

πŸ› - MatFl
MatFl

πŸ› - Mateusz Stefanski
Mateusz Stefanski

πŸ› + MatFl
MatFl

πŸ› + Mateusz Stefanski
Mateusz Stefanski

πŸ› Mathieu Gouin
Mathieu Gouin

πŸ› MatiasComercio
MatiasComercio

πŸ’» πŸ› Matt Benson
Matt Benson

πŸ› Matt De Poorter
Matt De Poorter

πŸ› Matt Hargett
Matt Hargett

πŸ’» πŸ’΅ - Matt Harrah
Matt Harrah

πŸ› - Matt Nelson
Matt Nelson

πŸ› + Matt Harrah
Matt Harrah

πŸ› + Matt Nelson
Matt Nelson

πŸ› Matthew Amos
Matthew Amos

πŸ› Matthew Duggan
Matthew Duggan

πŸ› Matthew Hall
Matthew Hall

πŸ› MatΓ­as Fraga
MatΓ­as Fraga

πŸ’» πŸ› Maxime Robert
Maxime Robert

πŸ’» πŸ› - MetaBF
MetaBF

πŸ› - Metin Dagcilar
Metin Dagcilar

πŸ› + MetaBF
MetaBF

πŸ› + Metin Dagcilar
Metin Dagcilar

πŸ› Michael
Michael

πŸ› Michael Bell
Michael Bell

πŸ› Michael Bernstein
Michael Bernstein

πŸ› Michael Clay
Michael Clay

πŸ› Michael Dombrowski
Michael Dombrowski

πŸ› - Michael Hausegger
Michael Hausegger

πŸ› - Michael Hoefer
Michael Hoefer

πŸ› + Michael Hausegger
Michael Hausegger

πŸ› + Michael Hoefer
Michael Hoefer

πŸ› Michael Kolesnikov
Michael Kolesnikov

πŸ› Michael MΓΆbius
Michael MΓΆbius

πŸ› Michael N. Lipp
Michael N. Lipp

πŸ› Michael Pellegrini
Michael Pellegrini

πŸ› Michal Kordas
Michal Kordas

πŸ› - MichaΕ‚ Borek
MichaΕ‚ Borek

πŸ› - MichaΕ‚ KuliΕ„ski
MichaΕ‚ KuliΕ„ski

πŸ› + MichaΕ‚ Borek
MichaΕ‚ Borek

πŸ› + MichaΕ‚ KuliΕ„ski
MichaΕ‚ KuliΕ„ski

πŸ› Miguel NΓΊΓ±ez DΓ­az-Montes
Miguel NΓΊΓ±ez DΓ­az-Montes

πŸ› Mihai Ionut
Mihai Ionut

πŸ› Mikhail Kuchma
Mikhail Kuchma

πŸ› Mirek Hankus
Mirek Hankus

πŸ› Mitch Spano
Mitch Spano

πŸ› - Mladjan Gadzic
Mladjan Gadzic

πŸ› - MrAngry52
MrAngry52

πŸ› + Mladjan Gadzic
Mladjan Gadzic

πŸ› + MrAngry52
MrAngry52

πŸ› Muminur Choudhury
Muminur Choudhury

πŸ› Mykhailo Palahuta
Mykhailo Palahuta

πŸ’» πŸ› Nagendra Kumar Singh
Nagendra Kumar Singh

πŸ› Nahuel Barrios
Nahuel Barrios

πŸ› Nakul Sharma
Nakul Sharma

πŸ› - Nathan Braun
Nathan Braun

πŸ› - Nathan Reynolds
Nathan Reynolds

πŸ› + Nathan Braun
Nathan Braun

πŸ› + Nathan Reynolds
Nathan Reynolds

πŸ› Nathan Reynolds
Nathan Reynolds

πŸ› NathanaΓ«l
NathanaΓ«l

πŸ› Naveen
Naveen

πŸ’» Nazdravi
Nazdravi

πŸ› Neha-Dhonde
Neha-Dhonde

πŸ› - Nicholas Doyle
Nicholas Doyle

πŸ› - Nick Butcher
Nick Butcher

πŸ› + Nicholas Doyle
Nicholas Doyle

πŸ› + Nick Butcher
Nick Butcher

πŸ› Nico Gallinal
Nico Gallinal

πŸ› Nicola Dal Maso
Nicola Dal Maso

πŸ› Nicolas Filotto
Nicolas Filotto

πŸ’» Nicolas Vervelle
Nicolas Vervelle

πŸ› Nicolas Vuillamy
Nicolas Vuillamy

πŸ“– - Nikita Chursin
Nikita Chursin

πŸ› - Niklas Baudy
Niklas Baudy

πŸ› + Nikita Chursin
Nikita Chursin

πŸ› + Niklas Baudy
Niklas Baudy

πŸ› Nikolas Havrikov
Nikolas Havrikov

πŸ› Nilesh Virkar
Nilesh Virkar

πŸ› Nimit Patel
Nimit Patel

πŸ› Niranjan Harpale
Niranjan Harpale

πŸ› Nirvik Patel
Nirvik Patel

πŸ’» - Noah Sussman
Noah Sussman

πŸ› - Noah0120
Noah0120

πŸ› + Noah Sussman
Noah Sussman

πŸ› + Noah0120
Noah0120

πŸ› Noam Tamim
Noam Tamim

πŸ› Noel Grandin
Noel Grandin

πŸ› Olaf Haalstra
Olaf Haalstra

πŸ› Oleg Andreych
Oleg Andreych

πŸ’» πŸ› Oleg Pavlenko
Oleg Pavlenko

πŸ› - Oleksii Dykov
Oleksii Dykov

πŸ’» πŸ› - Oliver Eikemeier
Oliver Eikemeier

πŸ› + Oleksii Dykov
Oleksii Dykov

πŸ’» πŸ› + Oliver Eikemeier
Oliver Eikemeier

πŸ› Oliver Siegmar
Oliver Siegmar

πŸ’΅ Olivier Parent
Olivier Parent

πŸ’» πŸ› Ollie Abbey
Ollie Abbey

πŸ’» πŸ› OverDrone
OverDrone

πŸ› Ozan Gulle
Ozan Gulle

πŸ’» πŸ› - PUNEET JAIN
PUNEET JAIN

πŸ› - Parbati Bose
Parbati Bose

πŸ› + PUNEET JAIN
PUNEET JAIN

πŸ› + Parbati Bose
Parbati Bose

πŸ› Paul Berg
Paul Berg

πŸ› Paul Guyot
Paul Guyot

πŸ’» Pavel Bludov
Pavel Bludov

πŸ› Pavel Mička
Pavel Mička

πŸ› Pedro Nuno Santos
Pedro Nuno Santos

πŸ› - Pedro Rijo
Pedro Rijo

πŸ› - Pelisse Romain
Pelisse Romain

πŸ’» πŸ“– πŸ› + Pedro Rijo
Pedro Rijo

πŸ› + Pelisse Romain
Pelisse Romain

πŸ’» πŸ“– πŸ› Per Abich
Per Abich

πŸ’» Pete Davids
Pete Davids

πŸ› Peter Bruin
Peter Bruin

πŸ› Peter Chittum
Peter Chittum

πŸ’» πŸ› Peter Cudmore
Peter Cudmore

πŸ› - Peter Kasson
Peter Kasson

πŸ› - Peter Kofler
Peter Kofler

πŸ› + Peter Kasson
Peter Kasson

πŸ› + Peter Kofler
Peter Kofler

πŸ› Peter Paul Bakker
Peter Paul Bakker

πŸ’» Peter Rader
Peter Rader

πŸ› Pham Hai Trung
Pham Hai Trung

πŸ› Philip Graf
Philip Graf

πŸ’» πŸ› Philip Hachey
Philip Hachey

πŸ› - Philippe Ozil
Philippe Ozil

πŸ› - Phinehas Artemix
Phinehas Artemix

πŸ› + Philippe Ozil
Philippe Ozil

πŸ› + Phinehas Artemix
Phinehas Artemix

πŸ› Phokham Nonava
Phokham Nonava

πŸ› Pim van der Loos
Pim van der Loos

πŸ’» ⚠️ Piotr SzymaΕ„ski
Piotr SzymaΕ„ski

πŸ› Piotrek Ε»ygieΕ‚o
Piotrek Ε»ygieΕ‚o

πŸ’» πŸ› πŸ“– Pranay Jaiswal
Pranay Jaiswal

πŸ› - Prasad Kamath
Prasad Kamath

πŸ› - Prasanna
Prasanna

πŸ› + Prasad Kamath
Prasad Kamath

πŸ› + Prasanna
Prasanna

πŸ› Presh-AR
Presh-AR

πŸ› Puneet1726
Puneet1726

πŸ› Rafael CortΓͺs
Rafael CortΓͺs

πŸ› RaheemShaik999
RaheemShaik999

πŸ› RajeshR
RajeshR

πŸ’» πŸ› - Ramachandra Mohan
Ramachandra Mohan

πŸ› - Ramel0921
Ramel0921

πŸ› + Ramachandra Mohan
Ramachandra Mohan

πŸ› + Ramel0921
Ramel0921

πŸ› Raquel Pau
Raquel Pau

πŸ› Ravikiran Janardhana
Ravikiran Janardhana

πŸ› Reda Benhemmouche
Reda Benhemmouche

πŸ› Reinhard Schiedermeier
Reinhard Schiedermeier

πŸ› Renato Oliveira
Renato Oliveira

πŸ’» πŸ› - Rich DiCroce
Rich DiCroce

πŸ› - Richard Corfield
Richard Corfield

πŸ’» + Rich DiCroce
Rich DiCroce

πŸ› + Richard Corfield
Richard Corfield

πŸ’» Richard Corfield
Richard Corfield

πŸ› πŸ’» Riot R1cket
Riot R1cket

πŸ› Rishabh Jain
Rishabh Jain

πŸ› RishabhDeep Singh
RishabhDeep Singh

πŸ› Rob Baillie
Rob Baillie

πŸ› - Robbie Martinus
Robbie Martinus

πŸ’» πŸ› - Robert Henry
Robert Henry

πŸ› + Robbie Martinus
Robbie Martinus

πŸ’» πŸ› + Robert Henry
Robert Henry

πŸ› Robert Mihaly
Robert Mihaly

πŸ› Robert Painsi
Robert Painsi

πŸ› Robert Russell
Robert Russell

πŸ› Robert SΓΆsemann
Robert SΓΆsemann

πŸ’» πŸ“– πŸ“’ πŸ› Robert Whitebit
Robert Whitebit

πŸ› - Robin Richtsfeld
Robin Richtsfeld

πŸ› - Robin Stocker
Robin Stocker

πŸ’» πŸ› + Robin Richtsfeld
Robin Richtsfeld

πŸ› + Robin Stocker
Robin Stocker

πŸ’» πŸ› Robin Wils
Robin Wils

πŸ› RochusOest
RochusOest

πŸ› Rodolfo Noviski
Rodolfo Noviski

πŸ› Rodrigo Casara
Rodrigo Casara

πŸ› Rodrigo Fernandes
Rodrigo Fernandes

πŸ› - Roman Salvador
Roman Salvador

πŸ’» πŸ› - Ronald Blaschke
Ronald Blaschke

πŸ› + Roman Salvador
Roman Salvador

πŸ’» πŸ› + Ronald Blaschke
Ronald Blaschke

πŸ› RΓ³bert Papp
RΓ³bert Papp

πŸ› Saikat Sengupta
Saikat Sengupta

πŸ› Saksham Handu
Saksham Handu

πŸ› Saladoc
Saladoc

πŸ› Salesforce Bob Lightning
Salesforce Bob Lightning

πŸ› - Sam Carlberg
Sam Carlberg

πŸ› - Sashko
Sashko

πŸ’» + Sam Carlberg
Sam Carlberg

πŸ› + Sashko
Sashko

πŸ’» Satoshi Kubo
Satoshi Kubo

πŸ› Scott Kennedy
Scott Kennedy

πŸ› Scott Wells
Scott Wells

πŸ› πŸ’» Scrates1
Scrates1

πŸ› πŸ’» Scrsloota
Scrsloota

πŸ’» - Sebastian BΓΆgl
Sebastian BΓΆgl

πŸ› - Sebastian Davids
Sebastian Davids

πŸ› + Sebastian BΓΆgl
Sebastian BΓΆgl

πŸ› + Sebastian Davids
Sebastian Davids

πŸ› Sebastian Schuberth
Sebastian Schuberth

πŸ› Sebastian Schwarz
Sebastian Schwarz

πŸ› Seren
Seren

πŸ› πŸ’» Sergey Gorbaty
Sergey Gorbaty

πŸ› Sergey Kozlov
Sergey Kozlov

πŸ› - Sergey Yanzin
Sergey Yanzin

πŸ’» πŸ› - Seth Wilcox
Seth Wilcox

πŸ’» + Sergey Yanzin
Sergey Yanzin

πŸ’» πŸ› + Seth Wilcox
Seth Wilcox

πŸ’» Shai Bennathan
Shai Bennathan

πŸ› πŸ’» Shubham
Shubham

πŸ’» πŸ› Simon Abykov
Simon Abykov

πŸ’» πŸ› Simon Xiao
Simon Xiao

πŸ› Srinivasan Venkatachalam
Srinivasan Venkatachalam

πŸ› - Stanislav Gromov
Stanislav Gromov

πŸ› - Stanislav Myachenkov
Stanislav Myachenkov

πŸ’» + Stanislav Gromov
Stanislav Gromov

πŸ› + Stanislav Myachenkov
Stanislav Myachenkov

πŸ’» Stefan Birkner
Stefan Birkner

πŸ› Stefan Bohn
Stefan Bohn

πŸ› Stefan Endrullis
Stefan Endrullis

πŸ› Stefan KlΓΆss-Schuster
Stefan KlΓΆss-Schuster

πŸ› Stefan Wolf
Stefan Wolf

πŸ› - Stephan H. Wissel
Stephan H. Wissel

πŸ› - Stephen
Stephen

πŸ› + Stephan H. Wissel
Stephan H. Wissel

πŸ› + Stephen
Stephen

πŸ› + Stephen Carter
Stephen Carter

πŸ› Stephen Friedrich
Stephen Friedrich

πŸ› Steve Babula
Steve Babula

πŸ’» Steven Stearns
Steven Stearns

πŸ› πŸ’» Stexxe
Stexxe

πŸ› + + Stian LΓ₯gstad
Stian LΓ₯gstad

πŸ› StuartClayton5
StuartClayton5

πŸ› Supun Arunoda
Supun Arunoda

πŸ› - - Suren Abrahamyan
Suren Abrahamyan

πŸ› Suvashri
Suvashri

πŸ“– SwatiBGupta1110
SwatiBGupta1110

πŸ› SyedThoufich
SyedThoufich

πŸ› + + Szymon Sasin
Szymon Sasin

πŸ› T-chuangxin
T-chuangxin

πŸ› TERAI Atsuhiro
TERAI Atsuhiro

πŸ› - - TIOBE Software
TIOBE Software

πŸ’» πŸ› Tarush Singh
Tarush Singh

πŸ’» Taylor Smock
Taylor Smock

πŸ› Techeira DamiΓ‘n
Techeira DamiΓ‘n

πŸ’» πŸ› + + Ted Husted
Ted Husted

πŸ› TehBakker
TehBakker

πŸ› The Gitter Badger
The Gitter Badger

πŸ› - - Theodoor
Theodoor

πŸ› Thiago Henrique HΓΌpner
Thiago Henrique HΓΌpner

πŸ› Thibault Meyer
Thibault Meyer

πŸ› Thomas GΓΌttler
Thomas GΓΌttler

πŸ› + + Thomas Jones-Low
Thomas Jones-Low

πŸ› Thomas Smith
Thomas Smith

πŸ’» πŸ› ThrawnCA
ThrawnCA

πŸ› - - Thu Vo
Thu Vo

πŸ› Thunderforge
Thunderforge

πŸ’» πŸ› Tim van der Lippe
Tim van der Lippe

πŸ› Tobias Weimer
Tobias Weimer

πŸ’» πŸ› + + Tom Copeland
Tom Copeland

πŸ› πŸ’» πŸ“– Tom Daly
Tom Daly

πŸ› Tomer Figenblat
Tomer Figenblat

πŸ› - - Tomi De Lucca
Tomi De Lucca

πŸ’» πŸ› Torsten Kleiber
Torsten Kleiber

πŸ› TrackerSB
TrackerSB

πŸ› Tyson Stewart
Tyson Stewart

πŸ› + + Ullrich Hafner
Ullrich Hafner

πŸ› Utku Cuhadaroglu
Utku Cuhadaroglu

πŸ’» πŸ› Valentin Brandl
Valentin Brandl

πŸ› - - Valeria
Valeria

πŸ› Valery Yatsynovich
Valery Yatsynovich

πŸ“– Vasily Anisimov
Vasily Anisimov

πŸ› Vibhor Goyal
Vibhor Goyal

πŸ› + + Vickenty Fesunov
Vickenty Fesunov

πŸ› Victor NoΓ«l
Victor NoΓ«l

πŸ› Vincent Galloy
Vincent Galloy

πŸ’» - - Vincent HUYNH
Vincent HUYNH

πŸ› Vincent Maurin
Vincent Maurin

πŸ› Vincent Privat
Vincent Privat

πŸ› Vishhwas
Vishhwas

πŸ› + + Vishv_Android
Vishv_Android

πŸ› Vitaly
Vitaly

πŸ› Vitaly Polonetsky
Vitaly Polonetsky

πŸ› - - Vojtech Polivka
Vojtech Polivka

πŸ› Vsevolod Zholobov
Vsevolod Zholobov

πŸ› Vyom Yadav
Vyom Yadav

πŸ’» Wang Shidong
Wang Shidong

πŸ› + + Waqas Ahmed
Waqas Ahmed

πŸ› Wayne J. Earl
Wayne J. Earl

πŸ› Wchenghui
Wchenghui

πŸ› - - Wener
Wener

πŸ’» Will Winder
Will Winder

πŸ› William Brockhus
William Brockhus

πŸ’» πŸ› Wilson Kurniawan
Wilson Kurniawan

πŸ› + + Wim Deblauwe
Wim Deblauwe

πŸ› Woongsik Choi
Woongsik Choi

πŸ› XenoAmess
XenoAmess

πŸ’» πŸ› - - Yang
Yang

πŸ’» YaroslavTER
YaroslavTER

πŸ› Yasar Shaikh
Yasar Shaikh

πŸ’» Young Chan
Young Chan

πŸ’» πŸ› + + YuJin Kim
YuJin Kim

πŸ› Yuri Dolzhenko
Yuri Dolzhenko

πŸ› Yurii Dubinka
Yurii Dubinka

πŸ› - - Zoltan Farkas
Zoltan Farkas

πŸ› Zustin
Zustin

πŸ› aaronhurst-google
aaronhurst-google

πŸ› πŸ’» alexmodis
alexmodis

πŸ› + + andreoss
andreoss

πŸ› andrey81inmd
andrey81inmd

πŸ’» πŸ› anicoara
anicoara

πŸ› - - arunprasathav
arunprasathav

πŸ› asiercamara
asiercamara

πŸ› astillich-igniti
astillich-igniti

πŸ’» avesolovksyy
avesolovksyy

πŸ› + + avishvat
avishvat

πŸ› avivmu
avivmu

πŸ› axelbarfod1
axelbarfod1

πŸ› - - b-3-n
b-3-n

πŸ› balbhadra9
balbhadra9

πŸ› base23de
base23de

πŸ› bergander
bergander

πŸ› πŸ’» + + berkam
berkam

πŸ’» πŸ› breizh31
breizh31

πŸ› caesarkim
caesarkim

πŸ› - - carolyujing
carolyujing

πŸ› cbfiddle
cbfiddle

πŸ› cesares-basilico
cesares-basilico

πŸ› chrite
chrite

πŸ› + + ciufudean
ciufudean

πŸ“– cobratbq
cobratbq

πŸ› coladict
coladict

πŸ› - - cosmoJFH
cosmoJFH

πŸ› cristalp
cristalp

πŸ› crunsk
crunsk

πŸ› cwholmes
cwholmes

πŸ› + + cyberjj999
cyberjj999

πŸ› cyw3
cyw3

πŸ› πŸ“– d1ss0nanz
d1ss0nanz

πŸ› - - dague1
dague1

πŸ“– dalizi007
dalizi007

πŸ’» danbrycefairsailcom
danbrycefairsailcom

πŸ› dariansanity
dariansanity

πŸ› + + darrenmiliband
darrenmiliband

πŸ› davidburstrom
davidburstrom

πŸ› dbirkman-paloalto
dbirkman-paloalto

πŸ› - - deepak-patra
deepak-patra

πŸ› dependabot[bot]
dependabot[bot]

πŸ’» πŸ› dinesh150
dinesh150

πŸ› diziaq
diziaq

πŸ› + + dreaminpast123
dreaminpast123

πŸ› duanyanan
duanyanan

πŸ› dutt-sanjay
dutt-sanjay

πŸ› - - + duursma
duursma

πŸ’» dylanleung
dylanleung

πŸ› dzeigler
dzeigler

πŸ› eant60
eant60

πŸ› + + ekkirala
ekkirala

πŸ› emersonmoura
emersonmoura

πŸ› emouty
emouty

πŸ’» eugenepugach
eugenepugach

πŸ› - - fairy
fairy

πŸ› filiprafalowicz
filiprafalowicz

πŸ’» flxbl-io
flxbl-io

πŸ’΅ + + foxmason
foxmason

πŸ› frankegabor
frankegabor

πŸ› frankl
frankl

πŸ› freafrea
freafrea

πŸ› - - fsapatin
fsapatin

πŸ› gracia19
gracia19

πŸ› guo fei
guo fei

πŸ› + + gurmsc5
gurmsc5

πŸ› gwilymatgearset
gwilymatgearset

πŸ’» πŸ› haigsn
haigsn

πŸ› hemanshu070
hemanshu070

πŸ› - - henrik242
henrik242

πŸ› hongpuwu
hongpuwu

πŸ› hvbtup
hvbtup

πŸ’» πŸ› + + igniti GmbH
igniti GmbH

πŸ› ilovezfs
ilovezfs

πŸ› itaigilo
itaigilo

πŸ› jakivey32
jakivey32

πŸ› - - jbennett2091
jbennett2091

πŸ› jcamerin
jcamerin

πŸ› jkeener1
jkeener1

πŸ› - jmetertea
jmetertea

πŸ› - johnra2
johnra2

πŸ’» - josemanuelrolon
josemanuelrolon

πŸ’» πŸ› - kabroxiko
kabroxiko

πŸ’» πŸ› + jmetertea
jmetertea

πŸ› + johnra2
johnra2

πŸ’» + johnzhao9
johnzhao9

πŸ› + josemanuelrolon
josemanuelrolon

πŸ’» πŸ› + kabroxiko
kabroxiko

πŸ’» πŸ› karwer
karwer

πŸ› kaulonline
kaulonline

πŸ› + + kdaemonv
kdaemonv

πŸ› kdebski85
kdebski85

πŸ› πŸ’» kenji21
kenji21

πŸ’» πŸ› kfranic
kfranic

πŸ› khalidkh
khalidkh

πŸ› - - koalalam
koalalam

πŸ› krzyk
krzyk

πŸ› + + lasselindqvist
lasselindqvist

πŸ› lgemeinhardt
lgemeinhardt

πŸ› lihuaib
lihuaib

πŸ› liqingjun123
liqingjun123

πŸ› lonelyma1021
lonelyma1021

πŸ› - - lpeddy
lpeddy

πŸ› lujiefsi
lujiefsi

πŸ’» + + lukelukes
lukelukes

πŸ’» lyriccoder
lyriccoder

πŸ› marcelmore
marcelmore

πŸ› matchbox
matchbox

πŸ› matthiaskraaz
matthiaskraaz

πŸ› - - meandonlyme
meandonlyme

πŸ› mikesive
mikesive

πŸ› + + milossesic
milossesic

πŸ› mluckam
mluckam

πŸ’» πŸ› mohan-chinnappan-n
mohan-chinnappan-n

πŸ’» mriddell95
mriddell95

πŸ› mrlzh
mrlzh

πŸ› - - msloan
msloan

πŸ› mucharlaravalika
mucharlaravalika

πŸ› + + mvenneman
mvenneman

πŸ› nareshl119
nareshl119

πŸ› nicolas-harraudeau-sonarsource
nicolas-harraudeau-sonarsource

πŸ› noerremark
noerremark

πŸ› novsirion
novsirion

πŸ› - - nwcm
nwcm

πŸ“– πŸ› πŸ’» oggboy
oggboy

πŸ› + + oinume
oinume

πŸ› orimarko
orimarko

πŸ’» πŸ› pablogomez2197
pablogomez2197

πŸ› pacvz
pacvz

πŸ’» pallavi agarwal
pallavi agarwal

πŸ› parksungrin
parksungrin

πŸ› + patpatpat123
patpatpat123

πŸ› - patpatpat123
patpatpat123

πŸ› patriksevallius
patriksevallius

πŸ› pbrajesh1
pbrajesh1

πŸ› phoenix384
phoenix384

πŸ› piotrszymanski-sc
piotrszymanski-sc

πŸ’» plan3d
plan3d

πŸ› poojasix
poojasix

πŸ› + prabhushrikant
prabhushrikant

πŸ› - prabhushrikant
prabhushrikant

πŸ› pujitha8783
pujitha8783

πŸ› r-r-a-j
r-r-a-j

πŸ› raghujayjunk
raghujayjunk

πŸ› rajeshveera
rajeshveera

πŸ› rajeswarreddy88
rajeswarreddy88

πŸ› recdevs
recdevs

πŸ› + reudismam
reudismam

πŸ’» πŸ› - reudismam
reudismam

πŸ’» πŸ› rijkt
rijkt

πŸ› rillig-tk
rillig-tk

πŸ› rmohan20
rmohan20

πŸ’» πŸ› rnveach
rnveach

πŸ› rxmicro
rxmicro

πŸ› ryan-gustafson
ryan-gustafson

πŸ’» πŸ› + sabi0
sabi0

πŸ› - sabi0
sabi0

πŸ› scais
scais

πŸ› screamingfrog
screamingfrog

πŸ’΅ sebbASF
sebbASF

πŸ› sergeygorbaty
sergeygorbaty

πŸ’» shilko2013
shilko2013

πŸ› shiomiyan
shiomiyan

πŸ“– + simeonKondr
simeonKondr

πŸ› - simeonKondr
simeonKondr

πŸ› snajberk
snajberk

πŸ› sniperrifle2004
sniperrifle2004

πŸ› snuyanzin
snuyanzin

πŸ› πŸ’» soyodream
soyodream

πŸ› sratz
sratz

πŸ› stonio
stonio

πŸ› + sturton
sturton

πŸ’» πŸ› - sturton
sturton

πŸ’» πŸ› sudharmohan
sudharmohan

πŸ› suruchidawar
suruchidawar

πŸ› svenfinitiv
svenfinitiv

πŸ› + szymanp23
szymanp23

πŸ› πŸ’» tashiscool
tashiscool

πŸ› test-git-hook
test-git-hook

πŸ› testation21
testation21

πŸ’» πŸ› diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index c616c1847b..a499b49cf8 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -5,6 +5,7 @@ tags: [userdocs] keywords: [command, line, options, help, formats, renderers] permalink: pmd_userdocs_cli_reference.html author: Tom Copeland , Xavier Le Vourch , Juan MartΓ­n Sotuyo Dodero +last_updated: June 2024 (7.3.0) --- @@ -19,7 +20,6 @@ The tool comes with a rather extensive help text, simply running with `--help`! Default value Applies to - {% include custom/cli_option_row.html options="--rulesets,-R" option_arg="refs" description="Path to a ruleset xml file. The path may reference @@ -55,7 +55,6 @@ The tool comes with a rather extensive help text, simply running with `--help`! (\":\" on Linux, \";\" on Windows) is used to separate the entries. Alternatively, a single `file:` URL to a text file containing path elements on consecutive lines can be specified. -

See also [Providing the auxiliary classpath](pmd_languages_java.html#providing-the-auxiliary-classpath).

" languages="Java" %} @@ -80,10 +79,15 @@ The tool comes with a rather extensive help text, simply running with `--help`! The valid values are the standard character sets of `java.nio.charset.Charset`." default="UTF-8" %} + {% include custom/cli_option_row.html options="--[no-]fail-on-error" + description="Specifies whether PMD exits with non-zero status if recoverable errors occurred. + By default PMD exits with status 5 if recoverable errors occurred (whether there are violations or not). + Disable this option with `--no-fail-on-error` to exit with 0 instead. In any case, a report with the found violations will be written." + %} {% include custom/cli_option_row.html options="--[no-]fail-on-violation" description="Specifies whether PMD exits with non-zero status if violations are found. By default PMD exits with status 4 if violations are found. - Disable this feature with `--no-fail-on-violation` to exit with 0 instead and just output the report." + Disable this feature with `--no-fail-on-violation` to exit with 0 instead. In any case a report with the found violations will be written." %} {% include custom/cli_option_row.html options="--file-list" option_arg="filepath" @@ -98,9 +102,7 @@ The tool comes with a rather extensive help text, simply running with `--help`! by extension is disabled and PMD tries to parse all files with the given language `<lang>`. Parsing errors are ignored and unparsable files are skipped. -

Use `--use-version` to specify the language version to use, if it is not the default.

-

This option allows to use the xml language for files, that don't use xml as extension. See [example](#analyze-other-xml-formats) below.

" %} @@ -125,7 +127,7 @@ The tool comes with a rather extensive help text, simply running with `--help`! {% include custom/cli_option_row.html options="--minimum-priority" option_arg="priority" description="Rule priority threshold; rules with lower priority than configured here won't be used. - Valid values (case insensitive): High, Medium_High, Medium, Medium_Low, Low. + Valid values (case-insensitive): High, Medium_High, Medium, Medium_Low, Low. An integer between 1 (High) and 5 (Low) is also supported. See [Configuring rules](pmd_userdocs_configuring_rules.html) on how to override priorities in custom rulesets." default="Low" @@ -141,7 +143,7 @@ The tool comes with a rather extensive help text, simply running with `--help`! description="Enables / disable progress bar indicator of live analysis progress. This ie enabled by default." %} {% include custom/cli_option_row.html options="--property,-P" - option_arg="name>= -0Everything is fine, no violations found. +0Everything is fine, no violations found and no recoverable error occurred. 1PMD exited with an exception. 2Usage error. Command-line parameters are invalid or missing. -4At least one violation has been detected, unless --no-fail-on-violation is set. +4At least one violation has been detected, unless --no-fail-on-violation is set.

Since PMD 5.3.

+5At least one recoverable error has occurred. There might be additionally zero or more violations detected. + To ignore recoverable errors, use --no-fail-on-error.

Since PMD 7.3.0.

+{%include note.html content="If PMD exits with 5, then PMD had either trouble parsing one or more files or a rule failed with an exception. +That means, that either no violations for the entire file or for that rule are reported. These cases can be considered as false-negatives. +In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report. Recoverable errors +are usually part of the generated PMD report." %} + ## Logging PMD internally uses [slf4j](https://www.slf4j.org/) and ships with slf4j-simple as the logging implementation. diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index 58259b8a71..1bf2954c18 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -4,7 +4,7 @@ tags: [cpd, userdocs] summary: "Learn how to use CPD, the copy-paste detector shipped with PMD." permalink: pmd_userdocs_cpd.html author: Tom Copeland -last_updated: August 2023 (7.0.0) +last_updated: June 2024 (7.3.0) --- ## Overview @@ -132,8 +132,8 @@ exactly identical. description="Don't scan subdirectories. By default, subdirectories are considered." %} {% include custom/cli_option_row.html options="--skip-lexical-errors" - description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD. - By default, CPD analysis is stopped on the first error." + description="Deprecated Skip files which can't be tokenized due to invalid characters instead of aborting CPD. + By default, CPD analysis is stopped on the first error. This is deprecated. Use `--fail-on-error` instead." %} {% include custom/cli_option_row.html options="--format,-f" option_arg="format" @@ -150,6 +150,11 @@ exactly identical. If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered as absolute." %} + {% include custom/cli_option_row.html options="--[no-]fail-on-error" + description="Specifies whether CPD exits with non-zero status if recoverable errors occurred. + By default CPD exits with status 5 if recoverable errors occurred (whether there are duplications or not). + Disable this option with `--no-fail-on-error` to exit with 0 instead. In any case, a report with the found duplications will be written." + %} {% include custom/cli_option_row.html options="--[no-]fail-on-violation" description="Specifies whether CPD exits with non-zero status if violations are found. By default CPD exits with status 4 if violations are found. @@ -279,16 +284,22 @@ If you specify a source directory but don't want to scan the sub-directories, yo ### Exit status -Please note that if CPD detects duplicated source code, it will exit with status 4 (since 5.0). +Please note that if CPD detects duplicated source code, it will exit with status 4 (since 5.0) or 5 (since 7.3.0). This behavior has been introduced to ease CPD integration into scripts or hooks, such as SVN hooks. - + - + +
0Everything is fine, no code duplications found.
0Everything is fine, no code duplications found and no recoverable errors occurred.
1CPD exited with an exception.
2Usage error. Command-line parameters are invalid or missing.
4At least one code duplication has been detected unless --no-fail-on-violation is set.
4At least one code duplication has been detected unless --no-fail-on-violation is set.

Since PMD 5.0.

5At least one recoverable error has occurred. There might be additionally zero or more duplications detected. + To ignore recoverable errors, use --no-fail-on-error.

Since PMD 7.3.0.

+{%include note.html content="If PMD exits with 5, then PMD had trouble lexing one or more files. +That means, that no duplications for the entire file are reported. This can be considered as false-negative. +In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report." %} + ## Logging PMD internally uses [slf4j](https://www.slf4j.org/) and ships with slf4j-simple as the logging implementation. @@ -390,6 +401,10 @@ Andy Glover wrote an Ant task for CPD; here's how to use it: keep their encoding.
If not specified, CPD uses the system default encoding." %} + {% include custom/cli_option_row.html options="failOnError" + description="Whether to fail the build if any errors occurred while processing the files. Since PMD 7.3.0." + default="true" + %} {% include custom/cli_option_row.html options="format" description="The format of the report (e.g. `csv`, `text`, `xml`)." default="text" @@ -424,8 +439,10 @@ Andy Glover wrote an Ant task for CPD; here's how to use it: default="false" %} {% include custom/cli_option_row.html options="skipLexicalErrors" - description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD." - default="false" + description="Deprecated Skip files which can't be tokenized + due to invalid characters instead of aborting CPD. This parameter is deprecated and + ignored since PMD 7.3.0. It is now by default true. Use `failOnError` instead to fail the build." + default="true" %} {% include custom/cli_option_row.html options="skipBlocks" description="Enables or disabled skipping of blocks like a pre-processor. See also option skipBlocksPattern." diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index f7e1b9d203..50049d0ef8 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -4,6 +4,7 @@ tags: [pmd, userdocs] summary: "Migrating to PMD 7 from PMD 6.x" permalink: pmd_userdocs_migrating_to_pmd7.html author: Andreas Dangel +last_updated: June 2024 (7.3.0) --- {% include important.html content=" @@ -3363,8 +3364,9 @@ See the use case [I'm using only built-in rules](#im-using-only-built-in-rules) #### Maven -* Due to some changes in PMD's API, you can't simply pull in the new PMD 7 dependency. -* See [Using PMD 7 with maven-pmd-plugin](pmd_userdocs_tools_maven.html#using-pmd-7-with-maven-pmd-plugin). +* Since maven-pmd-plugin 3.22.0, PMD 7 is supported directly. +* See [MPMD-379](https://issues.apache.org/jira/browse/MPMD-379) +* See [Using PMD 7 with maven-pmd-plugin](pmd_userdocs_tools_maven.html#using-pmd-7-with-maven-pmd-plugin) #### Gradle diff --git a/docs/pages/pmd/userdocs/tools/ant.md b/docs/pages/pmd/userdocs/tools/ant.md index 4c81ff7daf..b37ea8ad5f 100644 --- a/docs/pages/pmd/userdocs/tools/ant.md +++ b/docs/pages/pmd/userdocs/tools/ant.md @@ -6,6 +6,7 @@ author: > David Dixon-Peugh , Tom Copeland , Xavier Le Vourch +last_updated: June 2024 (7.3.0) --- ## PMD @@ -63,8 +64,8 @@ The examples below won't repeat this taskdef element, as this is always required Yes, unless the ruleset nested element is used - failonerror - Whether or not to fail the build if any errors occur while processing the files + failOnError + Whether or not to fail the build if any recoverable errors occurred while analyzing files. No diff --git a/docs/pages/pmd/userdocs/tools/maven.md b/docs/pages/pmd/userdocs/tools/maven.md index be76561367..73e7562ee9 100644 --- a/docs/pages/pmd/userdocs/tools/maven.md +++ b/docs/pages/pmd/userdocs/tools/maven.md @@ -2,8 +2,8 @@ title: Maven PMD Plugin tags: [userdocs, tools] permalink: pmd_userdocs_tools_maven.html -last_updated: February 2024 -mpmd_version: 3.21.2 +last_updated: June 2024 (7.3.0) +mpmd_version: 3.23.0 author: > Miguel Griffa , Romain PELISSE , @@ -72,7 +72,7 @@ This will add an entry to the 'project reports' section with the PMD report when To run PMD on a Maven project without adding it as a report, simply run - mvn pmd:pmd + mvn complile pmd:pmd The PMD plugin writes the report in XML which will then be formatted into more readable HTML. @@ -241,47 +241,25 @@ Maven plugin will use and benefit from the latest bugfixes and enhancements: #### Using PMD 7 with maven-pmd-plugin -The Maven PMD plugin comes with a specific PMD version, which is documented on the +Since version 3.22.0 ([MPMD-379](https://issues.apache.org/jira/browse/MPMD-379)), maven-pmd-plugin uses +by default now PMD 7.0.0 and no extra configuration is required. + +The specific PMD version used by maven-pmd-plugin might change. The exact version is documented on the [plugin project page](https://maven.apache.org/plugins/maven-pmd-plugin/index.html). -This might not support PMD 7 out of the box. -Since PMD 7 is a major release which breaks compatibility in various ways, the solution described -above in [Upgrading the PMD version at runtime](#upgrading-the-pmd-version-at-runtime) doesn't work -directly. +In order to use newer versions of PMD 7, you can simply follow the guide +[Upgrading PMD at Runtime](https://maven.apache.org/plugins/maven-pmd-plugin/examples/upgrading-PMD-at-runtime.html). -In order to use PMD 7 with [maven-pmd-plugin](https://maven.apache.org/plugins/maven-pmd-plugin/) a new -compatibility module has been created. This allows to use PMD 7 by simply adding one additional dependency: - -1. Follow the guide [Upgrading PMD at Runtime](https://maven.apache.org/plugins/maven-pmd-plugin/examples/upgrading-PMD-at-runtime.html) -2. Add additionally the following dependency: - -```xml - - net.sourceforge.pmd - pmd-compat6 - ${pmdVersion} - -``` - -It is important to add this dependency as the **first** in the list, so that maven-pmd-plugin sees the (old) -compatible versions of some classes. - -This module is available beginning with version 7.0.0-rc4 and will be there at least for the first -final version PMD 7 (7.0.0). It's not decided yet, whether we will keep updating it, after PMD 7 is finally -released. - -Note: This compatibility module only works for the built-in rules, that are still available in PMD 7. E.g. you need -to review your rulesets and look out for deprecated rules and such. See the use case -[I'm using only built-in rules](pmd_userdocs_migrating_to_pmd7.html#im-using-only-built-in-rules) -in the [Migration Guide for PMD 7](pmd_userdocs_migrating_to_pmd7.html). +Note: If you upgrade from Maven PMD Plugin before 3.22.0 you are most likely updating from PMD 6 to PMD 7. +This upgrade is a major version change. If you use the default ruleset from Maven PMD Plugin, then everything should +just work. But if you use a custom ruleset, you most likely need to review your ruleset and migrate it to PMD 7. +Rules might have been renamed or replaced. See [Detailed Release Notes for PMD 7](pmd_release_notes_pmd7.html) +and [Migration Guide for PMD 7](pmd_userdocs_migrating_to_pmd7.html). As PMD 7 revamped the Java module, if you have custom rules, you need to migrate these rules. See the use case [I'm using custom rules](pmd_userdocs_migrating_to_pmd7.html#im-using-custom-rules) in the Migration Guide. - - - ### Reference For more information, please see the well documented PMD plugin project page here: diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bbe210bcce..68fffd2835 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,46 +15,13 @@ This is a {{ site.pmd.release_type }} release. ### πŸš€ New and noteworthy ### πŸ› Fixed Issues -* core - * [#4992](https://github.com/pmd/pmd/pull/4992): \[core] CPD: Include processing errors in XML report -* apex - * [#4922](https://github.com/pmd/pmd/issues/4922): \[apex] SOQL syntax error with TYPEOF in sub-query - * [#5053](https://github.com/pmd/pmd/issues/5053): \[apex] CPD fails to parse string literals with escaped characters - * [#5055](https://github.com/pmd/pmd/issues/5055): \[apex] SOSL syntax error with WITH USER_MODE or WITH SYSTEM_MODE -* apex-bestpractices - * [#5000](https://github.com/pmd/pmd/issues/5000): \[apex] UnusedLocalVariable FP with binds in SOSL / SOQL -* java-bestpractices - * [#5047](https://github.com/pmd/pmd/issues/5047): \[java] UnusedPrivateMethod FP for Generics & Overloads * plsql - * [#1934](https://github.com/pmd/pmd/issues/1934): \[plsql] ParseException with MERGE statement in anonymous block - * [#2779](https://github.com/pmd/pmd/issues/2779): \[plsql] Error while parsing statement with (Oracle) DML Error Logging + * [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage ### 🚨 API Changes -#### CPD Report Format XML - -There are some important changes: - -1. The XML format will now use an XSD schema, that is available at . - This schema defines the valid elements and attributes that one can expect from a CPD report. -2. The root element `pmd-cpd` contains the new attributes `pmdVersion`, `timestamp` and `version`. The latter is - the schema version and is currently "1.0.0". -3. The CPD XML report will now also contain recoverable errors as additional `` elements. - -See [Report formats for CPD](pmd_userdocs_cpd_report_formats.html#xml) for an example. - -The XML format should be compatible as only attributes and elements have been added. However, if you parse -the document with a namespace aware parser, you might encounter some issues like no elements being found. -In case the new format doesn't work for you (e.g. namespaces, unexpected error elements), you can -go back using the old format with the renderer "xmlold" ({%jdoc core::cpd.XMLOldRenderer %}). Note, that -this old renderer is deprecated and only there for compatibility reasons. Whatever tooling is used to -read the XML format should be updated. - -#### Deprecated for removal - -* {%jdoc !!core::cpd.XMLOldRenderer %} (the CPD format "xmlold"). - ### ✨ External Contributions +* [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage - [Arjen Duursma](https://github.com/duursma) (@duursma) {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 4bd8722d4a..7e5f11b546 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,149 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: [Releases - pmd/pmd (GitHub)](https://github.com/pmd/pmd/releases) + + +## 28-June-2024 - 7.3.0 + +The PMD team is pleased to announce PMD 7.3.0. + +This is a minor release. + +### Table Of Contents + +* [πŸš€ New and noteworthy](#new-and-noteworthy) + * [✨ New Rules](#new-rules) + * [πŸ’₯ pmd-compat6 removed (breaking)](#pmd-compat6-removed-breaking) +* [πŸ› Fixed Issues](#fixed-issues) +* [🚨 API Changes](#api-changes) + * [CPD Report Format XML](#cpd-report-format-xml) + * [CLI](#cli) + * [Ant](#ant) + * [Deprecated API](#deprecated-api) + * [Breaking changes: pmd-compat6 removed](#breaking-changes-pmd-compat6-removed) +* [πŸ“ˆ Stats](#stats) + +### πŸš€ New and noteworthy + +#### ✨ New Rules + +* The new Java rule [`UseEnumCollections`](https://docs.pmd-code.org/pmd-doc-7.3.0/pmd_rules_java_bestpractices.html#useenumcollections) reports usages for `HashSet` and `HashMap` + when the keys are of an enum type. The specialized enum collections are more space- and time-efficient. + +#### πŸ’₯ pmd-compat6 removed (breaking) + +The already deprecated PMD 6 compatibility module (pmd-compat6) has been removed. It was intended to be used with +older versions of the maven-pmd-plugin, but since maven-pmd-plugin 3.22.0, PMD 7 is supported directly and this +module is not needed anymore. + +If you currently use this dependency (`net.sourceforge.pmd:pmd-compat6`), remove it and upgrade maven-pmd-plugin +to the latest version (3.23.0 or newer). + +See also [Maven PMD Plugin](https://docs.pmd-code.org/pmd-doc-7.3.0/pmd_userdocs_tools_maven.html). + +### πŸ› Fixed Issues + +* cli + * [#2827](https://github.com/pmd/pmd/issues/2827): \[cli] Consider processing errors in exit status +* core + * [#4396](https://github.com/pmd/pmd/issues/4396): \[core] CPD is always case sensitive + * [#4992](https://github.com/pmd/pmd/pull/4992): \[core] CPD: Include processing errors in XML report + * [#5066](https://github.com/pmd/pmd/issues/5066): \[core] CPD throws java.lang.OutOfMemoryError: Java heap space (since 7.1.0) +* apex + * [#4922](https://github.com/pmd/pmd/issues/4922): \[apex] SOQL syntax error with TYPEOF in sub-query + * [#5053](https://github.com/pmd/pmd/issues/5053): \[apex] CPD fails to parse string literals with escaped characters + * [#5055](https://github.com/pmd/pmd/issues/5055): \[apex] SOSL syntax error with WITH USER_MODE or WITH SYSTEM_MODE +* apex-bestpractices + * [#5000](https://github.com/pmd/pmd/issues/5000): \[apex] UnusedLocalVariable FP with binds in SOSL / SOQL +* java + * [#4885](https://github.com/pmd/pmd/issues/4885): \[java] AssertionError: Method should be accessible + * [#5050](https://github.com/pmd/pmd/issues/5050): \[java] Problems with pattern variables in switch branches +* java-bestpractices + * [#577](https://github.com/pmd/pmd/issues/577): \[java] New Rule: Check that Map is an EnumMap if K is an enum value + * [#5047](https://github.com/pmd/pmd/issues/5047): \[java] UnusedPrivateMethod FP for Generics & Overloads +* plsql + * [#1934](https://github.com/pmd/pmd/issues/1934): \[plsql] ParseException with MERGE statement in anonymous block + * [#2779](https://github.com/pmd/pmd/issues/2779): \[plsql] Error while parsing statement with (Oracle) DML Error Logging + * [#4270](https://github.com/pmd/pmd/issues/4270): \[plsql] Parsing exception COMPOUND TRIGGER with EXCEPTION handler + +### 🚨 API Changes + +#### CPD Report Format XML + +There are some important changes: + +1. The XML format will now use an XSD schema, that is available at . + This schema defines the valid elements and attributes that one can expect from a CPD report. +2. The root element `pmd-cpd` contains the new attributes `pmdVersion`, `timestamp` and `version`. The latter is + the schema version and is currently "1.0.0". +3. The CPD XML report will now also contain recoverable errors as additional `` elements. + +See [Report formats for CPD](pmd_userdocs_cpd_report_formats.html#xml) for an example. + +The XML format should be compatible as only attributes and elements have been added. However, if you parse +the document with a namespace aware parser, you might encounter some issues like no elements being found. +In case the new format doesn't work for you (e.g. namespaces, unexpected error elements), you can +go back using the old format with the renderer "xmlold" (XMLOldRenderer). Note, that +this old renderer is deprecated and only there for compatibility reasons. Whatever tooling is used to +read the XML format should be updated. + +#### CLI + +* New exit code 5 introduced. PMD and CPD will exit now by default with exit code 5, if any recoverable error + (e.g. parsing exception, lexing exception or rule exception) occurred. PMD will still create a report with + all detected violations or duplications if recoverable errors occurred. Such errors mean, that the report + might be incomplete, as either violations or duplications for an entire file or for a specific rule are missing. + These cases can be considered as false-negatives. + + In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report. + +* New CLI parameter `--no-fail-on-error` to ignore such errors and not exit with code 5. By default, + a build with errors will now fail and with that parameter, the previous behavior can be restored. + This parameter is available for both PMD and CPD. + +* The CLI parameter `--skip-lexical-errors` is deprecated. By default, lexical errors are skipped but the + build is failed. Use the new parameter `--[no-]fail-on-error` instead to control whether to fail the build or not. + +#### Ant + +* CPDTask has a new parameter `failOnError`. It controls, whether to fail the build if any recoverable error occurred. + By default, the build will fail. CPD will still create a report with all detected duplications, but the report might + be incomplete. +* The parameter `skipLexicalError` in CPDTask is deprecated and ignored. Lexical errors are now always skipped. + Use the new parameter `failOnError` instead to control whether to fail the build or not. + +#### Deprecated API + +* pmd-ant + * CPDTask#setSkipLexicalErrors: Use setFailOnError + instead to control, whether to ignore errors or fail the build. +* pmd-core + * CPDConfiguration#isSkipLexicalErrors and setSkipLexicalErrors: + Use setFailOnError to control whether to ignore errors or fail the build. + * net.sourceforge.pmd.cpd.XMLOldRenderer (the CPD format "xmlold"). + * The constructor + AntlrToken#AntlrToken + shouldn't be used directly. Use AntlrTokenManager instead. +* pmd-java + * ASTResource#getStableName and the corresponding attribute `@StableName`. + * ASTRecordPattern#getVarId This method was added here by mistake. Record + patterns don't declare a pattern variable for the whole pattern, but rather for individual record + components, which can be accessed via getComponentPatterns. +* pmd-plsql + * PLSQLParserImpl is deprecated now. It should have been package-private + because this is an implementation class that should not be used directly. + * The node ASTKEYWORD_UNRESERVED is deprecated and is now removed from the AST. + +#### Breaking changes: pmd-compat6 removed + +The already deprecated PMD 6 compatibility module (pmd-compat6) has been removed. +See above for details. + +### πŸ“ˆ Stats +* 88 commits +* 32 closed tickets & PRs +* Days since last release: 27 + ## 31-May-2024 - 7.2.0 The PMD team is pleased to announce PMD 7.2.0. diff --git a/mvnw b/mvnw index 5643201c7d..19529ddf8c 100755 --- a/mvnw +++ b/mvnw @@ -19,298 +19,241 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; esac -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" else JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi fi else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done - echo "${basedir}" + printf %x\\n $h } -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 } -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` + die "cannot create temp dir" fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS +mkdir -p -- "${MAVEN_HOME%/*}" -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 8a15b7f311..249bdf3822 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,3 +1,4 @@ +<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -18,171 +19,131 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM ---------------------------------------------------------------------------- -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) ) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" -:error -set ERROR_CODE=1 +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" -cmd /C exit /B %ERROR_CODE% +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pmd-ant/pom.xml b/pmd-ant/pom.xml index dc93238cd3..2d24dc491c 100644 --- a/pmd-ant/pom.xml +++ b/pmd-ant/pom.xml @@ -7,7 +7,7 @@ pmd net.sourceforge.pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT 4.0.0 diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index 58bf8967d5..c812893930 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -78,6 +78,7 @@ public class CPDTask extends Task { private boolean ignoreIdentifiers; private boolean ignoreAnnotations; private boolean ignoreUsings; + @Deprecated private boolean skipLexicalErrors; private boolean skipDuplicateFiles; private boolean skipBlocks = true; @@ -85,6 +86,7 @@ public class CPDTask extends Task { private File outputFile; private String encoding = System.getProperty("file.encoding"); private List filesets = new ArrayList<>(); + private boolean failOnError = true; @Override public void execute() throws BuildException { @@ -102,7 +104,15 @@ public class CPDTask extends Task { config.setOnlyRecognizeLanguage(config.getLanguageRegistry().getLanguageById(language)); config.setSourceEncoding(Charset.forName(encoding)); config.setSkipDuplicates(skipDuplicateFiles); - config.setSkipLexicalErrors(skipLexicalErrors); + + if (skipLexicalErrors) { + log("skipLexicalErrors is deprecated since 7.3.0 and the property is ignored. " + + "Lexical errors are now skipped by default and the build is failed. " + + "Use failOnError=\"false\" to not fail the build.", Project.MSG_WARN); + } + + // implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case. + config.setSkipLexicalErrors(true); config.setIgnoreAnnotations(ignoreAnnotations); config.setIgnoreLiterals(ignoreLiterals); @@ -121,12 +131,20 @@ public class CPDTask extends Task { long timeTaken = System.currentTimeMillis() - start; log("Done analyzing code; that took " + timeTaken + " milliseconds"); + int errors = config.getReporter().numErrors(); + if (errors > 0) { + String message = String.format("There were %d recovered errors during analysis.", errors); + if (failOnError) { + throw new BuildException(message + " Ignore these with failOnError=\"true\"."); + } else { + log(message + " Not failing build, because failOnError=\"false\".", Project.MSG_WARN); + } + } } } catch (IOException ioe) { log(ioe.toString(), Project.MSG_ERR); throw new BuildException("IOException during task execution", ioe); } catch (ReportException re) { - re.printStackTrace(); log(re.toString(), Project.MSG_ERR); throw new BuildException("ReportException during task execution", re); } finally { @@ -225,6 +243,10 @@ public class CPDTask extends Task { this.ignoreUsings = value; } + /** + * @deprecated Use {@link #setFailOnError(boolean)} instead. + */ + @Deprecated public void setSkipLexicalErrors(boolean skipLexicalErrors) { this.skipLexicalErrors = skipLexicalErrors; } @@ -257,6 +279,15 @@ public class CPDTask extends Task { this.skipBlocksPattern = skipBlocksPattern; } + /** + * Whether to fail the build if any recoverable errors occurred while processing the files. + * + * @since 7.3.0 + */ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + public static class FormatAttribute extends EnumeratedAttribute { private static final String[] FORMATS = new String[] { XML_FORMAT, TEXT_FORMAT, CSV_FORMAT, XMLOLD_FORMAT }; diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index a4be75fb15..5f5e1f9bd9 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -144,7 +144,7 @@ public class PMDTaskImpl { pmd.performAnalysis(); stats = reportStatsListener.getResult(); if (failOnError && pmd.getReporter().numErrors() > 0) { - throw new BuildException("Some errors occurred while running PMD"); + throw new BuildException("Some recoverable errors occurred while running PMD"); } } diff --git a/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java b/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java index 79b50a0dee..83b56f8781 100644 --- a/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java +++ b/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.ant; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; @@ -14,6 +15,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import org.apache.tools.ant.BuildException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,7 +36,25 @@ class CPDTaskTest extends AbstractAntTest { @Test void testBasic() throws IOException { executeTarget("testBasic"); - Path report = Paths.get("target/cpd.ant.tests"); + assertReport("target/cpd.ant.tests"); + } + + @Test + void failOnErrorDefault() throws IOException { + BuildException buildException = assertThrows(BuildException.class, () -> executeTarget("failOnErrorDefault")); + assertThat(buildException.getMessage(), containsString("There were 1 recovered errors during analysis.")); + assertReport("target/cpd.ant.tests"); + } + + @Test + void failOnErrorIgnore() throws IOException { + executeTarget("failOnErrorIgnore"); + assertReport("target/cpd.ant.tests"); + assertThat(log.toString(), containsString("There were 1 recovered errors during analysis.")); + } + + private static void assertReport(String path) throws IOException { + Path report = Paths.get(path); assertTrue(Files.exists(report), "Report was not created"); String reportContent = IOUtil.readFileToString(report.toFile(), StandardCharsets.UTF_8); assertThat(reportContent, containsString("Found a 1 line (21 tokens) duplication in the following files:")); diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sampleLexError.dummy b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sampleLexError.dummy new file mode 100644 index 0000000000..3175abe874 --- /dev/null +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sampleLexError.dummy @@ -0,0 +1 @@ +:throw_lex_source_exception: diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml index df4f993f92..922e4a2a9a 100644 --- a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml @@ -11,7 +11,24 @@ + + + + + + + + + + + + + + + + + diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml index 025ce8d6d5..e7895c7f55 100644 --- a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml @@ -16,4 +16,12 @@ Test Rule 2 Test Rule 3 + + +Test Rule 2 + + +Test Rule 3 + + diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index e3b9f32e27..a89fbebb49 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-cli/pom.xml b/pmd-cli/pom.xml index e971574645..415b1a72eb 100644 --- a/pmd-cli/pom.xml +++ b/pmd-cli/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java index e45086a60a..02a090ab3f 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java @@ -43,11 +43,17 @@ public abstract class AbstractAnalysisPmdSubcommand relativizeRootPaths; @Option(names = { "--relativize-paths-with", "-z"}, description = "Path relative to which directories are rendered in the report. " diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index f15328047b..fe8a835774 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -68,8 +68,12 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand @Option(names = "--ignore-sequences", description = "Ignore sequences of identifiers and literals") private boolean ignoreIdentifierAndLiteralSequences; + /** + * @deprecated Use {@link #failOnError} instead. + */ @Option(names = "--skip-lexical-errors", - description = "Skip files which can't be tokenized due to invalid characters, instead of aborting with an error.") + description = "Skip files which can't be tokenized due to invalid characters, instead of aborting with an error. Deprecated - use --[no-]fail-on-error instead.") + @Deprecated private boolean skipLexicalErrors; @Option(names = "--no-skip-blocks", @@ -103,6 +107,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand configuration.addRelativizeRoots(relativizeRootPaths); } configuration.setFailOnViolation(failOnViolation); + configuration.setFailOnError(failOnError); configuration.setInputFilePath(fileListPath); if (inputPaths != null) { configuration.setInputPathList(new ArrayList<>(inputPaths)); @@ -123,6 +128,14 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand configuration.setSourceEncoding(encoding.getEncoding()); configuration.setInputUri(uri); + if (skipLexicalErrors) { + configuration.getReporter().warn("--skip-lexical-errors is deprecated. Use --no-fail-on-error instead."); + configuration.setFailOnError(false); + } + + // implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case. + configuration.setSkipLexicalErrors(true); + return configuration; } @@ -133,6 +146,11 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand MutableBoolean hasViolations = new MutableBoolean(); cpd.performAnalysis(report -> hasViolations.setValue(!report.getMatches().isEmpty())); + boolean hasErrors = configuration.getReporter().numErrors() > 0; + if (hasErrors && configuration.isFailOnError()) { + return CliExitCode.RECOVERED_ERRORS_OR_VIOLATIONS; + } + if (hasViolations.booleanValue() && configuration.isFailOnViolation()) { return CliExitCode.VIOLATIONS_FOUND; } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java index 02a5804c98..f60632ba63 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java @@ -270,6 +270,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand configuration.setSuppressMarker(suppressMarker); configuration.setThreads(threads); configuration.setFailOnViolation(failOnViolation); + configuration.setFailOnError(failOnError); configuration.setAnalysisCacheLocation(cacheLocation != null ? cacheLocation.toString() : null); configuration.setIgnoreIncrementalAnalysis(noCache); @@ -330,6 +331,8 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand if (pmdReporter.numErrors() > 0) { // processing errors are ignored return CliExitCode.ERROR; + } else if (stats.getNumErrors() > 0 && configuration.isFailOnError()) { + return CliExitCode.RECOVERED_ERRORS_OR_VIOLATIONS; } else if (stats.getNumViolations() > 0 && configuration.isFailOnViolation()) { return CliExitCode.VIOLATIONS_FOUND; } else { diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/CliExitCode.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/CliExitCode.java index 16fabc270b..909f942c6c 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/CliExitCode.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/CliExitCode.java @@ -4,30 +4,39 @@ package net.sourceforge.pmd.cli.internal; -import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.AbstractConfiguration; /** * The execution result of any given command. */ public enum CliExitCode { - /** No errors, no violations. This is exit code {@code 0}. */ + /** No errors, no recoverable errors, no violations, no duplications. This is exit code {@code 0}. */ OK(0), /** - * Errors were detected, PMD may have not run to the end. + * Unexpected errors were detected, PMD may have not run to the end. * This is exit code {@code 1}. */ ERROR(1), /** * Indicates a problem with the CLI parameters: either a required - * parameter is missing or an invalid parameter was provided. + * parameter is missing or an invalid parameter was provided. This is exit code {@code 2}. */ USAGE_ERROR(2), /** - * No errors, but PMD found violations. This is exit code {@code 4}. - * This is only returned if {@link PMDConfiguration#isFailOnViolation()} - * is set (CLI flag {@code --failOnViolation}). + * No errors, but PMD found either duplications/violations. This is exit code {@code 4}. + * + *

This is only returned if {@link AbstractConfiguration#isFailOnViolation()} + * is set. It can be disabled by using CLI flag {@code --no-fail-on-violation}. */ - VIOLATIONS_FOUND(4); + VIOLATIONS_FOUND(4), + /** + * PMD did run, but there was at least one recoverable error. There + * might be additionally duplications or violations. This is exit code {@code 5}. + * + *

This is only returned if {@link AbstractConfiguration#isFailOnError()} + * is set. It can be disabled by using CLI flag {@code --no-fail-on-error}. + */ + RECOVERED_ERRORS_OR_VIOLATIONS(5); private final int exitCode; @@ -45,6 +54,7 @@ public enum CliExitCode { case 1: return ERROR; case 2: return USAGE_ERROR; case 4: return VIOLATIONS_FOUND; + case 5: return RECOVERED_ERRORS_OR_VIOLATIONS; default: throw new IllegalArgumentException("Not a known exit code: " + i); } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index b8388af890..3ff31a255d 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.cli; import static net.sourceforge.pmd.cli.internal.CliExitCode.OK; +import static net.sourceforge.pmd.cli.internal.CliExitCode.RECOVERED_ERRORS_OR_VIOLATIONS; import static net.sourceforge.pmd.cli.internal.CliExitCode.VIOLATIONS_FOUND; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.hamcrest.CoreMatchers.startsWith; @@ -138,14 +139,14 @@ class CpdCliTest extends BaseCliTest { @Test void testWrongCliOptionResultsInErrorLoggingAfterDir() throws Exception { // --ignore-identifiers doesn't take an argument anymore - it is interpreted as a file for inputPaths - final CliExecutionResult result = runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "false"); + final CliExecutionResult result = runCli(RECOVERED_ERRORS_OR_VIOLATIONS, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "false"); result.checkStdErr(containsString("No such file false")); } @Test void testWrongCliOptionResultsInErrorLoggingBeforeDir() throws Exception { // --ignore-identifiers doesn't take an argument anymore - it is interpreted as a file for inputPaths - final CliExecutionResult result = runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--ignore-identifiers", "false", "--dir", SRC_DIR); + final CliExecutionResult result = runCli(RECOVERED_ERRORS_OR_VIOLATIONS, "--minimum-tokens", "34", "--ignore-identifiers", "false", "--dir", SRC_DIR); result.checkStdErr(containsString("No such file false")); } @@ -162,7 +163,7 @@ class CpdCliTest extends BaseCliTest { */ @Test void testIgnoreIdentifiers() throws Exception { - runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "false", "--debug") + runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "--debug") .verify(result -> result.checkStdOut(containsString( "Found a 14 line (89 tokens) duplication" ))); @@ -242,6 +243,43 @@ class CpdCliTest extends BaseCliTest { }); } + @Test + void testExitCodeWithLexicalErrors() throws Exception { + runCli(RECOVERED_ERRORS_OR_VIOLATIONS, + "--minimum-tokens", "10", + "-d", Paths.get(BASE_RES_PATH, "badandgood", "BadFile.java").toString(), + "--format", "text") + .verify(r -> { + r.checkStdErr(containsPattern("Skipping file: Lexical error in file '.*?BadFile\\.java'")); + r.checkStdOut(emptyString()); + }); + } + + @Test + void testExitCodeWithLexicalErrorsNoFail() throws Exception { + runCli(OK, + "--minimum-tokens", "10", + "-d", Paths.get(BASE_RES_PATH, "badandgood", "BadFile.java").toString(), + "--format", "text", + "--no-fail-on-error") + .verify(r -> { + r.checkStdErr(containsPattern("Skipping file: Lexical error in file '.*?BadFile\\.java'")); + r.checkStdOut(emptyString()); + }); + } + + @Test + void testExitCodeWithLexicalErrorsAndSkipLexical() throws Exception { + runCli(OK, + "--minimum-tokens", "10", + "-d", Paths.get(BASE_RES_PATH, "badandgood", "BadFile.java").toString(), + "--format", "text", + "--skip-lexical-errors") + .verify(r -> { + r.checkStdErr(containsPattern("Skipping file: Lexical error in file .*?BadFile\\.java")); + r.checkStdOut(emptyString()); + }); + } @Test void jsShouldFindDuplicatesWithDifferentFileExtensions() throws Exception { diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java index 85958562fd..fd77c97b42 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cli; import static net.sourceforge.pmd.cli.internal.CliExitCode.ERROR; import static net.sourceforge.pmd.cli.internal.CliExitCode.OK; +import static net.sourceforge.pmd.cli.internal.CliExitCode.RECOVERED_ERRORS_OR_VIOLATIONS; import static net.sourceforge.pmd.cli.internal.CliExitCode.USAGE_ERROR; import static net.sourceforge.pmd.cli.internal.CliExitCode.VIOLATIONS_FOUND; import static net.sourceforge.pmd.util.CollectionUtil.listOf; @@ -318,6 +319,27 @@ class PmdCliTest extends BaseCliTest { )); } + @Test + void exitStatusWithErrors() throws Exception { + runCli(RECOVERED_ERRORS_OR_VIOLATIONS, "--use-version", "dummy-parserThrows", + "-d", srcDir.toString(), "-f", "text", "-R", RULESET_WITH_VIOLATION) + .verify(r -> { + r.checkStdOut(containsString("someSource.dummy\t-\tParseException: Parse exception: ohio")); + r.checkStdErr(containsString("An error occurred while executing PMD.")); + }); + } + + @Test + void exitStatusWithErrorsNoFail() throws Exception { + runCli(OK, "--use-version", "dummy-parserThrows", + "-d", srcDir.toString(), "-f", "text", "-R", RULESET_WITH_VIOLATION, + "--no-fail-on-error") + .verify(r -> { + r.checkStdOut(containsString("someSource.dummy\t-\tParseException: Parse exception: ohio")); + r.checkStdErr(containsString("An error occurred while executing PMD.")); + }); + } + @Test void testZipFileAsSource() throws Exception { Path zipArchive = createTemporaryZipArchive("sources.zip"); diff --git a/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/RuleSetWithViolations.xml b/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/RuleSetWithViolations.xml index 8049a8b63b..4243101b43 100644 --- a/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/RuleSetWithViolations.xml +++ b/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/RuleSetWithViolations.xml @@ -3,12 +3,12 @@ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> - Ruleset used by test RuleSetFactoryTest + Ruleset used by test net.sourceforge.pmd.cli.PmdCliTest + externalInfoUrl="${pmd.website.baseurl}/rules/test/RuleSetWithViolations.xml#ReportAllRootNodes"> Just for test 3 diff --git a/pmd-coco/pom.xml b/pmd-coco/pom.xml index bb12f86503..4dafe93c48 100644 --- a/pmd-coco/pom.xml +++ b/pmd-coco/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-compat6/README.md b/pmd-compat6/README.md deleted file mode 100644 index 4a11803caa..0000000000 --- a/pmd-compat6/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# pmd-compat6 - -This module contains classes from PMD6, that have been removed in PMD7 and also restores -some removed methods. - -The goal is, that PMD7 can be used with [Maven PMD Plugin](https://maven.apache.org/plugins/maven-pmd-plugin) -without any further changes to the plugin. - -The maven-pmd-plugin uses by default PMD Version 6.55.0, but it can be configured to -[Use a new PMD version at runtime](https://maven.apache.org/plugins/maven-pmd-plugin/examples/upgrading-PMD-at-runtime.html). - -Since PMD7 introduces many incompatible changes, another module is needed to restore -compatibility. This is this module. - -In order to use this compatibility module, it needs to be added as the _first_ dependency -when configuring maven-pmd-plugin. - -It is as simple as adding: - -```xml - - net.sourceforge.pmd - pmd-compat6 - ${pmdVersion} - -``` - -Note: The dependency "pmd-compat6" must be listed _first_ before pmd-core, pmd-java, and the others. - -Note: Once the default version of PMD is upgraded to PMD7 in maven-pmd-plugin -(see [MPMD-379](https://issues.apache.org/jira/projects/MPMD/issues/MPMD-379)), this -compatibility module is no longer needed. The module pmd-compat6 might not be maintaned then -any further, hence it is already declared as deprecated. - -The primary goal for this module is, to get maven-pmd-plugin working with PMD7. It might -be useful in other contexts, too, but no guarantee is given, that is works. - -No guarantee is given, that the (deprecated) module pmd-compat6 is being maintained over the -whole lifetime of PMD 7. diff --git a/pmd-compat6/pom.xml b/pmd-compat6/pom.xml deleted file mode 100644 index cf0c893b26..0000000000 --- a/pmd-compat6/pom.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - 4.0.0 - - - net.sourceforge.pmd - pmd - 7.3.0-SNAPSHOT - - - pmd-compat6 - PMD Compatibility Classes for PMD6 (deprecated) - - - ${project.version} - 3.21.2 - - - - - net.sourceforge.pmd - pmd-core - ${project.version} - - - net.sourceforge.pmd - pmd-java - ${project.version} - - - net.sourceforge.pmd - pmd-javascript - ${project.version} - - - net.sourceforge.pmd - pmd-jsp - ${project.version} - - - net.sourceforge.pmd - pmd-cs - ${project.version} - - - - - - - org.jboss.bridger - bridger - 1.6.Final - - - weave - process-classes - - transform - - - - - - org.apache.maven.plugins - maven-invoker-plugin - 3.6.0 - - ${project.build.directory}/it - src/it/settings.xml - ${project.build.directory}/local-repo - verify.bsh - true - - - - integration-test - - install - run - - - - - - - diff --git a/pmd-compat6/src/it/cpd-for-csharp/invoker.properties b/pmd-compat6/src/it/cpd-for-csharp/invoker.properties deleted file mode 100644 index 0d92d959f3..0000000000 --- a/pmd-compat6/src/it/cpd-for-csharp/invoker.properties +++ /dev/null @@ -1,2 +0,0 @@ -invoker.goals = verify -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/cpd-for-csharp/pom.xml b/pmd-compat6/src/it/cpd-for-csharp/pom.xml deleted file mode 100644 index d324ef4634..0000000000 --- a/pmd-compat6/src/it/cpd-for-csharp/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - cpd-for-csharp - 1.0-SNAPSHOT - - - 11 - 11 - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - csharp-cpd-check - - cpd-check - - - - - cs - 10 - - **/*.cs - - - ${basedir}/src/main/cs - - true - false - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-cs - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings1.cs b/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings1.cs deleted file mode 100644 index b36845bb8a..0000000000 --- a/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings1.cs +++ /dev/null @@ -1,12 +0,0 @@ -class Foo { - void bar() { - - var test = $@"test"; - var test2 = @$"test"; - - String query = - @"SELECT foo, bar - FROM table - WHERE id = 42"; - } -} diff --git a/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings2.cs b/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings2.cs deleted file mode 100644 index b36845bb8a..0000000000 --- a/pmd-compat6/src/it/cpd-for-csharp/src/main/cs/strings2.cs +++ /dev/null @@ -1,12 +0,0 @@ -class Foo { - void bar() { - - var test = $@"test"; - var test2 = @$"test"; - - String query = - @"SELECT foo, bar - FROM table - WHERE id = 42"; - } -} diff --git a/pmd-compat6/src/it/cpd-for-csharp/verify.bsh b/pmd-compat6/src/it/cpd-for-csharp/verify.bsh deleted file mode 100644 index f5db9350bd..0000000000 --- a/pmd-compat6/src/it/cpd-for-csharp/verify.bsh +++ /dev/null @@ -1,46 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (buildLog.contains("An API incompatibility was encountered while")) { - throw new RuntimeException("Executing failed due to API incompatibility"); -} - -if (!buildLog.contains("[INFO] CPD Failure: Found 12 lines of duplicated code at locations:")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} -File classA = new File("cpd-for-csharp/src/main/cs/strings1.cs"); -if (!buildLog.contains(classA + " line 1")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} -File classB = new File("cpd-for-csharp/src/main/cs/strings2.cs"); -if (!buildLog.contains(classA + " line 1")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} - -File cpdXmlReport = new File(basedir, "target/cpd.xml"); -if (!cpdXmlReport.exists()) { - throw new FileNotFoundException("Could not find cpd xml report: " + cpdXmlReport); -} -String cpdXml = readFile(cpdXmlReport); -if (!cpdXml.contains("")) { - throw new RuntimeException("Expected duplication has not been reported"); -} -if (!cpdXml.contains(classA + "\"/>")) { - throw new RuntimeException("Expected duplication has not been reported"); -} -if (!cpdXml.contains(classB + "\"/>")) { - throw new RuntimeException("Expected duplication has not been reported"); -} diff --git a/pmd-compat6/src/it/cpd-for-java/invoker.properties b/pmd-compat6/src/it/cpd-for-java/invoker.properties deleted file mode 100644 index a6149b5b14..0000000000 --- a/pmd-compat6/src/it/cpd-for-java/invoker.properties +++ /dev/null @@ -1,4 +0,0 @@ -invoker.goals.1 = verify -invoker.goals.2 = pmd:cpd-check -Dformat=csv -invoker.goals.3 = pmd:cpd-check -Dformat=txt -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/cpd-for-java/pom.xml b/pmd-compat6/src/it/cpd-for-java/pom.xml deleted file mode 100644 index 285c37467b..0000000000 --- a/pmd-compat6/src/it/cpd-for-java/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - cpd-for-java - 1.0-SNAPSHOT - - - 11 - 11 - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - java-cpd-check - - cpd-check - - - - - true - false - 5 - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassA.java b/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassA.java deleted file mode 100644 index 3df3dde7d9..0000000000 --- a/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassA.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.example; - -public class ClassA { - public int method1(int a, int b, int c) { - int d = (a + b + c + 1) * 10; - int e = (a + b + c - 1) * 5; - int f = (a + b + c); - return d * e * f + d + e + f; - } -} diff --git a/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassB.java b/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassB.java deleted file mode 100644 index 994e917441..0000000000 --- a/pmd-compat6/src/it/cpd-for-java/src/main/java/org/example/ClassB.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.example; - -public class ClassB { - public int method1(int a, int b, int c) { - int d = (a + b + c + 1) * 10; - int e = (a + b + c - 1) * 5; - int f = (a + b + c); - return d * e * f + d + e + f; - } -} diff --git a/pmd-compat6/src/it/cpd-for-java/verify.bsh b/pmd-compat6/src/it/cpd-for-java/verify.bsh deleted file mode 100644 index 8eaad55526..0000000000 --- a/pmd-compat6/src/it/cpd-for-java/verify.bsh +++ /dev/null @@ -1,62 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (buildLog.contains("An API incompatibility was encountered while")) { - throw new RuntimeException("Executing failed due to API incompatibility"); -} -if (!buildLog.contains("[INFO] CPD Failure: Found 8 lines of duplicated code at locations:")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} -File classA = new File("cpd-for-java/src/main/java/org/example/ClassA.java"); -if (!buildLog.contains(classA + " line 3")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} - -File cpdXmlReport = new File(basedir, "target/cpd.xml"); -if (!cpdXmlReport.exists()) { - throw new FileNotFoundException("Could not find cpd xml report: " + cpdXmlReport); -} -String cpdXml = readFile(cpdXmlReport); -if (!cpdXml.contains("")) { - throw new RuntimeException("Expected duplication has not been reported"); -} -if (!cpdXml.contains(classA + "\"/>")) { - throw new RuntimeException("Expected duplication has not been reported"); -} - -File csvReport = new File(basedir, "target/cpd.csv"); -if (!csvReport.exists()) { - throw new FileNotFoundException("Could not find cpd csv report: " + csvReport); -} -String csv = readFile(csvReport); -if (!csv.contains("8,67,2,3,")) { - throw new RuntimeException("Expected duplication in CSV has not been reported"); -} -if (!csv.contains(classA + ",")) { - throw new RuntimeException("Expected duplication in CSV has not been reported"); -} - -File textReport = new File(basedir, "target/cpd.txt"); -if (!textReport.exists()) { - throw new FileNotFoundException("Could not find cpd text report: " + textReport); -} -String text = readFile(textReport); -if (!text.contains("Found a 8 line (67 tokens) duplication in the following files:")) { - throw new RuntimeException("Expected duplication in TXT has not been reported"); -} -if (!text.contains("Starting at line 3 of ") && !text.contains(classA.toString())) { - throw new RuntimeException("Expected duplication in TXT has not been reported"); -} diff --git a/pmd-compat6/src/it/cpd-for-javascript/invoker.properties b/pmd-compat6/src/it/cpd-for-javascript/invoker.properties deleted file mode 100644 index 0d92d959f3..0000000000 --- a/pmd-compat6/src/it/cpd-for-javascript/invoker.properties +++ /dev/null @@ -1,2 +0,0 @@ -invoker.goals = verify -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/cpd-for-javascript/pom.xml b/pmd-compat6/src/it/cpd-for-javascript/pom.xml deleted file mode 100644 index a252b32a34..0000000000 --- a/pmd-compat6/src/it/cpd-for-javascript/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - cpd-for-javascript - 1.0-SNAPSHOT - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - javascript-cpd-check - - cpd-check - - - - - true - false - 5 - javascript - - /category/ecmascript/bestpractices.xml - - - **/*.js - - - ${basedir}/src/main/js - - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable.js b/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable.js deleted file mode 100644 index d9c86cf398..0000000000 --- a/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable.js +++ /dev/null @@ -1,7 +0,0 @@ -function(arg) { - notDeclaredVariable = 1; // this will create a global variable and trigger the rule - - var someVar = 1; // this is a local variable, that's ok - - window.otherGlobal = 2; // this will not trigger the rule, although it is a global variable. -} diff --git a/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable2.js b/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable2.js deleted file mode 100644 index d9c86cf398..0000000000 --- a/pmd-compat6/src/it/cpd-for-javascript/src/main/js/globalVariable2.js +++ /dev/null @@ -1,7 +0,0 @@ -function(arg) { - notDeclaredVariable = 1; // this will create a global variable and trigger the rule - - var someVar = 1; // this is a local variable, that's ok - - window.otherGlobal = 2; // this will not trigger the rule, although it is a global variable. -} diff --git a/pmd-compat6/src/it/cpd-for-javascript/verify.bsh b/pmd-compat6/src/it/cpd-for-javascript/verify.bsh deleted file mode 100644 index e31d6be8ee..0000000000 --- a/pmd-compat6/src/it/cpd-for-javascript/verify.bsh +++ /dev/null @@ -1,36 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (!buildLog.contains("[INFO] CPD Failure: Found 7 lines of duplicated code at locations:")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} -File globalVariable = new File("cpd-for-javascript/src/main/js/globalVariable.js"); -if (!buildLog.contains(globalVariable + " line 1")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} - -File cpdXmlReport = new File(basedir, "target/cpd.xml"); -if(!cpdXmlReport.exists()) -{ - throw new FileNotFoundException("Could not find cpd xml report: " + cpdXmlReport); -} -String cpdXml = readFile(cpdXmlReport); -if (!cpdXml.contains("")) { - throw new RuntimeException("Expected duplication has not been reported"); -} -if (!cpdXml.contains(globalVariable + "\"/>")) { - throw new RuntimeException("Expected duplication has not been reported"); -} diff --git a/pmd-compat6/src/it/cpd-for-jsp/invoker.properties b/pmd-compat6/src/it/cpd-for-jsp/invoker.properties deleted file mode 100644 index 0d92d959f3..0000000000 --- a/pmd-compat6/src/it/cpd-for-jsp/invoker.properties +++ /dev/null @@ -1,2 +0,0 @@ -invoker.goals = verify -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/cpd-for-jsp/pom.xml b/pmd-compat6/src/it/cpd-for-jsp/pom.xml deleted file mode 100644 index 1726d641d5..0000000000 --- a/pmd-compat6/src/it/cpd-for-jsp/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - cpd-for-jsp - 1.0-SNAPSHOT - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - jsp-cpd-check - - cpd-check - - - - - true - false - 5 - jsp - - /category/jsp/bestpractices.xml - - - **/*.jsp - - - ${basedir}/src/main/jsp - - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute.jsp b/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute.jsp deleted file mode 100644 index c86ca5aa9c..0000000000 --- a/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute.jsp +++ /dev/null @@ -1,3 +0,0 @@ - -

Some text

- diff --git a/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute2.jsp b/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute2.jsp deleted file mode 100644 index c86ca5aa9c..0000000000 --- a/pmd-compat6/src/it/cpd-for-jsp/src/main/jsp/classAttribute2.jsp +++ /dev/null @@ -1,3 +0,0 @@ - -

Some text

- diff --git a/pmd-compat6/src/it/cpd-for-jsp/verify.bsh b/pmd-compat6/src/it/cpd-for-jsp/verify.bsh deleted file mode 100644 index 17420241fe..0000000000 --- a/pmd-compat6/src/it/cpd-for-jsp/verify.bsh +++ /dev/null @@ -1,36 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (!buildLog.contains("[INFO] CPD Failure: Found 3 lines of duplicated code at locations:")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} -File classAttribute = new File("cpd-for-jsp/src/main/jsp/classAttribute.jsp"); -if (!buildLog.contains(classAttribute + " line 1")) { - throw new RuntimeException("No CPD failures detected, did CPD run?"); -} - -File cpdXmlReport = new File(basedir, "target/cpd.xml"); -if(!cpdXmlReport.exists()) -{ - throw new FileNotFoundException("Could not find cpd xml report: " + cpdXmlReport); -} -String cpdXml = readFile(cpdXmlReport); -if (!cpdXml.contains("")) { - throw new RuntimeException("Expected duplication has not been reported"); -} -if (!cpdXml.contains(classAttribute + "\"/>")) { - throw new RuntimeException("Expected duplication has not been reported"); -} diff --git a/pmd-compat6/src/it/pmd-for-java/config_error_ruleset.xml b/pmd-compat6/src/it/pmd-for-java/config_error_ruleset.xml deleted file mode 100644 index 1d646a3073..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/config_error_ruleset.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/pmd-compat6/src/it/pmd-for-java/exception_ruleset.xml b/pmd-compat6/src/it/pmd-for-java/exception_ruleset.xml deleted file mode 100644 index bd7427e5a8..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/exception_ruleset.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Use this rule to produce a processing error. - 3 - - - - - - - diff --git a/pmd-compat6/src/it/pmd-for-java/invoker.properties b/pmd-compat6/src/it/pmd-for-java/invoker.properties deleted file mode 100644 index d03166a079..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/invoker.properties +++ /dev/null @@ -1,4 +0,0 @@ -invoker.goals.1 = verify -e -invoker.goals.2 = pmd:check -Dformat=csv -invoker.goals.3 = pmd:check -Dformat=txt -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/pmd-for-java/pom.xml b/pmd-compat6/src/it/pmd-for-java/pom.xml deleted file mode 100644 index 13f0485c6f..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - pmd-for-java - 1.0-SNAPSHOT - - - 11 - 11 - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - java-check - - check - - - - - true - true - 5 - true - - /rulesets/java/maven-pmd-plugin-default.xml - ${project.basedir}/exception_ruleset.xml - ${project.basedir}/config_error_ruleset.xml - - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/pmd-for-java/src/main/java/org/example/Main.java b/pmd-compat6/src/it/pmd-for-java/src/main/java/org/example/Main.java deleted file mode 100644 index 8b4ddb6e37..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/src/main/java/org/example/Main.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.example; - -public class Main { - public static void main(String[] args) { - String thisIsAUnusedLocalVar = "a"; - System.out.println("Hello world!"); - - String thisIsASuppressedUnusedLocalVar = "b"; // NOPMD suppressed - } -} diff --git a/pmd-compat6/src/it/pmd-for-java/verify.bsh b/pmd-compat6/src/it/pmd-for-java/verify.bsh deleted file mode 100644 index 5e813cab8b..0000000000 --- a/pmd-compat6/src/it/pmd-for-java/verify.bsh +++ /dev/null @@ -1,59 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (buildLog.contains("An API incompatibility was encountered while")) { - throw new RuntimeException("Executing failed due to API incompatibility"); -} -if (!buildLog.contains("[INFO] PMD Failure: org.example.Main:5 Rule:UnusedLocalVariable")) { - throw new RuntimeException("No pmd violation detected, did PMD run?"); -} - -File pmdXmlReport = new File(basedir, "target/pmd.xml"); -if(!pmdXmlReport.exists()) { - throw new FileNotFoundException("Could not find pmd xml report: " + pmdXmlReport); -} -String pmdXml = readFile(pmdXmlReport); -if (!pmdXml.contains("")) { - throw new RuntimeException("Expected violation has not been reported"); -} -if (!pmdXml.contains("")) { - throw new RuntimeException("Configuration error has not been reported"); -} - -File pmdCsvReport = new File(basedir, "target/pmd.csv"); -if (!pmdCsvReport.exists()) { - throw new FileNotFoundException("Could not find pmd CSV report: " + pmdCsvReport); -} -String csvReport = readFile(pmdCsvReport); -if (!csvReport.contains(mainFile + "\",\"3\",\"5\",\"Avoid unused local")) { - throw new RuntimeException("Expected violation has not been reported in CSV"); -} - -File pmdTextReport = new File(basedir, "target/pmd.txt"); -if (!pmdTextReport.exists()) { - throw new FileNotFoundException("Could not find pmd TXT report: " + pmdTextReport); -} -String textReport = readFile(pmdTextReport); -if (!textReport.contains(mainFile + ":5:\tUnusedLocalVariable")) { - throw new RuntimeException("Expected violation has not been reported in TXT"); -} diff --git a/pmd-compat6/src/it/pmd-for-javascript/invoker.properties b/pmd-compat6/src/it/pmd-for-javascript/invoker.properties deleted file mode 100644 index 0d92d959f3..0000000000 --- a/pmd-compat6/src/it/pmd-for-javascript/invoker.properties +++ /dev/null @@ -1,2 +0,0 @@ -invoker.goals = verify -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/pmd-for-javascript/pom.xml b/pmd-compat6/src/it/pmd-for-javascript/pom.xml deleted file mode 100644 index e1fd383e42..0000000000 --- a/pmd-compat6/src/it/pmd-for-javascript/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - pmd-for-javascript - 1.0-SNAPSHOT - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - javascript-check - - check - - - - - true - false - 5 - javascript - - /category/ecmascript/bestpractices.xml - - - **/*.js - - - ${basedir}/src/main/js - - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/pmd-for-javascript/src/main/js/globalVariable.js b/pmd-compat6/src/it/pmd-for-javascript/src/main/js/globalVariable.js deleted file mode 100644 index d9c86cf398..0000000000 --- a/pmd-compat6/src/it/pmd-for-javascript/src/main/js/globalVariable.js +++ /dev/null @@ -1,7 +0,0 @@ -function(arg) { - notDeclaredVariable = 1; // this will create a global variable and trigger the rule - - var someVar = 1; // this is a local variable, that's ok - - window.otherGlobal = 2; // this will not trigger the rule, although it is a global variable. -} diff --git a/pmd-compat6/src/it/pmd-for-javascript/verify.bsh b/pmd-compat6/src/it/pmd-for-javascript/verify.bsh deleted file mode 100644 index 0d190f5f00..0000000000 --- a/pmd-compat6/src/it/pmd-for-javascript/verify.bsh +++ /dev/null @@ -1,33 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (!buildLog.contains("[INFO] PMD Failure: globalVariable.js:2 Rule:GlobalVariable")) { - throw new RuntimeException("No pmd violation detected, did PMD run?"); -} - -File pmdXmlReport = new File(basedir, "target/pmd.xml"); -if(!pmdXmlReport.exists()) -{ - throw new FileNotFoundException("Could not find pmd xml report: " + pmdXmlReport); -} -String pmdXml = readFile(pmdXmlReport); -if (!pmdXml.contains("")) { - throw new RuntimeException("Expected violation has not been reported"); -} diff --git a/pmd-compat6/src/it/pmd-for-jsp/invoker.properties b/pmd-compat6/src/it/pmd-for-jsp/invoker.properties deleted file mode 100644 index 0d92d959f3..0000000000 --- a/pmd-compat6/src/it/pmd-for-jsp/invoker.properties +++ /dev/null @@ -1,2 +0,0 @@ -invoker.goals = verify -invoker.buildResult = failure diff --git a/pmd-compat6/src/it/pmd-for-jsp/pom.xml b/pmd-compat6/src/it/pmd-for-jsp/pom.xml deleted file mode 100644 index 3784e88e69..0000000000 --- a/pmd-compat6/src/it/pmd-for-jsp/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - net.sourceforge.pmd.pmd-compat6.it - pmd-for-jsp - 1.0-SNAPSHOT - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-pmd-plugin - @maven-pmd-plugin.version.for.integrationtest@ - - - jsp-check - - check - - - - - true - false - 5 - jsp - - /category/jsp/bestpractices.xml - - - **/*.jsp - - - ${basedir}/src/main/jsp - - - - - net.sourceforge.pmd - pmd-compat6 - @project.version@ - - - net.sourceforge.pmd - pmd-core - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-java - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-javascript - @pmd.version.for.integrationtest@ - - - net.sourceforge.pmd - pmd-jsp - @pmd.version.for.integrationtest@ - - - - - - diff --git a/pmd-compat6/src/it/pmd-for-jsp/src/main/jsp/classAttribute.jsp b/pmd-compat6/src/it/pmd-for-jsp/src/main/jsp/classAttribute.jsp deleted file mode 100644 index c86ca5aa9c..0000000000 --- a/pmd-compat6/src/it/pmd-for-jsp/src/main/jsp/classAttribute.jsp +++ /dev/null @@ -1,3 +0,0 @@ - -

Some text

- diff --git a/pmd-compat6/src/it/pmd-for-jsp/verify.bsh b/pmd-compat6/src/it/pmd-for-jsp/verify.bsh deleted file mode 100644 index 55f734081e..0000000000 --- a/pmd-compat6/src/it/pmd-for-jsp/verify.bsh +++ /dev/null @@ -1,33 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -String readFile(File file) throws IOException { - StringBuilder content = new StringBuilder(); - for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { - content.append(line).append(System.lineSeparator()); - } - return content.toString(); -} - -File buildLogPath = new File(basedir, "build.log"); -String buildLog = readFile(buildLogPath); -if (!buildLog.contains("[INFO] PMD Failure: classAttribute.jsp:2 Rule:NoClassAttribute")) { - throw new RuntimeException("No pmd violation detected, did PMD run?"); -} - -File pmdXmlReport = new File(basedir, "target/pmd.xml"); -if(!pmdXmlReport.exists()) -{ - throw new FileNotFoundException("Could not find pmd xml report: " + pmdXmlReport); -} -String pmdXml = readFile(pmdXmlReport); -if (!pmdXml.contains("")) { - throw new RuntimeException("Expected violation has not been reported"); -} diff --git a/pmd-compat6/src/it/settings.xml b/pmd-compat6/src/it/settings.xml deleted file mode 100644 index b7724658de..0000000000 --- a/pmd-compat6/src/it/settings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - it-repo - - - local.central - @localRepositoryUrl@ - - true - - - true - - - - - - local.central - @localRepositoryUrl@ - - true - - - true - - - - - - - it-repo - - diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/PMDConfiguration.java deleted file mode 100644 index f058699d12..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ /dev/null @@ -1,604 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: -// - setSourceEncoding -// - setBenchmark (non-functional) -// - getMinimumPriority - -package net.sourceforge.pmd; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.Charset; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Properties; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.LoggerFactory; - -import net.sourceforge.pmd.annotation.DeprecatedUntil700; -import net.sourceforge.pmd.cache.internal.AnalysisCache; -import net.sourceforge.pmd.cache.internal.FileAnalysisCache; -import net.sourceforge.pmd.cache.internal.NoopAnalysisCache; -import net.sourceforge.pmd.internal.util.ClasspathClassLoader; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.rule.RuleSetLoader; -import net.sourceforge.pmd.renderers.Renderer; -import net.sourceforge.pmd.renderers.RendererFactory; -import net.sourceforge.pmd.util.AssertionUtil; -import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; - -/** - * This class contains the details for the runtime configuration of a - * PMD run. Once configured, use {@link PmdAnalysis#create(PMDConfiguration)} - * in a try-with-resources to execute the analysis (see {@link PmdAnalysis}). - * - *

Rulesets

- * - *
    - *
  • You can configure paths to the rulesets to use with {@link #addRuleSet(String)}. - * These can be file paths or classpath resources.
  • - *
  • Use {@link #setMinimumPriority(RulePriority)} to control the minimum priority a - * rule must have to be included. Defaults to the lowest priority, ie all rules are loaded.
  • - *
  • Use {@link #setRuleSetFactoryCompatibilityEnabled(boolean)} to disable the - * compatibility measures for removed and renamed rules in the rulesets that will - * be loaded. - *
- * - *

Source files

- * - *
    - *
  • The default encoding of source files is the system default as - * returned by System.getProperty("file.encoding"). - * You can set it with {@link #setSourceEncoding(Charset)}.
  • - *
  • The source files to analyze can be given in many ways. See - * {@link #addInputPath(Path)} {@link #setInputFilePath(Path)}, {@link #setInputUri(URI)}. - *
  • Files are assigned a language based on their name. The language - * version of languages can be given with - * {@link #setDefaultLanguageVersion(LanguageVersion)}. - * The default language assignment can be overridden with - * {@link #setForceLanguageVersion(LanguageVersion)}.
  • - *
- * - *

Rendering

- * - *
    - *
  • The renderer format to use for Reports. {@link #getReportFormat()}
  • - *
  • The file to which the Report should render. {@link #getReportFile()}
  • - *
  • Configure the root paths that are used to relativize file names in reports via {@link #addRelativizeRoot(Path)}. - * This enables to get short names in reports.
  • - *
  • The initialization properties to use when creating a Renderer instance. - * {@link #getReportProperties()}
  • - *
  • An indicator of whether to show suppressed Rule violations in Reports. - * {@link #isShowSuppressedViolations()}
  • - *
- * - *

Language configuration

- *
    - *
  • Use {@link #setSuppressMarker(String)} to change the comment marker for suppression comments. Defaults to {@value #DEFAULT_SUPPRESS_MARKER}.
  • - *
  • See {@link #setClassLoader(ClassLoader)} and {@link #prependAuxClasspath(String)} for - * information for how to configure classpath for Java analysis.
  • - *
  • You can set additional language properties with {@link #getLanguageProperties(Language)}
  • - *
- * - *

Miscellaneous

- *
    - *
  • Use {@link #setThreads(int)} to control the parallelism of the analysis. Defaults - * one thread per available processor. {@link #getThreads()}
  • - *
- */ -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"; - private Path reportFile; - - // General behavior options - private String suppressMarker = DEFAULT_SUPPRESS_MARKER; - private int threads = Runtime.getRuntime().availableProcessors(); - private ClassLoader classLoader = getClass().getClassLoader(); - - // Rule and source file options - private List ruleSets = new ArrayList<>(); - private RulePriority minimumPriority = RulePriority.LOW; - private boolean ruleSetFactoryCompatibilityEnabled = true; - - // Reporting options - private String reportFormat; - private Properties reportProperties = new Properties(); - private boolean showSuppressedViolations = false; - private boolean failOnViolation = true; - - private AnalysisCache analysisCache = new NoopAnalysisCache(); - private boolean ignoreIncrementalAnalysis; - - public PMDConfiguration() { - this(DEFAULT_REGISTRY); - } - - public PMDConfiguration(@NonNull LanguageRegistry languageRegistry) { - super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(PmdAnalysis.class))); - } - - /** - * Get the suppress marker. This is the source level marker used to indicate - * a RuleViolation should be suppressed. - * - * @return The suppress marker. - */ - public String getSuppressMarker() { - return suppressMarker; - } - - /** - * Set the suppress marker. - * - * @param suppressMarker - * The suppress marker to use. - */ - public void setSuppressMarker(String suppressMarker) { - Objects.requireNonNull(suppressMarker, "Suppress marker was null"); - this.suppressMarker = suppressMarker; - } - - /** - * Get the number of threads to use when processing Rules. - * - * @return The number of threads. - */ - public int getThreads() { - return threads; - } - - /** - * Set the number of threads to use when processing Rules. - * - * @param threads - * The number of threads. - */ - public void setThreads(int threads) { - this.threads = threads; - } - - /** - * Get the ClassLoader being used by PMD when processing Rules. - * - * @return The ClassLoader being used - */ - public ClassLoader getClassLoader() { - return classLoader; - } - - /** - * Set the ClassLoader being used by PMD when processing Rules. Setting a - * value of null will cause the default ClassLoader to be used. - * - * @param classLoader - * The ClassLoader to use - */ - public void setClassLoader(ClassLoader classLoader) { - if (classLoader == null) { - this.classLoader = getClass().getClassLoader(); - } else { - this.classLoader = classLoader; - } - } - - /** - * Prepend the specified classpath like string to the current ClassLoader of - * the configuration. If no ClassLoader is currently configured, the - * ClassLoader used to load the {@link PMDConfiguration} class will be used - * as the parent ClassLoader of the created ClassLoader. - * - *

If the classpath String looks like a URL to a file (i.e. starts with - * file://) the file will be read with each line representing - * an entry on the classpath.

- * - * @param classpath - * The prepended classpath. - * @throws IOException - * if the given classpath is invalid (e.g. does not exist) - * @see PMDConfiguration#setClassLoader(ClassLoader) - * @see ClasspathClassLoader - * - * @deprecated Use {@link #prependAuxClasspath(String)}, which doesn't - * throw a checked {@link IOException} - */ - @Deprecated - public void prependClasspath(String classpath) throws IOException { - try { - prependAuxClasspath(classpath); - } catch (IllegalArgumentException e) { - throw new IOException(e); - } - } - - /** - * Prepend the specified classpath like string to the current ClassLoader of - * the configuration. If no ClassLoader is currently configured, the - * ClassLoader used to load the {@link PMDConfiguration} class will be used - * as the parent ClassLoader of the created ClassLoader. - * - *

If the classpath String looks like a URL to a file (i.e. starts with - * file://) the file will be read with each line representing - * an entry on the classpath.

- * - *

You can specify multiple class paths separated by `:` on Unix-systems or `;` under Windows. - * See {@link File#pathSeparator}. - * - * @param classpath The prepended classpath. - * - * @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist) - * @see PMDConfiguration#setClassLoader(ClassLoader) - */ - public void prependAuxClasspath(String classpath) { - try { - if (classLoader == null) { - classLoader = PMDConfiguration.class.getClassLoader(); - } - if (classpath != null) { - classLoader = new ClasspathClassLoader(classpath, classLoader); - } - } catch (IOException e) { - // Note: IOExceptions shouldn't appear anymore, they should already be converted - // to IllegalArgumentException in ClasspathClassLoader. - throw new IllegalArgumentException(e); - } - } - - /** - * Get the comma separated list of RuleSet URIs. - * - * @return The RuleSet URIs. - * - * @deprecated Use {@link #getRuleSetPaths()} - */ - @Deprecated - @DeprecatedUntil700 - public @Nullable String getRuleSets() { - if (ruleSets.isEmpty()) { - return null; - } - return String.join(",", ruleSets); - } - - /** - * Returns the list of ruleset URIs. - * - * @see RuleSetLoader#loadFromResource(String) - */ - public @NonNull List<@NonNull String> getRuleSetPaths() { - return ruleSets; - } - - /** - * Sets the list of ruleset paths to load when starting the analysis. - * - * @param ruleSetPaths A list of ruleset paths, understandable by {@link RuleSetLoader#loadFromResource(String)}. - * - * @throws NullPointerException If the parameter is null - */ - public void setRuleSets(@NonNull List<@NonNull String> ruleSetPaths) { - AssertionUtil.requireParamNotNull("ruleSetPaths", ruleSetPaths); - AssertionUtil.requireContainsNoNullValue("ruleSetPaths", ruleSetPaths); - this.ruleSets = new ArrayList<>(ruleSetPaths); - } - - /** - * Add a new ruleset paths to load when starting the analysis. - * This list is initially empty. - * - * @param rulesetPath A ruleset path, understandable by {@link RuleSetLoader#loadFromResource(String)}. - * - * @throws NullPointerException If the parameter is null - */ - public void addRuleSet(@NonNull String rulesetPath) { - AssertionUtil.requireParamNotNull("rulesetPath", rulesetPath); - this.ruleSets.add(rulesetPath); - } - - /** - * Set the comma separated list of RuleSet URIs. - * - * @param ruleSets the rulesets to set - * - * @deprecated Use {@link #setRuleSets(List)} or {@link #addRuleSet(String)}. - */ - @Deprecated - @DeprecatedUntil700 - public void setRuleSets(@Nullable String ruleSets) { - if (ruleSets == null) { - this.ruleSets = new ArrayList<>(); - } else { - this.ruleSets = new ArrayList<>(Arrays.asList(ruleSets.split(","))); - } - } - - /** - * Get the minimum priority threshold when loading Rules from RuleSets. - * - * @return The minimum priority threshold. - */ - public RulePriority getMinimumPriority() { - return minimumPriority; - } - - /** - * Set the minimum priority threshold when loading Rules from RuleSets. - * - * @param minimumPriority - * The minimum priority. - */ - public void setMinimumPriority(RulePriority minimumPriority) { - this.minimumPriority = minimumPriority; - } - - public void setMinimumPriority(net.sourceforge.pmd.lang.rule.RulePriority mininumPriority) { - this.minimumPriority = RulePriority.valueOf(mininumPriority.name()); - } - - /** - * Create a Renderer instance based upon the configured reporting options. - * No writer is created. - * - * @return renderer - */ - public Renderer createRenderer() { - return createRenderer(false); - } - - /** - * Create a Renderer instance based upon the configured reporting options. - * If withReportWriter then we'll configure it with a writer for the - * reportFile specified. - * - * @param withReportWriter - * whether to configure a writer or not - * @return A Renderer instance. - */ - public Renderer createRenderer(boolean withReportWriter) { - Renderer renderer = RendererFactory.createRenderer(reportFormat, reportProperties); - renderer.setShowSuppressedViolations(showSuppressedViolations); - if (withReportWriter) { - renderer.setReportFile(getReportFile()); - } - return renderer; - } - - /** - * Get the report format. - * - * @return The report format. - */ - public String getReportFormat() { - return reportFormat; - } - - /** - * Set the report format. This should be a name of a Renderer. - * - * @param reportFormat - * The report format. - * - * @see Renderer - */ - public void setReportFormat(String reportFormat) { - this.reportFormat = reportFormat; - } - - /** - * Get whether the report should show suppressed violations. - * - * @return true if showing suppressed violations, - * false otherwise. - */ - public boolean isShowSuppressedViolations() { - return showSuppressedViolations; - } - - /** - * Set whether the report should show suppressed violations. - * - * @param showSuppressedViolations - * true if showing suppressed violations, - * false otherwise. - */ - public void setShowSuppressedViolations(boolean showSuppressedViolations) { - this.showSuppressedViolations = showSuppressedViolations; - } - - /** - * Get the Report properties. These are used to create the Renderer. - * - * @return The report properties. - */ - public Properties getReportProperties() { - return reportProperties; - } - - /** - * Set the Report properties. These are used to create the Renderer. - * - * @param reportProperties - * The Report properties to set. - */ - public void setReportProperties(Properties reportProperties) { - this.reportProperties = reportProperties; - } - - /** - * Whether PMD should exit with status 4 (the default behavior, true) if - * violations are found or just with 0 (to not break the build, e.g.). - * - * @return failOnViolation - */ - public boolean isFailOnViolation() { - return failOnViolation; - } - - /** - * Sets whether PMD should exit with status 4 (the default behavior, true) - * if violations are found or just with 0 (to not break the build, e.g.). - * - * @param failOnViolation - * failOnViolation - */ - public void setFailOnViolation(boolean failOnViolation) { - this.failOnViolation = failOnViolation; - } - - /** - * Checks if the rule set factory compatibility feature is enabled. - * - * @return true, if the rule set factory compatibility feature is enabled - * - */ - public boolean isRuleSetFactoryCompatibilityEnabled() { - return ruleSetFactoryCompatibilityEnabled; - } - - /** - * Sets the rule set factory compatibility feature enabled/disabled. - * - * @param ruleSetFactoryCompatibilityEnabled {@code true} if the feature should be enabled - * - */ - public void setRuleSetFactoryCompatibilityEnabled(boolean ruleSetFactoryCompatibilityEnabled) { - this.ruleSetFactoryCompatibilityEnabled = ruleSetFactoryCompatibilityEnabled; - } - - /** - * Retrieves the currently used analysis cache. Will never be null. - * - * @return The currently used analysis cache. Never null. - */ - public AnalysisCache getAnalysisCache() { - // Make sure we are not null - if (analysisCache == null || isIgnoreIncrementalAnalysis() && !(analysisCache instanceof NoopAnalysisCache)) { - // sets a noop cache - setAnalysisCache(new NoopAnalysisCache()); - } - - return analysisCache; - } - - /** - * Sets the analysis cache to be used. Setting a - * value of {@code null} will cause a Noop AnalysisCache to be used. - * If incremental analysis was explicitly disabled ({@link #isIgnoreIncrementalAnalysis()}), - * then this method is a noop. - * - * @param cache The analysis cache to be used. - */ - public void setAnalysisCache(final AnalysisCache cache) { - // the doc says it's a noop if incremental analysis was disabled, - // but it's actually the getter that enforces that - this.analysisCache = cache == null ? new NoopAnalysisCache() : cache; - } - - /** - * Sets the location of the analysis cache to be used. This will automatically configure - * and appropriate AnalysisCache implementation. - * - * @param cacheLocation The location of the analysis cache to be used. - */ - public void setAnalysisCacheLocation(final String cacheLocation) { - setAnalysisCache(cacheLocation == null - ? new NoopAnalysisCache() - : new FileAnalysisCache(new File(cacheLocation))); - } - - - /** - * Sets whether the user has explicitly disabled incremental analysis or not. - * If so, incremental analysis is not used, and all suggestions to use it are - * disabled. The analysis cached location is ignored, even if it's specified. - * - * @param noCache Whether to ignore incremental analysis or not - */ - public void setIgnoreIncrementalAnalysis(boolean noCache) { - // see #getAnalysisCache for the implementation. - this.ignoreIncrementalAnalysis = noCache; - } - - - /** - * Returns whether incremental analysis was explicitly disabled by the user - * or not. - * - * @return {@code true} if incremental analysis is explicitly disabled - */ - public boolean isIgnoreIncrementalAnalysis() { - return ignoreIncrementalAnalysis; - } - - - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - * @deprecated Use {@link #getReportFilePath()} - */ - @Deprecated - public String getReportFile() { - return reportFile == null ? null : reportFile.toString(); - } - - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - */ - public Path getReportFilePath() { - return reportFile; - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - * @deprecated Use {@link #setReportFile(Path)} - */ - @Deprecated - public void setReportFile(String reportFile) { - this.reportFile = reportFile == null ? null : Paths.get(reportFile); - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - */ - public void setReportFile(Path reportFile) { - this.reportFile = reportFile; - } - - // ------------------- compat extensions -------------------- - @Deprecated - public void setSourceEncoding(String sourceEncoding) { - setSourceEncoding(Charset.forName(Objects.requireNonNull(sourceEncoding))); - } - - @Deprecated - public void setBenchmark(boolean benchmark) { - // ignored - } - - // new method to be compatible with PMD 7 - RulePriority has changed package - public net.sourceforge.pmd.lang.rule.RulePriority getMinimumPriority$$bridge() { // SUPPRESS CHECKSTYLE ignore - return minimumPriority.asPMD7RulePriority(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java deleted file mode 100644 index 42b769b42a..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ /dev/null @@ -1,625 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: performAnalysisAndCollectReport, changes due to Report moved package - -package net.sourceforge.pmd; - -import static net.sourceforge.pmd.util.CollectionUtil.listOf; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.benchmark.TimedOperation; -import net.sourceforge.pmd.benchmark.TimedOperationCategory; -import net.sourceforge.pmd.cache.internal.AnalysisCacheListener; -import net.sourceforge.pmd.cache.internal.NoopAnalysisCache; -import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.util.ClasspathClassLoader; -import net.sourceforge.pmd.internal.util.FileCollectionUtil; -import net.sourceforge.pmd.internal.util.IOUtil; -import net.sourceforge.pmd.lang.InternalApiBridge; -import net.sourceforge.pmd.lang.JvmLanguagePropertyBundle; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageProcessor.AnalysisTask; -import net.sourceforge.pmd.lang.LanguageProcessorRegistry; -import net.sourceforge.pmd.lang.LanguageProcessorRegistry.LanguageTerminationException; -import net.sourceforge.pmd.lang.LanguagePropertyBundle; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.lang.document.FileCollector; -import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.lang.rule.RuleSet; -import net.sourceforge.pmd.lang.rule.RuleSetLoader; -import net.sourceforge.pmd.renderers.Renderer; -import net.sourceforge.pmd.reporting.ConfigurableFileNameRenderer; -import net.sourceforge.pmd.reporting.FileAnalysisListener; -import net.sourceforge.pmd.reporting.GlobalAnalysisListener; -import net.sourceforge.pmd.reporting.ListenerInitializer; -import net.sourceforge.pmd.reporting.Report; -import net.sourceforge.pmd.reporting.ReportStats; -import net.sourceforge.pmd.reporting.ReportStatsListener; -import net.sourceforge.pmd.util.AssertionUtil; -import net.sourceforge.pmd.util.StringUtil; -import net.sourceforge.pmd.util.log.PmdReporter; - -/** - * Main programmatic API of PMD. This is not a CLI entry point, see module - * {@code pmd-cli} for that. - * - *

Usage overview

- * - *

Create and configure a {@link PMDConfiguration}, - * then use {@link #create(PMDConfiguration)} to obtain an instance. - * You can perform additional configuration on the instance, e.g. adding - * files to process, or additional rulesets and renderers. Then, call - * {@link #performAnalysis()} or one of the related terminal methods. - * - *

Simple example

- * - *
{@code
- *   PMDConfiguration config = new PMDConfiguration();
- *   config.setDefaultLanguageVersion(LanguageRegistry.findLanguageByTerseName("java").getVersion("11"));
- *   config.addInputPath(Path.of("src/main/java"));
- *   config.prependClasspath("target/classes");
- *   config.setMinimumPriority(RulePriority.HIGH);
- *   config.addRuleSet("rulesets/java/quickstart.xml");
- *   config.setReportFormat("xml");
- *   config.setReportFile("target/pmd-report.xml");
- *
- *   try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
- *     // note: don't use `config` once a PmdAnalysis has been created.
- *     // optional: add more rulesets
- *     pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
- *     // optional: add more files
- *     pmd.files().addFile(Paths.get("src", "main", "more-java", "ExtraSource.java"));
- *     // optional: add more renderers
- *     pmd.addRenderer(renderer);
- *
- *     pmd.performAnalysis();
- *   }
- * }
- * - *

Rendering reports

- * - *

If you just want to render a report to a file like with the CLI, you - * should use a {@link Renderer}. You can add a custom one with {@link PmdAnalysis#addRenderer(Renderer)}. - * You can add one of the builtin renderers from its ID using {@link PMDConfiguration#setReportFormat(String)}. - * - *

Reports and events

- * - *

If you want strongly typed access to violations and other analysis events, - * you can implement and register a {@link GlobalAnalysisListener} with {@link #addListener(GlobalAnalysisListener)}. - * The listener needs to provide a new {@link FileAnalysisListener} for each file, - * which will receive events from the analysis. The listener's lifecycle - * happens only once the analysis is started ({@link #performAnalysis()}). - * - *

If you want access to all events once the analysis ends instead of processing - * events as they go, you can obtain a {@link Report} instance from {@link #performAnalysisAndCollectReport()}, - * or use {@link Report.GlobalReportBuilderListener} manually. Keep in - * mind collecting a report is less memory-efficient than using a listener. - * - *

If you want to process events in batches, one per file, you can - * use {@link Report.ReportBuilderListener}. to implement {@link GlobalAnalysisListener#startFileAnalysis(TextFile)}. - * - *

Listeners can be used alongside renderers. - * - *

Specifying the Java classpath

- * - *

Java rules work better if you specify the path to the compiled classes - * of the analysed sources. See {@link PMDConfiguration#prependAuxClasspath(String)}. - * - *

Customizing message output

- * - *

The analysis reports messages like meta warnings and errors through a - * {@link PmdReporter} instance. To override how those messages are output, - * you can set it in {@link PMDConfiguration#setReporter(PmdReporter)}. - * By default, it forwards messages to SLF4J. - * - */ -public final class PmdAnalysis implements AutoCloseable { - - private static final Logger LOG = LoggerFactory.getLogger(PmdAnalysis.class); - - private final FileCollector collector; - private final List renderers = new ArrayList<>(); - private final List listeners = new ArrayList<>(); - private final List ruleSets = new ArrayList<>(); - private final PMDConfiguration configuration; - private final PmdReporter reporter; - - private final Map langProperties = new HashMap<>(); - private boolean closed; - private final ConfigurableFileNameRenderer fileNameRenderer = new ConfigurableFileNameRenderer(); - - /** - * Constructs a new instance. The files paths (input files, filelist, - * exclude list, etc) given in the configuration are collected into - * the file collector ({@link #files()}), but more can be added - * programmatically using the file collector. - */ - private PmdAnalysis(PMDConfiguration config) { - this.configuration = config; - this.reporter = config.getReporter(); - this.collector = net.sourceforge.pmd.lang.document.InternalApiBridge.newCollector( - config.getLanguageVersionDiscoverer(), - reporter - ); - - } - - /** - * Constructs a new instance from a configuration. - * - *

    - *
  • The files paths (input files, filelist, - * exclude list, etc) are explored and the files to analyse are - * collected into the file collector ({@link #files()}). - * More can be added programmatically using the file collector. - *
  • The rulesets given in the configuration are loaded ({@link PMDConfiguration#getRuleSets()}) - *
  • A renderer corresponding to the parameters of the configuration - * is created and added (but not started). - *
- */ - public static PmdAnalysis create(PMDConfiguration config) { - PmdAnalysis pmd = new PmdAnalysis(config); - - // note: do not filter files by language - // they could be ignored later. The problem is if you call - // addRuleSet later, then you could be enabling new languages - // So the files should not be pruned in advance - FileCollectionUtil.collectFiles(config, pmd.files()); - - if (config.getReportFormat() != null) { - Renderer renderer = config.createRenderer(true); - pmd.addRenderer(renderer); - } - - if (!config.getRuleSetPaths().isEmpty()) { - final net.sourceforge.pmd.lang.rule.RuleSetLoader ruleSetLoader = pmd.newRuleSetLoader(); - final List ruleSets = net.sourceforge.pmd.lang.rule.InternalApiBridge.loadRuleSetsWithoutException(ruleSetLoader, config.getRuleSetPaths()); - pmd.addRuleSets(ruleSets); - } - - for (Language language : config.getLanguageRegistry()) { - LanguagePropertyBundle props = config.getLanguageProperties(language); - assert props.getLanguage().equals(language); - pmd.langProperties.put(language, props); - - LanguageVersion forcedVersion = config.getForceLanguageVersion(); - if (forcedVersion != null && forcedVersion.getLanguage().equals(language)) { - props.setLanguageVersion(forcedVersion.getVersion()); - } - - // TODO replace those with actual language properties when the - // CLI syntax is implemented. - props.setProperty(LanguagePropertyBundle.SUPPRESS_MARKER, config.getSuppressMarker()); - if (props instanceof JvmLanguagePropertyBundle) { - ((JvmLanguagePropertyBundle) props).setClassLoader(config.getClassLoader()); - } - } - - for (Path path : config.getRelativizeRoots()) { - pmd.fileNameRenderer.relativizeWith(path); - } - - return pmd; - } - - // test only - List rulesets() { - return ruleSets; - } - - // test only - List renderers() { - return renderers; - } - - - /** - * Returns the file collector for the analysed sources. - */ - public FileCollector files() { - return collector; // todo user can close collector programmatically - } - - /** - * Returns a new ruleset loader, which can be used to create new - * rulesets (add them then with {@link #addRuleSet(RuleSet)}). - * - *
{@code
-     * try (PmdAnalysis pmd = create(config)) {
-     *     pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
-     * }
-     * }
- */ - public net.sourceforge.pmd.lang.rule.RuleSetLoader newRuleSetLoader() { - return RuleSetLoader.fromPmdConfig(configuration); - } - - /** - * Add a new renderer. The given renderer must not already be started, - * it will be started by {@link #performAnalysis()}. - * - * @throws NullPointerException If the parameter is null - */ - public void addRenderer(Renderer renderer) { - AssertionUtil.requireParamNotNull("renderer", renderer); - this.renderers.add(renderer); - } - - /** - * Add several renderers at once. - * - * @throws NullPointerException If the parameter is null, or any of its items is null. - */ - public void addRenderers(Collection renderers) { - renderers.forEach(this::addRenderer); - } - - /** - * Add a new listener. As per the contract of {@link GlobalAnalysisListener}, - * this object must be ready for interaction. However, nothing will - * be done with the listener until {@link #performAnalysis()} is called. - * The listener will be closed by {@link #performAnalysis()}, or - * {@link #close()}, whichever happens first. - * - * @throws NullPointerException If the parameter is null - */ - public void addListener(GlobalAnalysisListener listener) { - AssertionUtil.requireParamNotNull("listener", listener); - this.listeners.add(listener); - } - - /** - * Add several listeners at once. - * - * @throws NullPointerException If the parameter is null, or any of its items is null. - * @see #addListener(GlobalAnalysisListener) - */ - public void addListeners(Collection listeners) { - listeners.forEach(this::addListener); - } - - /** - * Add a new ruleset. - * - * @throws NullPointerException If the parameter is null - */ - public void addRuleSet(RuleSet ruleSet) { - AssertionUtil.requireParamNotNull("rule set", ruleSet); - this.ruleSets.add(ruleSet); - } - - /** - * Add several rulesets at once. - * - * @throws NullPointerException If the parameter is null, or any of its items is null. - */ - public void addRuleSets(Collection ruleSets) { - ruleSets.forEach(this::addRuleSet); - } - - /** - * Returns an unmodifiable view of the ruleset list. That will be - * processed. - */ - public List getRulesets() { - return Collections.unmodifiableList(ruleSets); - } - - - /** - * Returns a mutable bundle of language properties that are associated - * to the given language (always the same for a given language). - * - * @param language A language, which must be registered - */ - public LanguagePropertyBundle getLanguageProperties(Language language) { - configuration.checkLanguageIsRegistered(language); - return langProperties.computeIfAbsent(language, Language::newPropertyBundle); - } - - - public ConfigurableFileNameRenderer fileNameRenderer() { - return fileNameRenderer; - } - - /** - * Run PMD with the current state of this instance. This will start - * and finish the registered renderers, and close all - * {@linkplain #addListener(GlobalAnalysisListener) registered listeners}. - * All files collected in the {@linkplain #files() file collector} are - * processed. This does not return a report, as the analysis results - * are consumed by {@link GlobalAnalysisListener} instances (of which - * Renderers are a special case). Note that this does - * not throw, errors are instead accumulated into a {@link PmdReporter}. - */ - public void performAnalysis() { - performAnalysisImpl(Collections.emptyList()); - } - - /** - * Run PMD with the current state of this instance. This will start - * and finish the registered renderers. All files collected in the - * {@linkplain #files() file collector} are processed. Returns the - * output report. Note that this does not throw, errors are instead - * accumulated into a {@link PmdReporter}. - */ - public Report performAnalysisAndCollectReport() { - try (Report.GlobalReportBuilderListener reportBuilder = new Report.GlobalReportBuilderListener()) { - performAnalysisImpl(listOf(reportBuilder)); // closes the report builder - return reportBuilder.getResultImpl(); - } - } - - void performAnalysisImpl(List extraListeners) { - try (FileCollector files = collector) { - files.filterLanguages(getApplicableLanguages(false)); - performAnalysisImpl(extraListeners, files.getCollectedFiles()); - } - } - - void performAnalysisImpl(List extraListeners, List textFiles) { - net.sourceforge.pmd.lang.rule.internal.RuleSets rulesets = new net.sourceforge.pmd.lang.rule.internal.RuleSets(this.ruleSets); - - GlobalAnalysisListener listener; - try { - @SuppressWarnings("PMD.CloseResource") - AnalysisCacheListener cacheListener = new AnalysisCacheListener(configuration.getAnalysisCache(), - rulesets, - configuration.getClassLoader(), - textFiles); - listener = GlobalAnalysisListener.tee(listOf(createComposedRendererListener(renderers), - GlobalAnalysisListener.tee(listeners), - GlobalAnalysisListener.tee(extraListeners), - cacheListener)); - - // Initialize listeners - try (ListenerInitializer initializer = listener.initializer()) { - initializer.setNumberOfFilesToAnalyze(textFiles.size()); - initializer.setFileNameRenderer(fileNameRenderer()); - } - } catch (Exception e) { - reporter.errorEx("Exception while initializing analysis listeners", e); - throw new RuntimeException("Exception while initializing analysis listeners", e); - } - - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) { - for (final Rule rule : removeBrokenRules(rulesets)) { - // todo Just like we throw for invalid properties, "broken rules" - // shouldn't be a "config error". This is the only instance of - // config errors... - // see https://github.com/pmd/pmd/issues/3901 - listener.onConfigError(new Report.ConfigurationError(rule, rule.dysfunctionReason())); - } - - encourageToUseIncrementalAnalysis(configuration); - - try (LanguageProcessorRegistry lpRegistry = LanguageProcessorRegistry.create( - // only start the applicable languages (and dependencies) - new LanguageRegistry(getApplicableLanguages(true)), - langProperties, - reporter - )) { - // Note the analysis task is shared: all processors see - // the same file list, which may contain files for other - // languages. - AnalysisTask analysisTask = InternalApiBridge.createAnalysisTask( - rulesets, - textFiles, - listener, - configuration.getThreads(), - configuration.getAnalysisCache(), - reporter, - lpRegistry - ); - - List analyses = new ArrayList<>(); - try { - for (Language lang : lpRegistry.getLanguages()) { - analyses.add(lpRegistry.getProcessor(lang).launchAnalysis(analysisTask)); - } - } finally { - Exception e = IOUtil.closeAll(analyses); - if (e != null) { - reporter.errorEx("Error while joining analysis", e); - } - } - - } catch (LanguageTerminationException e) { - reporter.errorEx("Error while closing language processors", e); - } - } finally { - try { - listener.close(); - } catch (Exception e) { - reporter.errorEx("Exception while closing analysis listeners", e); - // todo better exception - throw new RuntimeException("Exception while closing analysis listeners", e); - } - } - } - - - private GlobalAnalysisListener createComposedRendererListener(List renderers) throws Exception { - if (renderers.isEmpty()) { - return GlobalAnalysisListener.noop(); - } - - List rendererListeners = new ArrayList<>(renderers.size()); - for (Renderer renderer : renderers) { - try { - @SuppressWarnings("PMD.CloseResource") - GlobalAnalysisListener listener = - Objects.requireNonNull(renderer.newListener(), "Renderer should provide non-null listener"); - rendererListeners.add(listener); - } catch (Exception ioe) { - // close listeners so far, throw their close exception or the ioe - IOUtil.ensureClosed(rendererListeners, ioe); - throw AssertionUtil.shouldNotReachHere("ensureClosed should have thrown", ioe); - } - } - return GlobalAnalysisListener.tee(rendererListeners); - } - - private Set getApplicableLanguages(boolean quiet) { - Set languages = new HashSet<>(); - LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer(); - - for (RuleSet ruleSet : ruleSets) { - for (Rule rule : ruleSet.getRules()) { - Language ruleLanguage = rule.getLanguage(); - Objects.requireNonNull(ruleLanguage, "Rule has no language " + rule); - if (!languages.contains(ruleLanguage)) { - LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage); - if (net.sourceforge.pmd.lang.rule.InternalApiBridge.ruleSetApplies((net.sourceforge.pmd.lang.rule.Rule) rule, version)) { - configuration.checkLanguageIsRegistered(ruleLanguage); - languages.add(ruleLanguage); - if (!quiet) { - LOG.trace("Using {} version ''{}''", version.getLanguage().getName(), version.getTerseName()); - } - } - } - } - } - - // collect all dependencies, they shouldn't be filtered out - LanguageRegistry reg = configuration.getLanguageRegistry(); - boolean changed; - do { - changed = false; - for (Language lang : new HashSet<>(languages)) { - for (String depId : lang.getDependencies()) { - Language depLang = reg.getLanguageById(depId); - if (depLang == null) { - // todo maybe report all then throw - throw new IllegalStateException( - "Language " + lang.getId() + " has unsatisfied dependencies: " - + depId + " is not found in " + reg - ); - } - changed |= languages.add(depLang); - } - } - } while (changed); - return languages; - } - - /** - * Remove and return the misconfigured rules from the rulesets and log them - * for good measure. - */ - private Set removeBrokenRules(final RuleSets ruleSets) { - final Set brokenRules = new HashSet<>(); - ruleSets.removeDysfunctionalRules(brokenRules); - - for (final Rule rule : brokenRules) { - reporter.warn("Removed misconfigured rule: {0} cause: {1}", - rule.getName(), rule.dysfunctionReason()); - } - - return brokenRules; - } - - - public PmdReporter getReporter() { - return reporter; - } - - @Override - public void close() { - if (closed) { - return; - } - closed = true; - collector.close(); - - // close listeners if analysis is not run. - IOUtil.closeAll(listeners); - - /* - * Make sure it's our own classloader before attempting to close it.... - * Maven + Jacoco provide us with a cloaseable classloader that if closed - * will throw a ClassNotFoundException. - */ - if (configuration.getClassLoader() instanceof ClasspathClassLoader) { - IOUtil.tryCloseClassLoader(configuration.getClassLoader()); - } - } - - public ReportStats runAndReturnStats() { - if (getRulesets().isEmpty()) { - return ReportStats.empty(); - } - - @SuppressWarnings("PMD.CloseResource") - ReportStatsListener listener = new ReportStatsListener(); - - addListener(listener); - - try { - performAnalysis(); - } catch (Exception e) { - getReporter().errorEx("Exception during processing", e); - ReportStats stats = listener.getResult(); - printErrorDetected(1 + stats.getNumErrors()); - return stats; // should have been closed - } - ReportStats stats = listener.getResult(); - - if (stats.getNumErrors() > 0) { - printErrorDetected(stats.getNumErrors()); - } - - return stats; - } - - static void printErrorDetected(PmdReporter reporter, int errors) { - String msg = LogMessages.errorDetectedMessage(errors, "PMD"); - // note: using error level here increments the error count of the reporter, - // which we don't want. - reporter.info(StringUtil.quoteMessageFormat(msg)); - } - - void printErrorDetected(int errors) { - printErrorDetected(getReporter(), errors); - } - - private static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) { - final PmdReporter reporter = configuration.getReporter(); - - if (!configuration.isIgnoreIncrementalAnalysis() - && configuration.getAnalysisCache() instanceof NoopAnalysisCache - && reporter.isLoggable(Level.WARN)) { - final String version = - PMDVersion.isUnknown() || PMDVersion.isSnapshot() ? "latest" : "pmd-doc-" + PMDVersion.VERSION; - reporter.warn("This analysis could be faster, please consider using Incremental Analysis: " - + "https://docs.pmd-code.org/{0}/pmd_userdocs_incremental_analysis.html", version); - } - } - - // ---- compatibility - - // new method to be compatible with PMD 6 - Report has changed package - public net.sourceforge.pmd.Report performAnalysisAndCollectReport$$bridge() { // SUPPRESS CHECKSTYLE ignore - return performAnalysisAndCollectReport(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/Report.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/Report.java deleted file mode 100644 index c02ae1a01f..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/Report.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: filterViolations(net.sourceforge.pmd.util.Predicate filter) - -package net.sourceforge.pmd; - -import static java.util.Collections.synchronizedList; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; - -import net.sourceforge.pmd.annotation.DeprecatedUntil700; -import net.sourceforge.pmd.annotation.Experimental; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.document.FileId; -import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.renderers.AbstractAccumulatingRenderer; -import net.sourceforge.pmd.reporting.FileAnalysisListener; -import net.sourceforge.pmd.reporting.GlobalAnalysisListener; -import net.sourceforge.pmd.reporting.ViolationSuppressor; -import net.sourceforge.pmd.util.BaseResultProducingCloseable; - -/** - * A {@link Report} collects all information during a PMD execution. This - * includes violations, suppressed violations, metrics, error during processing - * and configuration errors. - * - *

A report may be created by a {@link GlobalReportBuilderListener} that you - * use as the {@linkplain GlobalAnalysisListener} in {@link PmdAnalysis#performAnalysisAndCollectReport() PMD's entry point}. - * You can also create one manually with {@link #buildReport(Consumer)}. - * - *

For special use cases, like filtering the report after PMD analysis and - * before rendering the report, some transformation operations are provided: - *

    - *
  • {@link #filterViolations(Predicate)}
  • - *
  • {@link #union(Report)}
  • - *
- * These methods create a new {@link Report} rather than modifying their receiver. - */ -public class Report { - // todo move to package reporting - - protected final List violations = synchronizedList(new ArrayList<>()); - protected final List suppressedRuleViolations = synchronizedList(new ArrayList<>()); - protected final List errors = synchronizedList(new ArrayList<>()); - protected final List configErrors = synchronizedList(new ArrayList<>()); - - @DeprecatedUntil700 - @InternalApi - public Report() { // NOPMD - UnnecessaryConstructor - // TODO: should be package-private, you have to use a listener to build a report. - } - - /** - * Represents a configuration error. - */ - public static class ConfigurationError { - - private final Rule rule; - private final String issue; - - /** - * Creates a new configuration error for a specific rule. - * - * @param theRule - * the rule which is configured wrongly - * @param theIssue - * the reason, why the configuration is wrong - */ - public ConfigurationError(Rule theRule, String theIssue) { - rule = theRule; - issue = theIssue; - } - - /** - * Gets the wrongly configured rule - * - * @return the wrongly configured rule - */ - public Rule rule() { - return rule; - } - - /** - * Gets the reason for the configuration error. - * - * @return the issue - */ - public String issue() { - return issue; - } - } - - /** - * Represents a processing error, such as a parse error. - */ - public static class ProcessingError { - - private final Throwable error; - private final FileId file; - - /** - * Creates a new processing error - * - * @param error - * the error - * @param file - * the file during which the error occurred - */ - public ProcessingError(Throwable error, FileId file) { - this.error = error; - this.file = file; - } - - public String getMsg() { - return error.getClass().getSimpleName() + ": " + error.getMessage(); - } - - public String getDetail() { - try (StringWriter stringWriter = new StringWriter(); - PrintWriter writer = new PrintWriter(stringWriter)) { - error.printStackTrace(writer); - return stringWriter.toString(); - } catch (IOException e) { - // IOException on close - should never happen when using StringWriter - throw new RuntimeException(e); - } - } - - public FileId getFileId() { - return file; - } - - public Throwable getError() { - return error; - } - - // ------------------- compat extensions -------------------- - public String getFile() { - return file.getAbsolutePath(); - } - } - - /** - * Represents a violation, that has been suppressed. - */ - public static class SuppressedViolation { - - private final RuleViolation rv; - private final String userMessage; - private final ViolationSuppressor suppressor; - - /** - * Creates a suppressed violation. - * - * @param rv The violation, that has been suppressed - * @param suppressor The suppressor which suppressed the violation - * @param userMessage Any relevant info given by the suppressor - */ - public SuppressedViolation(RuleViolation rv, ViolationSuppressor suppressor, String userMessage) { - this.suppressor = suppressor; - this.rv = rv; - this.userMessage = userMessage; - } - - public ViolationSuppressor getSuppressor() { - return suppressor; - } - - public RuleViolation getRuleViolation() { - return this.rv; - } - - public String getUserMessage() { - return userMessage; - } - } - - /** - * Adds a new rule violation to the report and notify the listeners. - * - * @param violation the violation to add - * - * @deprecated PMD's way of creating a report is internal and may be changed in pmd 7. - */ - @DeprecatedUntil700 - @Deprecated - @InternalApi - public void addRuleViolation(RuleViolation violation) { - synchronized (violations) { - // note that this binary search is inefficient as we usually - // report violations file by file. - int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR); - violations.add(index < 0 ? -index - 1 : index, violation); - } - } - - /** - * Adds a new suppressed violation. - */ - private void addSuppressedViolation(SuppressedViolation sv) { - suppressedRuleViolations.add(sv); - } - - /** - * Adds a new configuration error to the report. - * - * @param error the error to add - * - * @deprecated PMD's way of creating a report is internal and may be changed in pmd 7. - */ - @DeprecatedUntil700 - @Deprecated - @InternalApi - public void addConfigError(ConfigurationError error) { - configErrors.add(error); - } - - /** - * Adds a new processing error to the report. - * - * @param error - * the error to add - * @deprecated PMD's way of creating a report is internal and may be changed in pmd 7. - */ - @DeprecatedUntil700 - @Deprecated - @InternalApi - public void addError(ProcessingError error) { - errors.add(error); - } - - /** - * Merges the given report into this report. This might be necessary, if a - * summary over all violations is needed as PMD creates one report per file - * by default. - * - *

This is synchronized on an internal lock (note that other mutation - * operations are not synchronized, todo for pmd 7). - * - * @param r the report to be merged into this. - * - * @see AbstractAccumulatingRenderer - * - * @deprecated Convert Renderer to use the reports. - */ - @Deprecated - public void merge(Report r) { - errors.addAll(r.errors); - configErrors.addAll(r.configErrors); - suppressedRuleViolations.addAll(r.suppressedRuleViolations); - - for (RuleViolation violation : r.getViolations()) { - addRuleViolation(violation); - } - } - - - /** - * Returns an unmodifiable list of violations that were suppressed. - */ - public List getSuppressedViolations() { - return Collections.unmodifiableList(suppressedRuleViolations); - } - - /** - * Returns an unmodifiable list of violations that have been - * recorded until now. None of those violations were suppressed. - * - *

The violations list is sorted with {@link RuleViolation#DEFAULT_COMPARATOR}. - */ - public List getViolations() { - return Collections.unmodifiableList(violations); - } - - - /** - * Returns an unmodifiable list of processing errors that have been - * recorded until now. - */ - public List getProcessingErrors() { - return Collections.unmodifiableList(errors); - } - - - /** - * Returns an unmodifiable list of configuration errors that have - * been recorded until now. - */ - public List getConfigurationErrors() { - return Collections.unmodifiableList(configErrors); - } - - /** - * Create a report by making side effects on a {@link FileAnalysisListener}. - * This wraps a {@link ReportBuilderListener}. - */ - public static Report buildReport(Consumer lambda) { - return BaseResultProducingCloseable.using(new ReportBuilderListener(), lambda); - } - - /** - * A {@link FileAnalysisListener} that accumulates events into a - * {@link Report}. - */ - public static /*final*/ class ReportBuilderListener extends BaseResultProducingCloseable implements FileAnalysisListener { - - private final Report report; - - public ReportBuilderListener() { - this(new Report()); - } - - ReportBuilderListener(Report report) { - this.report = report; - } - - @Override - protected Report getResultImpl() { - return report; - } - - @Override - public void onRuleViolation(net.sourceforge.pmd.reporting.RuleViolation violation) { - report.addRuleViolation(violation); - } - - @Override - public void onSuppressedRuleViolation(net.sourceforge.pmd.reporting.Report.SuppressedViolation violation) { - report.addSuppressedViolation(violation); - } - - @Override - public void onError(net.sourceforge.pmd.reporting.Report.ProcessingError error) { - report.addError(error); - } - - @Override - public String toString() { - return "ReportBuilderListener"; - } - } - - /** - * A {@link GlobalAnalysisListener} that accumulates the events of - * all files into a {@link Report}. - */ - public static /*final*/ class GlobalReportBuilderListener extends BaseResultProducingCloseable implements GlobalAnalysisListener { - - private final net.sourceforge.pmd.reporting.Report report = new net.sourceforge.pmd.reporting.Report(); - - @Override - public FileAnalysisListener startFileAnalysis(TextFile file) { - // note that the report is shared, but Report is now thread-safe - return new ReportBuilderListener(this.report); - } - - @Override - public void onConfigError(net.sourceforge.pmd.reporting.Report.ConfigurationError error) { - report.addConfigError(error); - } - - @Override - protected net.sourceforge.pmd.reporting.Report getResultImpl() { - return report; - } - } - - /** - * Creates a new report taking all the information from this report, - * but filtering the violations. - * - * @param filter when true, the violation will be kept. - * @return copy of this report - */ - @Experimental - public Report filterViolations(Predicate filter) { - Report copy = new Report(); - - for (RuleViolation violation : violations) { - if (filter.test(violation)) { - copy.addRuleViolation(violation); - } - } - - copy.suppressedRuleViolations.addAll(suppressedRuleViolations); - copy.errors.addAll(errors); - copy.configErrors.addAll(configErrors); - return copy; - } - - /** - * Creates a new report by combining this report with another report. - * This is similar to {@link #merge(Report)}, but instead a new report - * is created. The lowest start time and greatest end time are kept in the copy. - * - * @param other the other report to combine - * @return - */ - @Experimental - public Report union(Report other) { - Report copy = new Report(); - - for (RuleViolation violation : violations) { - copy.addRuleViolation(violation); - } - for (RuleViolation violation : other.violations) { - copy.addRuleViolation(violation); - } - - copy.suppressedRuleViolations.addAll(suppressedRuleViolations); - copy.suppressedRuleViolations.addAll(other.suppressedRuleViolations); - - copy.errors.addAll(errors); - copy.errors.addAll(other.errors); - copy.configErrors.addAll(configErrors); - copy.configErrors.addAll(other.configErrors); - - return copy; - } - - // ------------------- compat extensions -------------------- - @Deprecated - public Report filterViolations(net.sourceforge.pmd.util.Predicate filter) { - Predicate javaPredicate = filter::test; - return filterViolations(javaPredicate); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/Rule.java deleted file mode 100644 index 29116c858f..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/Rule.java +++ /dev/null @@ -1,321 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// Old version of this class. In PMD 7, this has been moved into sub-package "rule". -// Changes: -// - deepCopy - -package net.sourceforge.pmd; - -import java.util.List; -import java.util.Optional; -import java.util.regex.Pattern; - -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageProcessor; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.RulePriority; -import net.sourceforge.pmd.lang.rule.RuleSet; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.PropertySource; -import net.sourceforge.pmd.reporting.RuleContext; - -/** - * This is the basic Rule interface for PMD rules. - * - *

- * Thread safety: PMD will create one instance of a rule per - * thread. The instances are not shared across different threads. However, a - * single rule instance is reused for analyzing multiple files. - *

- */ -public interface Rule extends PropertySource { - - // TODO these should not be properties - - /** - * The property descriptor to universally suppress violations with messages - * matching a regular expression. - */ - PropertyDescriptor> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR = - PropertyFactory.regexProperty("violationSuppressRegex") - .desc("Suppress violations with messages matching a regular expression") - .toOptional("") - .defaultValue(Optional.empty()) - .build(); - - /** - * Name of the property to universally suppress violations on nodes which - * match a given relative XPath expression. - */ - PropertyDescriptor> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR = - PropertyFactory.stringProperty("violationSuppressXPath") - .desc("Suppress violations on nodes which match a given relative XPath expression.") - .toOptional("") - .defaultValue(Optional.empty()) - .build(); - - /** - * Get the Language of this Rule. - * - * @return the language - */ - Language getLanguage(); - - /** - * Set the Language of this Rule. - * - * @param language - * the language - */ - void setLanguage(Language language); - - /** - * Get the minimum LanguageVersion to which this Rule applies. If this value - * is null it indicates there is no minimum bound. - * - * @return the minimum language version - */ - LanguageVersion getMinimumLanguageVersion(); - - /** - * Set the minimum LanguageVersion to which this Rule applies. - * - * @param minimumLanguageVersion - * the minimum language version - */ - void setMinimumLanguageVersion(LanguageVersion minimumLanguageVersion); - - /** - * Get the maximum LanguageVersion to which this Rule applies. If this value - * is null it indicates there is no maximum bound. - * - * @return the maximum language version - */ - LanguageVersion getMaximumLanguageVersion(); - - /** - * Set the maximum LanguageVersion to which this Rule applies. - * - * @param maximumLanguageVersion - * the maximum language version - */ - void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion); - - /** - * Gets whether this Rule is deprecated. A deprecated Rule is one which: - *
    - *
  • is scheduled for removal in a future version of PMD
  • - *
  • or, has been removed and replaced with a non-functioning place-holder - * and will be completely removed in a future version of PMD
  • - *
  • or, has been renamed/moved and the old name will be completely - * removed in a future version of PMD
  • - *
- * - * @return true if this rule is deprecated - */ - boolean isDeprecated(); - - /** - * Sets whether this Rule is deprecated. - * - * @param deprecated - * whether this rule is deprecated - */ - void setDeprecated(boolean deprecated); - - /** - * Get the name of this Rule. - * - * @return the name - */ - @Override - String getName(); - - /** - * Set the name of this Rule. - * - * @param name - * the name - */ - void setName(String name); - - /** - * Get the version of PMD in which this Rule was added. Return - * null if not applicable. - * - * @return version of PMD since when this rule was added - */ - String getSince(); - - /** - * Set the version of PMD in which this Rule was added. - * - * @param since - * the version of PMD since when this rule was added - */ - void setSince(String since); - - /** - * Get the implementation class of this Rule. - * - * @return the implementation class name of this rule. - */ - String getRuleClass(); - - /** - * Set the class of this Rule. - * - * @param ruleClass - * the class name of this rule. - */ - void setRuleClass(String ruleClass); - - /** - * Get the name of the RuleSet containing this Rule. - * - * @return the name of th ruleset containing this rule. - * @see RuleSet - */ - String getRuleSetName(); - - /** - * Set the name of the RuleSet containing this Rule. - * - * @param name - * the name of the ruleset containing this rule. - * @see RuleSet - */ - void setRuleSetName(String name); - - /** - * Get the message to show when this Rule identifies a violation. - * - * @return the message to show for a violation. - */ - String getMessage(); - - /** - * Set the message to show when this Rule identifies a violation. - * - * @param message - * the message to show for a violation. - */ - void setMessage(String message); - - /** - * Get the description of this Rule. - * - * @return the description - */ - String getDescription(); - - /** - * Set the description of this Rule. - * - * @param description - * the description - */ - void setDescription(String description); - - /** - * Get the list of examples for this Rule. - * - * @return the list of examples for this rule. - */ - List getExamples(); - - /** - * Add a single example for this Rule. - * - * @param example - * a single example to add - */ - void addExample(String example); - - /** - * Get a URL for external information about this Rule. - * - * @return the URL for external information about this rule. - */ - String getExternalInfoUrl(); - - /** - * Set a URL for external information about this Rule. - * - * @param externalInfoUrl - * the URL for external information about this rule. - */ - void setExternalInfoUrl(String externalInfoUrl); - - /** - * Get the priority of this Rule. - * - * @return the priority - */ - RulePriority getPriority(); - - /** - * Set the priority of this Rule. - * - * @param priority - * the priority - */ - void setPriority(RulePriority priority); - - - /** - * Returns the object that selects the nodes to which this rule applies. - * The selected nodes will be handed to {@link #apply(Node, RuleContext)}. - */ - RuleTargetSelector getTargetSelector(); - - /** - * Initialize the rule using the language processor if needed. - * - * @param languageProcessor The processor for the rule's language - */ - default void initialize(LanguageProcessor languageProcessor) { - // by default do nothing - } - - /** - * Start processing. Called once per file, before apply() is first called. - * - * @param ctx the rule context - */ - void start(RuleContext ctx); - - - /** - * Process the given node. The nodes that are fed to this method - * are the nodes selected by {@link #getTargetSelector()}. - * - * @param target Node on which to apply the rule - * @param ctx Rule context, handling violations - */ - void apply(Node target, RuleContext ctx); - - /** - * End processing. Called once per file, after apply() is last called. - * - * @param ctx - * the rule context - */ - void end(RuleContext ctx); - - /** - * Creates a new copy of this rule. - * @return A new exact copy of this rule - */ - net.sourceforge.pmd.lang.rule.Rule deepCopy(); - - // new method to be compatible with PMD 6 - Rule has changed package - default Rule deepCopy$$bridge() { // SUPPRESS CHECKSTYLE ignore - return deepCopy(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RulePriority.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RulePriority.java deleted file mode 100644 index c624f1200e..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RulePriority.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// Copy of net.sourceforge.pmd.lang.rule.RulePriority -// Added method: -// - asPMD7RulePriority - -package net.sourceforge.pmd; - -/** - * These are the possible Rule priority values. - * - * For backward compatibility, priorities range in value from 1 to 5, with 5 - * being the lowest priority. This means the ordinal value of the Enum should be - * avoided in favor of {@link RulePriority#getPriority()} and - * {@link RulePriority#valueOf(int)} - * - * @see How - * to define rules priority - */ -public enum RulePriority { - - /** High: Change absolutely required. Behavior is critically broken/buggy */ - HIGH(1, "High"), - /** - * Medium to high: Change highly recommended. Behavior is quite likely to be - * broken/buggy. - */ - MEDIUM_HIGH(2, "Medium High"), - /** - * Medium: Change recommended. Behavior is confusing, perhaps buggy, and/or - * against standards/best practices. - */ - MEDIUM(3, "Medium"), - /** - * Medium to low: Change optional. Behavior is not likely to be buggy, but - * more just flies in the face of standards/style/good taste. - */ - MEDIUM_LOW(4, "Medium Low"), - /** - * Low: Change highly optional. Nice to have, such as a consistent naming - * policy for package/class/fields... - */ - LOW(5, "Low"); - - private final int priority; - private final String name; - - RulePriority(int priority, String name) { - this.priority = priority; - this.name = name; - } - - /** - * Get the priority value as a number. This is the value to be used in the - * externalized form of a priority (e.g. in RuleSet XML). - * - * @return The int value of the priority. - */ - public int getPriority() { - return priority; - } - - /** - * Get the descriptive name of this priority. - * - * @return The descriptive name. - */ - public String getName() { - return name; - } - - /** - * Returns the descriptive name of the priority. - * - * @return descriptive name of the priority - * @see #getName() - */ - @Override - public String toString() { - return name; - } - - /** - * Get the priority which corresponds to the given number as returned by - * {@link RulePriority#getPriority()}. If the number is an invalid value, - * then {@link RulePriority#LOW} will be returned. - * - * @param priority - * The numeric priority value. - * @return The priority. - */ - public static RulePriority valueOf(int priority) { - try { - return RulePriority.values()[priority - 1]; - } catch (ArrayIndexOutOfBoundsException e) { - return LOW; - } - } - - /** - * Returns the priority which corresponds to the given number as returned by - * {@link RulePriority#getPriority()}. If the number is an invalid value, - * then null will be returned. - * - * @param priority The numeric priority value. - */ - public static RulePriority valueOfNullable(int priority) { - try { - return RulePriority.values()[priority - 1]; - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - - /** - * Returns the priority which corresponds to the given number as returned by - * {@link RulePriority#getPriority()}. If the number is an invalid value, - * then null will be returned. - * - * @param priority The numeric priority value. - */ - public static RulePriority valueOfNullable(String priority) { - try { - int integer = Integer.parseInt(priority); - return RulePriority.values()[integer - 1]; - } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) { - return null; - } - } - - // ---- compatibility extensions - - public net.sourceforge.pmd.lang.rule.RulePriority asPMD7RulePriority() { - return net.sourceforge.pmd.lang.rule.RulePriority.valueOf(name()); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java deleted file mode 100644 index 2e79697725..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Before removing RuleSetFactoryCompatibility (#4314) - -package net.sourceforge.pmd; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; - -import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides a simple filter mechanism to avoid failing to parse an old ruleset, - * which references rules, that have either been removed from PMD already or - * renamed or moved to another ruleset. - * - * @see issue 1360 - */ -final class RuleSetFactoryCompatibility { - - static final RuleSetFactoryCompatibility EMPTY = new RuleSetFactoryCompatibility(); - /** The instance with the built-in filters for the modified PMD rules. */ - static final RuleSetFactoryCompatibility DEFAULT = new RuleSetFactoryCompatibility(); - - - static { - // PMD 5.3.0 - DEFAULT.addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); - DEFAULT.addFilterRuleRemoved("java", "controversial", "BooleanInversion"); - - // PMD 5.3.1 - DEFAULT.addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); - - // PMD 5.4.0 - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); - DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); - DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); - - // PMD 5.6.0 - DEFAULT.addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); - // unused/UnusedModifier moved AND renamed, order is important! - DEFAULT.addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); - - // PMD 6.0.0 - DEFAULT.addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); - DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); - DEFAULT.addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); - DEFAULT.addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); - DEFAULT.addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); - DEFAULT.addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); - DEFAULT.addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); - DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); - DEFAULT.addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); - // GuardLogStatementJavaUtil moved and renamed... - DEFAULT.addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); - DEFAULT.addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); - - } - - - private static final Logger LOG = LoggerFactory.getLogger(RuleSetFactoryCompatibility.class); - - private final List filters = new ArrayList<>(); - - - void addFilterRuleMovedAndRenamed(String language, String oldRuleset, String oldName, String newRuleset, String newName) { - filters.add(RuleSetFilter.ruleMoved(language, oldRuleset, newRuleset, oldName)); - filters.add(RuleSetFilter.ruleRenamed(language, newRuleset, oldName, newName)); - } - - void addFilterRuleRenamed(String language, String ruleset, String oldName, String newName) { - filters.add(RuleSetFilter.ruleRenamed(language, ruleset, oldName, newName)); - } - - void addFilterRuleMoved(String language, String oldRuleset, String newRuleset, String ruleName) { - filters.add(RuleSetFilter.ruleMoved(language, oldRuleset, newRuleset, ruleName)); - } - - void addFilterRuleRemoved(String language, String ruleset, String name) { - filters.add(RuleSetFilter.ruleRemoved(language, ruleset, name)); - } - - @Nullable String applyRef(String ref) { - return applyRef(ref, false); - } - - - /** - * Returns the new rule ref, or null if the rule was deleted. Returns - * the argument if no replacement is needed. - * - * @param ref Original ref - * @param warn Whether to output a warning if a replacement is done - */ - public @Nullable String applyRef(String ref, boolean warn) { - String result = ref; - for (RuleSetFilter filter : filters) { - result = filter.applyRef(result, warn); - if (result == null) { - return null; - } - } - return result; - } - - /** - * Returns the new rule name, or null if the rule was deleted. Returns - * the argument if no replacement is needed. - * - * @param rulesetRef Ruleset name - * @param excludeName Original excluded name - * @param warn Whether to output a warning if a replacement is done - */ - public @Nullable String applyExclude(String rulesetRef, String excludeName, boolean warn) { - String result = excludeName; - for (RuleSetFilter filter : filters) { - result = filter.applyExclude(rulesetRef, result, warn); - if (result == null) { - return null; - } - } - return result; - } - - private static final class RuleSetFilter { - - private static final String MOVED_MESSAGE = "The rule \"{1}\" has been moved from ruleset \"{0}\" to \"{2}\". Please change your ruleset!"; - private static final String RENAMED_MESSAGE = "The rule \"{1}\" has been renamed to \"{3}\". Please change your ruleset!"; - private static final String REMOVED_MESSAGE = "The rule \"{1}\" in ruleset \"{0}\" has been removed from PMD and no longer exists. Please change your ruleset!"; - private final String ruleRef; - private final String oldRuleset; - private final String oldName; - private final String newRuleset; - private final String newName; - private final String logMessage; - - private RuleSetFilter(String oldRuleset, - String oldName, - @Nullable String newRuleset, - @Nullable String newName, - String logMessage) { - this.oldRuleset = oldRuleset; - this.oldName = oldName; - this.newRuleset = newRuleset; - this.newName = newName; - this.logMessage = logMessage; - this.ruleRef = oldRuleset + "/" + oldName; - } - - public static RuleSetFilter ruleRenamed(String language, String ruleset, String oldName, String newName) { - String base = "rulesets/" + language + "/" + ruleset + ".xml"; - return new RuleSetFilter(base, oldName, base, newName, RENAMED_MESSAGE); - } - - public static RuleSetFilter ruleMoved(String language, String oldRuleset, String newRuleset, String ruleName) { - String base = "rulesets/" + language + "/"; - return new RuleSetFilter(base + oldRuleset + ".xml", ruleName, - base + newRuleset + ".xml", ruleName, - MOVED_MESSAGE); - } - - public static RuleSetFilter ruleRemoved(String language, String ruleset, String name) { - String oldRuleset = "rulesets/" + language + "/" + ruleset + ".xml"; - return new RuleSetFilter(oldRuleset, name, - null, null, - REMOVED_MESSAGE); - } - - @Nullable String applyExclude(String ref, String name, boolean warn) { - if (oldRuleset.equals(ref) - && oldName.equals(name) - && oldRuleset.equals(newRuleset)) { - if (warn) { - warn(); - } - - return newName; - } - - return name; - } - - @Nullable String applyRef(String ref, boolean warn) { - - if (ref.equals(this.ruleRef)) { - - if (warn) { - warn(); - } - - if (newName != null) { - return newRuleset + "/" + newName; - } else { - // deleted - return null; - } - } - - return ref; - } - - private void warn() { - if (LOG.isWarnEnabled()) { - String log = MessageFormat.format(logMessage, oldRuleset, oldName, newRuleset, newName); - LOG.warn("Applying rule set filter: {}", log); - } - } - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java deleted file mode 100644 index 4af60a4d22..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -import org.checkerframework.checker.nullness.qual.NonNull; - -public class RuleSetLoadException extends net.sourceforge.pmd.lang.rule.RuleSetLoadException { - public RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { - super(rsetId, cause); - } - - public RuleSetLoadException(RuleSetReferenceId rsetId, String message) { - super(rsetId, message); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoader.java deleted file mode 100644 index 20e91f3069..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// Copy from PMD 7 with minimized functionality. -// Only the methods called by maven-pmd-plugin are kept, -// but they do nothing. That means, that maven-pmd-plugin can't report deprecated -// rules properly anymore. -// The ruleset for actual PMD analysis is loaded by PMD itself later on, and not -// through this class. - -package net.sourceforge.pmd; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import net.sourceforge.pmd.lang.rule.RuleSet; - -public final class RuleSetLoader { - public RuleSetLoader warnDeprecated(boolean warn) { - return this; - } - - public List loadFromResources(Collection paths) { - return Collections.emptyList(); - } - - public static RuleSetLoader fromPmdConfig(PMDConfiguration configuration) { - return new RuleSetLoader(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java deleted file mode 100644 index 0cb348899a..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -public class RuleSetReferenceId extends net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId { - public RuleSetReferenceId(String id) { - super(id); - } - - public RuleSetReferenceId(String id, RuleSetReferenceId externalRuleSetReferenceId) { - super(id, externalRuleSetReferenceId); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java deleted file mode 100644 index 7ee5ec4daa..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java +++ /dev/null @@ -1,222 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// And it is kept in the old package. -// The moved class is in net.sourceforge.pmd.lang.rule.internal - -package net.sourceforge.pmd; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.benchmark.TimedOperation; -import net.sourceforge.pmd.benchmark.TimedOperationCategory; -import net.sourceforge.pmd.lang.LanguageProcessorRegistry; -import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.lang.rule.InternalApiBridge; -import net.sourceforge.pmd.lang.rule.Rule; -import net.sourceforge.pmd.lang.rule.RuleSet; -import net.sourceforge.pmd.lang.rule.internal.RuleApplicator; -import net.sourceforge.pmd.reporting.FileAnalysisListener; -import net.sourceforge.pmd.util.log.PmdReporter; - -/** - * Grouping of Rules per Language in a RuleSet. - * - * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be - */ -public class RuleSets { - - private final List ruleSets; - - private RuleApplicator ruleApplicator; - - /** - * Copy constructor. Deep copies RuleSets. - * - * @param ruleSets The RuleSets to copy. - */ - public RuleSets(final RuleSets ruleSets) { - List rsets = new ArrayList<>(); - for (final RuleSet rs : ruleSets.ruleSets) { - rsets.add(new RuleSet(rs)); - } - this.ruleSets = Collections.unmodifiableList(rsets); - } - - public RuleSets(Collection ruleSets) { - this.ruleSets = Collections.unmodifiableList(new ArrayList<>(ruleSets)); - } - - /** - * Public constructor. Add the given rule set. - * - * @param ruleSet the RuleSet - */ - public RuleSets(RuleSet ruleSet) { - this.ruleSets = Collections.singletonList(ruleSet); - } - - public void initializeRules(LanguageProcessorRegistry lpReg, PmdReporter reporter) { - // this is abusing the mutability of RuleSet, will go away eventually. - for (RuleSet rset : ruleSets) { - for (Iterator iterator = rset.getRules().iterator(); iterator.hasNext();) { - Rule rule = iterator.next(); - try { - rule.initialize(lpReg.getProcessor(rule.getLanguage())); - } catch (Exception e) { - reporter.errorEx( - "Exception while initializing rule " + rule.getName() + ", the rule will not be run", e); - iterator.remove(); - } - } - } - } - - private RuleApplicator prepareApplicator() { - return RuleApplicator.build(ruleSets.stream().flatMap(it -> it.getRules().stream())::iterator); - } - - /** - * Get all the RuleSets. - * - * @return RuleSet[] - */ - public RuleSet[] getAllRuleSets() { - return ruleSets.toArray(new RuleSet[0]); - } - - // internal - List getRuleSetsInternal() { - return ruleSets; - } - - public Iterator getRuleSetsIterator() { - return ruleSets.iterator(); - } - - /** - * Return all rules from all rulesets. - * - * @return Set - */ - public Set getAllRules() { - Set result = new HashSet<>(); - for (RuleSet r : ruleSets) { - result.addAll(r.getRules()); - } - return result; - } - - /** - * Check if a given source file should be checked by rules in this RuleSets. - * - * @param file - * the source file to check - * @return true if the file should be checked, - * false otherwise - */ - public boolean applies(TextFile file) { - for (RuleSet ruleSet : ruleSets) { - if (InternalApiBridge.ruleSetApplies(ruleSet, file.getFileId())) { - return true; - } - } - return false; - } - - /** - * Apply all applicable rules to the compilation units. Applicable means the - * language of the rules must match the language of the source (@see - * applies). - * - * @param root the List of compilation units; the type these must have, - * depends on the source language - * @param listener Listener that will handle events while analysing. - */ - public void apply(RootNode root, FileAnalysisListener listener) { - if (ruleApplicator == null) { - // initialize here instead of ctor, because some rules properties - // are set after creating the ruleset, and jaxen xpath queries - // initialize their XPath expressions when calling getRuleChainVisits()... fixme - this.ruleApplicator = prepareApplicator(); - } - - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.RULE_AST_INDEXATION)) { - ruleApplicator.index(root); - } - - for (RuleSet ruleSet : ruleSets) { - if (InternalApiBridge.ruleSetApplies(ruleSet, root.getTextDocument().getFileId())) { - ruleApplicator.apply(ruleSet.getRules(), listener); - } - } - } - - /** - * Returns the first Rule found with the given name. - * - * Note: Since we support multiple languages, rule names are not expected to - * be unique within any specific ruleset. - * - * @param ruleName - * the exact name of the rule to find - * @return the rule or null if not found - */ - public Rule getRuleByName(String ruleName) { - Rule rule = null; - for (Iterator i = ruleSets.iterator(); i.hasNext() && rule == null;) { - RuleSet ruleSet = i.next(); - rule = ruleSet.getRuleByName(ruleName); - } - return rule; - } - - /** - * Determines the total count of rules that are used in all rule sets. - * - * @return the count - */ - public int ruleCount() { - int count = 0; - for (RuleSet r : ruleSets) { - count += r.getRules().size(); - } - return count; - } - - - /** - * Remove and collect any rules that report problems. - * - * @param collector - */ - public void removeDysfunctionalRules(Collection collector) { - for (RuleSet ruleSet : ruleSets) { - ruleSet.removeDysfunctionalRules(collector); - } - } - - /** - * Retrieves a checksum of the rulesets being used. Any change to any rule - * of any ruleset should trigger a checksum change. - * - * @return The checksum for this ruleset collection. - */ - public long getChecksum() { - long checksum = 1; - for (final RuleSet ruleSet : ruleSets) { - checksum = checksum * 31 + ruleSet.getChecksum(); - } - return checksum; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleViolation.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleViolation.java deleted file mode 100644 index b1d685bf46..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleViolation.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: -// - getFilename -// - getRule returns n.s.pmd.rule.Rule or the old n.s.pmd.Rule - -package net.sourceforge.pmd; - -import java.util.Comparator; -import java.util.Map; - -import net.sourceforge.pmd.annotation.DeprecatedUntil700; -import net.sourceforge.pmd.lang.document.FileId; -import net.sourceforge.pmd.lang.document.FileLocation; - -/** - * A RuleViolation is created by a Rule when it identifies a violation of the - * Rule constraints. RuleViolations are simple data holders that are collected - * into a {@link Report}. - * - *

Since PMD 6.21.0, implementations of this interface are considered internal - * API and hence deprecated. Clients should exclusively use this interface. - * - * @see Rule - */ -public interface RuleViolation { - // todo move to package reporting - - /** - * A comparator for rule violations. This compares all exposed attributes - * of a violation, filename first. The remaining parameters are compared - * in an unspecified order. - */ - Comparator DEFAULT_COMPARATOR = - Comparator.comparing(RuleViolation::getFileId) - .thenComparingInt(RuleViolation::getBeginLine) - .thenComparingInt(RuleViolation::getBeginColumn) - .thenComparing(RuleViolation::getDescription, Comparator.nullsLast(Comparator.naturalOrder())) - .thenComparingInt(RuleViolation::getEndLine) - .thenComparingInt(RuleViolation::getEndColumn) - .thenComparing(rv -> rv.getRule().getName()); - - - /** - * Key in {@link #getAdditionalInfo()} for the name of the class in - * which the violation was identified. - */ - String CLASS_NAME = "className"; - /** - * Key in {@link #getAdditionalInfo()} for the name of the variable - * related to the violation. - */ - String VARIABLE_NAME = "variableName"; - /** - * Key in {@link #getAdditionalInfo()} for the name of the method in - * which the violation was identified. - */ - String METHOD_NAME = "methodName"; - /** - * Key in {@link #getAdditionalInfo()} for the name of the package in - * which the violation was identified. - */ - String PACKAGE_NAME = "packageName"; - - /** - * Get the Rule which identified this violation. - * - * @return The identifying Rule. - */ - net.sourceforge.pmd.lang.rule.Rule getRule(); - - /** - * Get the description of this violation. - * - * @return The description. - */ - String getDescription(); - - - /** - * Returns the location where the violation should be reported. - */ - FileLocation getLocation(); - - /** - * Return the ID of the file where the violation was found. - */ - default FileId getFileId() { - return getLocation().getFileId(); - } - - /** - * Get the begin line number in the source file in which this violation was - * identified. - * - * @return Begin line number. - */ - default int getBeginLine() { - return getLocation().getStartPos().getLine(); - } - - /** - * Get the column number of the begin line in the source file in which this - * violation was identified. - * - * @return Begin column number. - */ - default int getBeginColumn() { - return getLocation().getStartPos().getColumn(); - } - - /** - * Get the end line number in the source file in which this violation was - * identified. - * - * @return End line number. - */ - default int getEndLine() { - return getLocation().getEndPos().getLine(); - } - - /** - * Get the column number of the end line in the source file in which this - * violation was identified. - * - * @return End column number. - */ - default int getEndColumn() { - return getLocation().getEndPos().getColumn(); - } - - /** - * A map of additional key-value pairs known about this violation. - * What data is in there is language specific. Common keys supported - * by several languages are defined as constants on this interface. - * The map is unmodifiable. - */ - Map getAdditionalInfo(); - - - /** - * Get the package name of the Class in which this violation was identified. - * - * @return The package name. - * - * @deprecated Use {@link #PACKAGE_NAME} - */ - @Deprecated - @DeprecatedUntil700 - default String getPackageName() { - return getAdditionalInfo().get(PACKAGE_NAME); - } - - /** - * Get the name of the Class in which this violation was identified. - * - * @return The Class name. - * @deprecated Use {@link #CLASS_NAME} - */ - @Deprecated - @DeprecatedUntil700 - default String getClassName() { - return getAdditionalInfo().get(CLASS_NAME); - } - - /** - * Get the method name in which this violation was identified. - * - * @return The method name. - * @deprecated Use {@link #METHOD_NAME} - */ - @Deprecated - @DeprecatedUntil700 - default String getMethodName() { - return getAdditionalInfo().get(METHOD_NAME); - } - - /** - * Get the variable name on which this violation was identified. - * - * @return The variable name. - * @deprecated Use {@link #VARIABLE_NAME} - */ - @Deprecated - @DeprecatedUntil700 - default String getVariableName() { - return getAdditionalInfo().get(VARIABLE_NAME); - } - - // ------------------- compat extensions -------------------- - @Deprecated - default String getFilename() { - return getLocation().getFileId().getFileName(); - } - - // returns the PMD 6 compatible Rule - default net.sourceforge.pmd.Rule getRule$$bridge() { // SUPPRESS CHECKSTYLE ignore - return getRule(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/annotation/DeprecatedUntil700.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/annotation/DeprecatedUntil700.java deleted file mode 100644 index c74f37fc42..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/annotation/DeprecatedUntil700.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// Moved from pmd-core 7.0.0-SNAPSHOT - -package net.sourceforge.pmd.annotation; - -/** - * Tags a deprecated member that should not be removed before PMD 7.0.0. - * Such members were made deprecated on the PMD 7 development branch and - * may be kept for backwards compatibility on the day of the PMD 7 release, - * because the replacement API cannot be backported to PMD 6. - */ -public @interface DeprecatedUntil700 { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java deleted file mode 100644 index c6d1d84760..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.cpd; - -import java.io.FilenameFilter; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; - -import net.sourceforge.pmd.util.filter.Filters; - -public abstract class AbstractLanguage implements Language { - private final String name; - private final String terseName; - private final Tokenizer tokenizer; - private final FilenameFilter fileFilter; - private final List extensions; - - public AbstractLanguage(String name, String terseName, Tokenizer tokenizer, String... extensions) { - this.name = name; - this.terseName = terseName; - this.tokenizer = tokenizer; - fileFilter = Filters.toFilenameFilter(Filters.getFileExtensionOrDirectoryFilter(extensions)); - this.extensions = Arrays.asList(extensions); - } - - @Override - public FilenameFilter getFileFilter() { - return fileFilter; - } - - @Override - public Tokenizer getTokenizer() { - return tokenizer; - } - - @Override - public void setProperties(Properties properties) { - // needs to be implemented by subclasses. - } - - @Override - public String getName() { - return name; - } - - @Override - public String getTerseName() { - return terseName; - } - - @Override - public List getExtensions() { - return extensions; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPD.java deleted file mode 100644 index c570dcec07..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import net.sourceforge.pmd.internal.util.FileFinder; -import net.sourceforge.pmd.internal.util.IOUtil; - -/** - * Adapter for PMD 7. This exposes CPD interface of PMD 6 but runs PMD 7 under the hood. - */ -public class CPD { - private final CPDConfiguration configuration; - private final List files = new ArrayList<>(); - private Set current = new HashSet<>(); - private CPDReport report; - - public CPD(CPDConfiguration configuration) { - this.configuration = configuration; - } - - public void addAllInDirectory(File dir) throws IOException { - addDirectory(dir, false); - } - - public void addRecursively(File dir) throws IOException { - addDirectory(dir, true); - } - - public void add(List files) throws IOException { - for (File f : files) { - add(f); - } - } - - private void addDirectory(File dir, boolean recurse) throws IOException { - if (!dir.exists()) { - throw new FileNotFoundException("Couldn't find directory " + dir); - } - FileFinder finder = new FileFinder(); - // TODO - could use SourceFileSelector here - add(finder.findFilesFrom(dir, configuration.filenameFilter(), recurse)); - } - - public void add(File file) throws IOException { - if (configuration.isSkipDuplicates()) { - // TODO refactor this thing into a separate class - String signature = file.getName() + '_' + file.length(); - if (current.contains(signature)) { - System.err.println("Skipping " + file.getAbsolutePath() - + " since it appears to be a duplicate file and --skip-duplicate-files is set"); - return; - } - current.add(signature); - } - - if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { - System.err.println("Skipping " + file + " since it appears to be a symlink"); - return; - } - - if (!file.exists()) { - System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)"); - return; - } - - files.add(file.toPath()); - } - - public void go() { - try (CpdAnalysis cpd = CpdAnalysis.create(configuration)) { - files.forEach(cpd.files()::addFile); - cpd.performAnalysis(this::collectReport); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void collectReport(CPDReport cpdReport) { - this.report = cpdReport; - } - - public Iterator getMatches() { - return report.getMatches().iterator(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java deleted file mode 100644 index c49b21586c..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ /dev/null @@ -1,345 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: setLanguage, setSourceEncoding, filenameFilter - -package net.sourceforge.pmd.cpd; - -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; -import java.io.File; -import java.io.FilenameFilter; -import java.lang.reflect.Method; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.LoggerFactory; - -import net.sourceforge.pmd.AbstractConfiguration; -import net.sourceforge.pmd.cpd.internal.CpdLanguagePropertiesDefaults; -import net.sourceforge.pmd.internal.util.FileFinder; -import net.sourceforge.pmd.internal.util.FileUtil; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.jsp.JspLanguageModule; -import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; - -/** - * - * @author Brian Remedios - * @author Romain Pelisse - <belaran@gmail.com> - */ -public class CPDConfiguration extends AbstractConfiguration { - - public static final String DEFAULT_LANGUAGE = "java"; - public static final String DEFAULT_RENDERER = "text"; - - private static final Map> RENDERERS = new HashMap<>(); - - - static { - RENDERERS.put(DEFAULT_RENDERER, SimpleRenderer.class); - RENDERERS.put("xml", XMLRenderer.class); - RENDERERS.put("csv", CSVRenderer.class); - RENDERERS.put("csv_with_linecount_per_file", CSVWithLinecountPerFileRenderer.class); - RENDERERS.put("vs", VSRenderer.class); - } - - - private int minimumTileSize; - - private boolean skipDuplicates; - - private String rendererName = DEFAULT_RENDERER; - - private @Nullable CPDReportRenderer cpdReportRenderer; - - private boolean ignoreLiterals; - - private boolean ignoreIdentifiers; - - private boolean ignoreAnnotations; - - private boolean ignoreUsings; - - private boolean ignoreLiteralSequences = false; - - private boolean ignoreIdentifierAndLiteralSequences = false; - - private boolean skipLexicalErrors = false; - - private boolean noSkipBlocks = false; - - private String skipBlocksPattern = CpdLanguagePropertiesDefaults.DEFAULT_SKIP_BLOCKS_PATTERN; - - private boolean help; - - private boolean failOnViolation = true; - - - public CPDConfiguration() { - this(LanguageRegistry.CPD); - } - - public CPDConfiguration(LanguageRegistry languageRegistry) { - super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(CpdAnalysis.class))); - } - - @Override - public void setSourceEncoding(Charset sourceEncoding) { - super.setSourceEncoding(sourceEncoding); - if (cpdReportRenderer != null) { - setRendererEncoding(cpdReportRenderer, sourceEncoding); - } - } - - static CPDReportRenderer createRendererByName(String name, Charset encoding) { - if (name == null || "".equals(name)) { - name = DEFAULT_RENDERER; - } - Class rendererClass = RENDERERS.get(name.toLowerCase(Locale.ROOT)); - if (rendererClass == null) { - Class klass; - try { - klass = Class.forName(name); - if (CPDReportRenderer.class.isAssignableFrom(klass)) { - rendererClass = (Class) klass; - } else { - throw new IllegalArgumentException("Class " + name + " does not implement " + CPDReportRenderer.class); - } - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Cannot find class " + name); - } - } - - CPDReportRenderer renderer; - try { - renderer = rendererClass.getDeclaredConstructor().newInstance(); - setRendererEncoding(renderer, encoding); - } catch (Exception e) { - System.err.println("Couldn't instantiate renderer, defaulting to SimpleRenderer: " + e); - renderer = new SimpleRenderer(); - } - return renderer; - } - - private static void setRendererEncoding(@NonNull Object renderer, Charset encoding) { - try { - PropertyDescriptor encodingProperty = new PropertyDescriptor("encoding", renderer.getClass()); - Method method = encodingProperty.getWriteMethod(); - if (method == null) { - return; - } - if (method.getParameterTypes()[0] == Charset.class) { - method.invoke(renderer, encoding); - } else if (method.getParameterTypes()[0] == String.class) { - method.invoke(renderer, encoding.name()); - } - } catch (IntrospectionException | ReflectiveOperationException ignored) { - // ignored - maybe this renderer doesn't have a encoding property - } - } - - public static Set getRenderers() { - return Collections.unmodifiableSet(RENDERERS.keySet()); - } - - public int getMinimumTileSize() { - return minimumTileSize; - } - - public void setMinimumTileSize(int minimumTileSize) { - this.minimumTileSize = minimumTileSize; - } - - public boolean isSkipDuplicates() { - return skipDuplicates; - } - - public void setSkipDuplicates(boolean skipDuplicates) { - this.skipDuplicates = skipDuplicates; - } - - public String getRendererName() { - return rendererName; - } - - public void setRendererName(String rendererName) { - this.rendererName = rendererName; - if (rendererName == null) { - this.cpdReportRenderer = null; - } - this.cpdReportRenderer = createRendererByName(rendererName, getSourceEncoding()); - } - - - public CPDReportRenderer getCPDReportRenderer() { - return cpdReportRenderer; - } - - void setRenderer(CPDReportRenderer renderer) { - this.cpdReportRenderer = renderer; - } - - public boolean isIgnoreLiterals() { - return ignoreLiterals; - } - - public void setIgnoreLiterals(boolean ignoreLiterals) { - this.ignoreLiterals = ignoreLiterals; - } - - public boolean isIgnoreIdentifiers() { - return ignoreIdentifiers; - } - - public void setIgnoreIdentifiers(boolean ignoreIdentifiers) { - this.ignoreIdentifiers = ignoreIdentifiers; - } - - public boolean isIgnoreAnnotations() { - return ignoreAnnotations; - } - - public void setIgnoreAnnotations(boolean ignoreAnnotations) { - this.ignoreAnnotations = ignoreAnnotations; - } - - public boolean isIgnoreUsings() { - return ignoreUsings; - } - - public void setIgnoreUsings(boolean ignoreUsings) { - this.ignoreUsings = ignoreUsings; - } - - public boolean isIgnoreLiteralSequences() { - return ignoreLiteralSequences; - } - - public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { - this.ignoreLiteralSequences = ignoreLiteralSequences; - } - - public boolean isIgnoreIdentifierAndLiteralSequences() { - return ignoreIdentifierAndLiteralSequences; - } - - public void setIgnoreIdentifierAndLiteralSequences(boolean ignoreIdentifierAndLiteralSequences) { - this.ignoreIdentifierAndLiteralSequences = ignoreIdentifierAndLiteralSequences; - } - - public boolean isSkipLexicalErrors() { - return skipLexicalErrors; - } - - public void setSkipLexicalErrors(boolean skipLexicalErrors) { - this.skipLexicalErrors = skipLexicalErrors; - } - - public boolean isHelp() { - return help; - } - - public void setHelp(boolean help) { - this.help = help; - } - - public boolean isNoSkipBlocks() { - return noSkipBlocks; - } - - public void setNoSkipBlocks(boolean noSkipBlocks) { - this.noSkipBlocks = noSkipBlocks; - } - - public String getSkipBlocksPattern() { - return skipBlocksPattern; - } - - public void setSkipBlocksPattern(String skipBlocksPattern) { - this.skipBlocksPattern = skipBlocksPattern; - } - - public boolean isFailOnViolation() { - return failOnViolation; - } - - public void setFailOnViolation(boolean failOnViolation) { - this.failOnViolation = failOnViolation; - } - - // ------------------- compat extensions -------------------- - private FilenameFilter filenameFilter; - - public void setLanguage(Language language) { - if (language instanceof JavaLanguage) { - filenameFilter = language.getFileFilter(); - setForceLanguageVersion(JavaLanguageModule.getInstance().getDefaultVersion()); - } else if (language instanceof EcmascriptLanguage) { - filenameFilter = language.getFileFilter(); - setForceLanguageVersion(EcmascriptLanguageModule.getInstance().getDefaultVersion()); - } else if (language instanceof JSPLanguage) { - filenameFilter = language.getFileFilter(); - setForceLanguageVersion(JspLanguageModule.getInstance().getDefaultVersion()); - } else if (language instanceof LanguageFactory.CpdLanguageAdapter) { - filenameFilter = language.getFileFilter(); - setForceLanguageVersion(((LanguageFactory.CpdLanguageAdapter) language).getLanguage().getDefaultVersion()); - } else { - throw new UnsupportedOperationException("Language " + language.getName() + " is not supported"); - } - } - - public void setSourceEncoding(String sourceEncoding) { - setSourceEncoding(Charset.forName(Objects.requireNonNull(sourceEncoding))); - } - - public FilenameFilter filenameFilter() { - if (getForceLanguageVersion() == null) { - throw new IllegalStateException("Language is null."); - } - - final FilenameFilter languageFilter = filenameFilter; - final Set exclusions = new HashSet<>(); - - if (getExcludes() != null) { - FileFinder finder = new FileFinder(); - for (Path excludedFile : getExcludes()) { - if (Files.isDirectory(excludedFile)) { - List files = finder.findFilesFrom(excludedFile.toFile(), languageFilter, true); - for (File f : files) { - exclusions.add(FileUtil.normalizeFilename(f.getAbsolutePath())); - } - } else { - exclusions.add(FileUtil.normalizeFilename(excludedFile.toAbsolutePath().toString())); - } - } - } - - return new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - File f = new File(dir, name); - if (exclusions.contains(FileUtil.normalizeFilename(f.getAbsolutePath()))) { - System.err.println("Excluding " + f.getAbsolutePath()); - return false; - } - return languageFilter.accept(dir, name); - } - }; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java deleted file mode 100644 index 366386b1c5..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: implements old interface CPDRenderer, old render(Iterator matches, Writer writer) method - -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.Writer; -import java.util.Iterator; - -import org.apache.commons.lang3.StringEscapeUtils; - -import net.sourceforge.pmd.cpd.renderer.CPDRenderer; -import net.sourceforge.pmd.lang.document.FileLocation; - -/** - * Renders a report to CSV. The CSV format renders each match (duplication) - * as a single line with the following columns: - *

    - *
  • lines (optional): The number of lines the first mark of a match spans. - * Only output if the {@code lineCountPerFile} is disabled (see ctor params).
  • - *
  • tokens: The number of duplicated tokens in a match (size of the match).
  • - *
  • occurrences: The number of duplicates in a match (number of times the tokens were found in distinct places).
  • - *
- * - *

Trailing each line are pairs (or triples, if {@code lineCountPerFile} is enabled) - * of fields describing each file where the duplication was found in the format - * {@code (start line, line count (optional), file path)}. These repeat at least twice. - * - *

Examples

- *

- * Example without {@code lineCountPerFile}: - *

{@code
- * lines,tokens,occurrences
- * 10,75,2,48,/var/file1,73,/var/file2
- * }
- * This describes one match with the following characteristics: - *
    - *
  • The first duplicate instance is 10 lines long; - *
  • 75 duplicated tokens; - *
  • 2 duplicate instances; - *
  • The first duplicate instance is in file {@code /var/file1} and starts at line 48;
  • - *
  • The second duplicate instance is in file {@code /var/file2} and starts at line 73.
  • - *
- *

- * Example with {@code lineCountPerFile}: - *

{@code
- * tokens,occurrences
- * 75,2,48,10,/var/file1,73,12,/var/file2
- * }
- * This describes one match with the following characteristics: - *
    - *
  • 75 duplicated tokens - *
  • 2 duplicate instances - *
  • The first duplicate instance is in file {@code /var/file1}, starts at line 48, and is 10 lines long;
  • - *
  • The second duplicate instance is in file {@code /var/file2}, starts at line 73, and is 12 lines long.
  • - *
- */ -public class CSVRenderer implements CPDReportRenderer, CPDRenderer { - - private final char separator; - private final boolean lineCountPerFile; - - public static final char DEFAULT_SEPARATOR = ','; - public static final boolean DEFAULT_LINECOUNTPERFILE = false; - - public CSVRenderer() { - this(DEFAULT_SEPARATOR, DEFAULT_LINECOUNTPERFILE); - } - - public CSVRenderer(boolean lineCountPerFile) { - this(DEFAULT_SEPARATOR, lineCountPerFile); - } - - public CSVRenderer(char separatorChar) { - this(separatorChar, DEFAULT_LINECOUNTPERFILE); - } - - public CSVRenderer(char separatorChar, boolean lineCountPerFile) { - this.separator = separatorChar; - this.lineCountPerFile = lineCountPerFile; - } - - @Override - public void render(CPDReport report, Writer writer) throws IOException { - if (!lineCountPerFile) { - writer.append("lines").append(separator); - } - writer.append("tokens").append(separator).append("occurrences").append(System.lineSeparator()); - - for (Match match : report.getMatches()) { - if (!lineCountPerFile) { - writer.append(String.valueOf(match.getLineCount())).append(separator); - } - writer.append(String.valueOf(match.getTokenCount())).append(separator) - .append(String.valueOf(match.getMarkCount())).append(separator); - for (Iterator marks = match.iterator(); marks.hasNext();) { - Mark mark = marks.next(); - FileLocation loc = mark.getLocation(); - - writer.append(String.valueOf(loc.getStartLine())).append(separator); - if (lineCountPerFile) { - writer.append(String.valueOf(loc.getLineCount())).append(separator); - } - writer.append(StringEscapeUtils.escapeCsv(report.getDisplayName(loc.getFileId()))); - if (marks.hasNext()) { - writer.append(separator); - } - } - writer.append(System.lineSeparator()); - } - writer.flush(); - } - - // ------------------- compat extensions -------------------- - @Override - public void render(Iterator matches, Writer writer) throws IOException { - RendererHelper.render(matches, writer, this); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java deleted file mode 100644 index ecd2a5a2cf..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.cpd; - -/** - * - * @author Zev Blut zb@ubit.com - */ -public class EcmascriptLanguage extends AbstractLanguage { - public EcmascriptLanguage() { - super("JavaScript", "ecmascript", new EcmascriptTokenizer(), ".js"); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java deleted file mode 100644 index a63b348db2..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public class EcmascriptTokenizer extends net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptCpdLexer implements Tokenizer { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java deleted file mode 100644 index e1501bad5d..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.cpd; - -public class JSPLanguage extends AbstractLanguage { - public JSPLanguage() { - super("JSP", "jsp", new JSPTokenizer(), ".jsp", ".jspx", ".jspf", ".tag"); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java deleted file mode 100644 index 2e6f8d112b..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public class JSPTokenizer extends net.sourceforge.pmd.lang.jsp.cpd.JspCpdLexer implements Tokenizer { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java deleted file mode 100644 index 344a5d9bbc..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 -// Changes: setProperties doesn't work, provide properties in constructor already - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -public class JavaLanguage extends AbstractLanguage { - public JavaLanguage() { - this(System.getProperties()); - } - - public JavaLanguage(Properties properties) { - super("Java", "java", new JavaTokenizer(properties), ".java"); - } - - @Override - public final void setProperties(Properties properties) { - // note: this might be actually incompatible - throw new UnsupportedOperationException(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java deleted file mode 100644 index 39f2013699..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -import net.sourceforge.pmd.lang.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties; - -public class JavaTokenizer extends net.sourceforge.pmd.lang.java.cpd.JavaCpdLexer implements Tokenizer { - public JavaTokenizer(Properties properties) { - super(convertLanguageProperties(properties)); - } - - public static final String IGNORE_LITERALS = "ignore_literals"; - public static final String IGNORE_IDENTIFIERS = "ignore_identifiers"; - public static final String IGNORE_ANNOTATIONS = "ignore_annotations"; - - private static JavaLanguageProperties convertLanguageProperties(Properties properties) { - boolean ignoreAnnotations = Boolean.parseBoolean(properties.getProperty(IGNORE_ANNOTATIONS, "false")); - boolean ignoreLiterals = Boolean.parseBoolean(properties.getProperty(IGNORE_LITERALS, "false")); - boolean ignoreIdentifiers = Boolean.parseBoolean(properties.getProperty(IGNORE_IDENTIFIERS, "false")); - - JavaLanguageProperties javaLanguageProperties = (JavaLanguageProperties) JavaLanguageModule.getInstance().newPropertyBundle(); - javaLanguageProperties.setProperty(CpdLanguageProperties.CPD_IGNORE_METADATA, ignoreAnnotations); - javaLanguageProperties.setProperty(CpdLanguageProperties.CPD_ANONYMIZE_LITERALS, ignoreLiterals); - javaLanguageProperties.setProperty(CpdLanguageProperties.CPD_ANONYMIZE_IDENTIFIERS, ignoreIdentifiers); - - return javaLanguageProperties; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Language.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Language.java deleted file mode 100644 index d2ee070bb5..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Language.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.cpd; - -import java.io.FilenameFilter; -import java.util.List; -import java.util.Properties; - -public interface Language { - String getName(); - - String getTerseName(); - - Tokenizer getTokenizer(); - - FilenameFilter getFileFilter(); - - void setProperties(Properties properties); - - List getExtensions(); -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java deleted file mode 100644 index 8191bcb298..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class is here just to make maven-pmd-plugin compile with -// pmd 7.0.0 including this compat6 module. -// It would only be used, if a custom language (other than java, jsp or javascript) -// would be requested. - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; -import java.util.stream.Collectors; - -import net.sourceforge.pmd.lang.LanguagePropertyBundle; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -public final class LanguageFactory { - private LanguageFactory() { - // utility class - } - - public static Language createLanguage(String name, Properties properties) { - CpdCapableLanguage cpdLanguage = (CpdCapableLanguage) LanguageRegistry.CPD.getLanguageById(name); - if (cpdLanguage != null) { - return new CpdLanguageAdapter(cpdLanguage, properties); - } - throw new UnsupportedOperationException("Language " + name + " is not supported"); - } - - public static class CpdLanguageAdapter extends AbstractLanguage { - private CpdCapableLanguage language; - - public CpdLanguageAdapter(CpdCapableLanguage cpdCapableLanguage, Properties properties) { - super(cpdCapableLanguage.getName(), cpdCapableLanguage.getId(), createLexer(cpdCapableLanguage, properties), convertExtensions(cpdCapableLanguage)); - this.language = cpdCapableLanguage; - } - - private static Tokenizer createLexer(CpdCapableLanguage cpdCapableLanguage, Properties properties) { - LanguagePropertyBundle propertyBundle = cpdCapableLanguage.newPropertyBundle(); - for (String propName : properties.stringPropertyNames()) { - PropertyDescriptor propertyDescriptor = propertyBundle.getPropertyDescriptor(propName); - if (propertyDescriptor != null) { - setProperty(propertyBundle, propertyDescriptor, properties.getProperty(propName)); - } - } - CpdLexer cpdLexer = cpdCapableLanguage.createCpdLexer(propertyBundle); - return cpdLexer::tokenize; - } - - private static void setProperty(LanguagePropertyBundle propertyBundle, PropertyDescriptor propertyDescriptor, String stringValue) { - T value = propertyDescriptor.serializer().fromString(stringValue); - propertyBundle.setProperty(propertyDescriptor, value); - } - - private static String[] convertExtensions(CpdCapableLanguage cpdCapableLanguage) { - return cpdCapableLanguage.getExtensions().stream().map(s -> "." + s).collect(Collectors.toList()).toArray(new String[0]); - } - - public CpdCapableLanguage getLanguage() { - return language; - } - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Mark.java deleted file mode 100644 index 2bcfdc15ea..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: getFilename - -package net.sourceforge.pmd.cpd; - -import java.util.Objects; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.lang.document.FileId; -import net.sourceforge.pmd.lang.document.FileLocation; -import net.sourceforge.pmd.lang.document.TextRange2d; - -/** - * A range of tokens in a source file, identified by a start and end - * token (both included in the range). The start and end token may be - * the same token. - */ -public final class Mark implements Comparable { - - private final @NonNull TokenEntry token; - private @Nullable TokenEntry endToken; - - Mark(@NonNull TokenEntry token) { - this.token = token; - } - - @NonNull TokenEntry getToken() { - return this.token; - } - - @NonNull TokenEntry getEndToken() { - return endToken == null ? token : endToken; - } - - /** - * Return the location of this source range in the source file. - */ - public FileLocation getLocation() { - TokenEntry endToken = getEndToken(); - return FileLocation.range( - getFileId(), - TextRange2d.range2d(token.getBeginLine(), token.getBeginColumn(), - endToken.getEndLine(), endToken.getEndColumn())); - } - - FileId getFileId() { - return token.getFileId(); - } - - public int getBeginTokenIndex() { - return this.token.getIndex(); - } - - public int getEndTokenIndex() { - return getEndToken().getIndex(); - } - - void setEndToken(@NonNull TokenEntry endToken) { - assert endToken.getFileId().equals(token.getFileId()) - : "Tokens are not from the same file"; - this.endToken = endToken; - } - - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + token.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Mark other = (Mark) obj; - return Objects.equals(token, other.token) - && Objects.equals(endToken, other.endToken); - } - - @Override - public int compareTo(Mark other) { - return getToken().compareTo(other.getToken()); - } - - // ------------------- compat extensions -------------------- - public String getFilename() { - return this.token.getFileId().getOriginalPath(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/RendererHelper.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/RendererHelper.java deleted file mode 100644 index c25a80d964..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/RendererHelper.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; - -final class RendererHelper { - private RendererHelper() { - // utility class - } - - static void render(Iterator matches, Writer writer, CPDReportRenderer renderer) throws IOException { - List matchesList = new ArrayList<>(); - matches.forEachRemaining(matchesList::add); - - List textFiles = new ArrayList<>(); - Set paths = new HashSet<>(); - for (Match match : matchesList) { - for (Mark mark : match.getMarkSet()) { - paths.add(mark.getFilename()); - } - } - for (String path : paths) { - textFiles.add(TextFile.forPath(Paths.get(path), StandardCharsets.UTF_8, JavaLanguageModule.getInstance().getDefaultVersion())); - } - - try (SourceManager sourceManager = new SourceManager(textFiles)) { - CPDReport report = new CPDReport(sourceManager, matchesList, Collections.emptyMap(), Collections.emptyList()); - renderer.render(report, writer); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java deleted file mode 100644 index d4aaff8038..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: implements old interface CPDRenderer, old render(Iterator matches, Writer writer) method - -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.Iterator; - -import net.sourceforge.pmd.cpd.renderer.CPDRenderer; -import net.sourceforge.pmd.lang.document.Chars; -import net.sourceforge.pmd.lang.document.FileLocation; -import net.sourceforge.pmd.util.StringUtil; - -public class SimpleRenderer implements CPDReportRenderer, CPDRenderer { - - private String separator; - private boolean trimLeadingWhitespace; - - public static final String DEFAULT_SEPARATOR = "====================================================================="; - - public SimpleRenderer() { - this(false); - } - - public SimpleRenderer(boolean trimLeadingWhitespace) { - this(DEFAULT_SEPARATOR); - this.trimLeadingWhitespace = trimLeadingWhitespace; - } - - public SimpleRenderer(String theSeparator) { - separator = theSeparator; - } - - @Override - public void render(CPDReport report, Writer writer0) throws IOException { - PrintWriter writer = new PrintWriter(writer0); - Iterator matches = report.getMatches().iterator(); - if (matches.hasNext()) { - renderOn(report, writer, matches.next()); - } - - while (matches.hasNext()) { - Match match = matches.next(); - writer.println(separator); - renderOn(report, writer, match); - } - writer.flush(); - } - - private void renderOn(CPDReport report, PrintWriter writer, Match match) throws IOException { - - writer.append("Found a ").append(String.valueOf(match.getLineCount())).append(" line (").append(String.valueOf(match.getTokenCount())) - .append(" tokens) duplication in the following files: ").println(); - - for (Mark mark : match) { - FileLocation loc = mark.getLocation(); - writer.append("Starting at line ") - .append(String.valueOf(loc.getStartLine())) - .append(" of ").append(report.getDisplayName(loc.getFileId())) - .println(); - } - - writer.println(); // add a line to separate the source from the desc above - - Chars source = report.getSourceCodeSlice(match.getFirstMark()); - - if (trimLeadingWhitespace) { - for (Chars line : StringUtil.linesWithTrimIndent(source)) { - line.writeFully(writer); - writer.println(); - } - return; - } - - source.writeFully(writer); - writer.println(); - } - - // ------------------- compat extensions -------------------- - @Override - public void render(Iterator matches, Writer writer) throws IOException { - RendererHelper.render(matches, writer, this); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java deleted file mode 100644 index ece7fc3618..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public interface Tokenizer extends CpdLexer { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java deleted file mode 100644 index 31f2b41d0a..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ /dev/null @@ -1,165 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: implements old interface CPDRenderer, old render(Iterator matches, Writer writer) method - -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.Writer; -import java.util.Iterator; -import java.util.Map; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import net.sourceforge.pmd.cpd.renderer.CPDRenderer; -import net.sourceforge.pmd.lang.document.Chars; -import net.sourceforge.pmd.lang.document.FileId; -import net.sourceforge.pmd.lang.document.FileLocation; -import net.sourceforge.pmd.util.StringUtil; - -/** - * @author Philippe T'Seyen - original implementation - * @author Romain Pelisse - javax.xml implementation - * - */ -public final class XMLRenderer implements CPDReportRenderer, CPDRenderer { - - private String encoding; - - /** - * Creates a XML Renderer with the default (platform dependent) encoding. - */ - public XMLRenderer() { - this(null); - } - - /** - * Creates a XML Renderer with a specific output encoding. - * - * @param encoding - * the encoding to use or null. If null, default (platform - * dependent) encoding is used. - */ - public XMLRenderer(String encoding) { - setEncoding(encoding); - } - - public void setEncoding(String encoding) { - if (encoding != null) { - this.encoding = encoding; - } else { - this.encoding = System.getProperty("file.encoding"); - } - } - - public String getEncoding() { - return this.encoding; - } - - private Document createDocument() { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder parser = factory.newDocumentBuilder(); - return parser.newDocument(); - } catch (ParserConfigurationException e) { - throw new IllegalStateException(e); - } - } - - private void dumpDocToWriter(Document doc, Writer writer) { - try { - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.VERSION, "1.0"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.setOutputProperty(OutputKeys.ENCODING, encoding); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "codefragment"); - transformer.transform(new DOMSource(doc), new StreamResult(writer)); - } catch (TransformerException e) { - throw new IllegalStateException(e); - } - } - - - @Override - public void render(final CPDReport report, final Writer writer) throws IOException { - final Document doc = createDocument(); - final Element root = doc.createElement("pmd-cpd"); - final Map numberOfTokensPerFile = report.getNumberOfTokensPerFile(); - doc.appendChild(root); - - for (final Map.Entry pair : numberOfTokensPerFile.entrySet()) { - final Element fileElement = doc.createElement("file"); - fileElement.setAttribute("path", report.getDisplayName(pair.getKey())); - fileElement.setAttribute("totalNumberOfTokens", String.valueOf(pair.getValue())); - root.appendChild(fileElement); - } - - for (Match match : report.getMatches()) { - Element dupElt = createDuplicationElement(doc, match); - addFilesToDuplicationElement(doc, dupElt, match, report); - addCodeSnippet(doc, dupElt, match, report); - root.appendChild(dupElt); - } - dumpDocToWriter(doc, writer); - writer.flush(); - } - - private void addFilesToDuplicationElement(Document doc, Element duplication, Match match, CPDReport report) { - for (Mark mark : match) { - final Element file = doc.createElement("file"); - FileLocation loc = mark.getLocation(); - file.setAttribute("line", String.valueOf(loc.getStartLine())); - // only remove invalid characters, escaping is done by the DOM impl. - String filenameXml10 = StringUtil.removedInvalidXml10Characters(report.getDisplayName(loc.getFileId())); - file.setAttribute("path", filenameXml10); - file.setAttribute("endline", String.valueOf(loc.getEndLine())); - file.setAttribute("column", String.valueOf(loc.getStartColumn())); - file.setAttribute("endcolumn", String.valueOf(loc.getEndColumn())); - file.setAttribute("begintoken", String.valueOf(mark.getBeginTokenIndex())); - file.setAttribute("endtoken", String.valueOf(mark.getEndTokenIndex())); - duplication.appendChild(file); - } - } - - private void addCodeSnippet(Document doc, Element duplication, Match match, CPDReport report) { - Chars codeSnippet = report.getSourceCodeSlice(match.getFirstMark()); - if (codeSnippet != null) { - // the code snippet has normalized line endings - String platformSpecific = codeSnippet.toString().replace("\n", System.lineSeparator()); - Element codefragment = doc.createElement("codefragment"); - // only remove invalid characters, escaping is not necessary in CDATA. - // if the string contains the end marker of a CDATA section, then the DOM impl will - // create two cdata sections automatically. - codefragment.appendChild(doc.createCDATASection(StringUtil.removedInvalidXml10Characters(platformSpecific))); - duplication.appendChild(codefragment); - } - } - - private Element createDuplicationElement(Document doc, Match match) { - Element duplication = doc.createElement("duplication"); - duplication.setAttribute("lines", String.valueOf(match.getLineCount())); - duplication.setAttribute("tokens", String.valueOf(match.getTokenCount())); - return duplication; - } - - // ------------------- compat extensions -------------------- - @Override - public void render(Iterator matches, Writer writer) throws IOException { - RendererHelper.render(matches, writer, this); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDRenderer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDRenderer.java deleted file mode 100644 index 5776309eba..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDRenderer.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.cpd.renderer; - -import java.io.IOException; -import java.io.Writer; -import java.util.Iterator; - -import net.sourceforge.pmd.cpd.Match; - -/** - * @deprecated Use {@link net.sourceforge.pmd.cpd.CPDReportRenderer} - */ -@Deprecated -public interface CPDRenderer { - void render(Iterator matches, Writer writer) throws IOException; -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java deleted file mode 100644 index 8cf677dabd..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: The following methods are still here, but deleted in 7.0.0 -// LanguageRegistry#getLanguage -// LanguageRegistry#findLanguageByTerseName -// LanguageRegistry#findByExtension - -package net.sourceforge.pmd.lang; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -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.function.Predicate; -import java.util.stream.Collectors; - -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.cpd.CpdCapableLanguage; -import net.sourceforge.pmd.util.CollectionUtil; - -/** - * 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 implements Iterable { - - private static final Logger LOG = LoggerFactory.getLogger(LanguageRegistry.class); - - private static final LanguageRegistry ALL_LANGUAGES = - loadLanguages(LanguageRegistry.class.getClassLoader()); - - /** - * 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 = ALL_LANGUAGES.filter(it -> it instanceof PmdCapableLanguage); - - /** - * Contains the languages that support CPD and are found on the classpath - * of the classloader of this class. - */ - public static final LanguageRegistry CPD = ALL_LANGUAGES.filter(it -> it instanceof CpdCapableLanguage); - - private final Set languages; - - private final Map languagesById; - private final Map languagesByFullName; - - /** - * 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::getId, String::compareToIgnoreCase)) - .collect(CollectionUtil.toUnmodifiableSet()); - this.languagesById = CollectionUtil.associateBy(languages, Language::getId); - this.languagesByFullName = CollectionUtil.associateBy(languages, Language::getName); - } - - /** - * Create a new registry with the languages that satisfy the predicate. - */ - public LanguageRegistry filter(Predicate filterFun) { - return new LanguageRegistry(languages.stream().filter(filterFun) - .collect(Collectors.toSet())); - } - - /** - * Creates a language registry containing a single language. Note - * that this may be inconvertible to a {@link LanguageProcessorRegistry} - * if the language depends on other languages. - */ - public static LanguageRegistry singleton(Language l) { - return new LanguageRegistry(Collections.singleton(l)); - } - - /** - * Creates a language registry containing the given language and - * its dependencies, fetched from this language registry or the - * parameter. - * - * @throws IllegalStateException If dependencies cannot be fulfilled. - */ - public LanguageRegistry getDependenciesOf(Language lang) { - Set result = new HashSet<>(); - result.add(lang); - addDepsOrThrow(lang, result); - return new LanguageRegistry(result); - } - - private void addDepsOrThrow(Language l, Set languages) { - for (String depId : l.getDependencies()) { - Language dep = getLanguageById(depId); - if (dep == null) { - throw new IllegalStateException( - "Cannot find language " + depId + " in " + this); - } - if (languages.add(dep)) { - addDepsOrThrow(dep, languages); - } - } - } - - @Override - public @NonNull Iterator iterator() { - return languages.iterator(); - } - - /** - * Create a new registry by loading the languages registered via {@link ServiceLoader} - * on the classpath of the given classloader. - * - * @param classLoader A classloader - */ - 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::getId, 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 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) LanguageRegistry.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); - } - - /** - * 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 - */ - public @Nullable Language getLanguageByFullName(String languageName) { - return languagesByFullName.get(languageName); - } - - /** - * Returns a language from its {@linkplain Language#getId() terse name} - * (eg {@code "java"}). This is case sensitive. - * - * @param terseName Language terse name - * - * @return A language, or null if the name is unknown - * - * @deprecated Use {@link #getLanguageById(String) LanguageRegistry.PMD.getLanguageById}. - */ - @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 : PMD.getLanguages()) { - if (language.hasExtension(extensionWithoutDot)) { - languages.add(language); - } - } - 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(", ")); - } - - @Override - public String toString() { - return "LanguageRegistry(" + commaSeparatedList(Language::getId) + ")"; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/Rule.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/Rule.java deleted file mode 100644 index a152534e5f..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/Rule.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// The class net.sourceforge.pmd.Rule has been moved into sub-package rule -// in 7.0.0-SNAPSHOT. All rules should be interchangeable. - -package net.sourceforge.pmd.lang.rule; - -public interface Rule extends net.sourceforge.pmd.Rule { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java deleted file mode 100644 index 5cc43b7131..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// Copied from 7.0.0-SNAPSHOT -// Changes: constructors are public again - -package net.sourceforge.pmd.lang.rule; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId; - -/** - * An exception that is thrown when something wrong occurs while - * {@linkplain RuleSetLoader loading rulesets}. This may be because the - * XML is not well-formed, does not respect the ruleset schema, is - * not a valid ruleset or is otherwise unparsable. - */ -public class RuleSetLoadException extends RuntimeException { - - /** - * @apiNote Internal API. - */ - public RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { - super("Cannot load ruleset " + rsetId + ": " + cause.getMessage(), cause); - } - - /** - * @apiNote Internal API. - */ - public RuleSetLoadException(RuleSetReferenceId rsetId, String message) { - super("Cannot load ruleset " + rsetId + ": " + message); - } - -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java deleted file mode 100644 index eb11ac7e77..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.rule; - -public class XPathRule extends net.sourceforge.pmd.lang.rule.xpath.XPathRule { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java deleted file mode 100644 index dbf505107a..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.rule.internal; - -import java.util.Collection; - -import net.sourceforge.pmd.lang.rule.RuleSet; - -public class RuleSets extends net.sourceforge.pmd.RuleSets { - public RuleSets(net.sourceforge.pmd.RuleSets ruleSets) { - super(ruleSets); - } - - public RuleSets(net.sourceforge.pmd.lang.rule.internal.RuleSets ruleSets) { - super(ruleSets); - } - - public RuleSets(Collection ruleSets) { - super(ruleSets); - } - - public RuleSets(RuleSet ruleSet) { - super(ruleSet); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRule.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRule.java deleted file mode 100644 index 6f567f1ed7..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/xpath/XPathRule.java +++ /dev/null @@ -1,190 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: not final anymore to allow a subclass in the old package - -package net.sourceforge.pmd.lang.rule.xpath; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ContextedRuntimeException; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.sourceforge.pmd.lang.LanguageProcessor; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.rule.Rule; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; -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.reporting.RuleContext; -import net.sourceforge.pmd.util.IteratorUtil; - - -/** - * Rule that tries to match an XPath expression against a DOM view of an AST. - */ -public /*final*/ class XPathRule extends AbstractRule { - - private static final Logger LOG = LoggerFactory.getLogger(XPathRule.class); - - /** - * @deprecated Use {@link #XPathRule(XPathVersion, String)} - */ - @Deprecated - public static final PropertyDescriptor XPATH_DESCRIPTOR = - PropertyFactory.stringProperty("xpath") - .desc("XPath expression") - .defaultValue("") - .build(); - - /** - * This is initialized only once when calling {@link #apply(Node, RuleContext)} or {@link #getTargetSelector()}. - */ - private SaxonXPathRuleQuery xpathRuleQuery; - - - // this is shared with rules forked by deepCopy, used by the XPathRuleQuery - private DeprecatedAttrLogger attrLogger = DeprecatedAttrLogger.create(this); - - - /** - * @deprecated This is now only used by the ruleset loader. When - * we have syntactic sugar for XPath rules in the XML, we won't - * need this anymore. - */ - @Deprecated - public XPathRule() { - definePropertyDescriptor(XPATH_DESCRIPTOR); - } - - /** - * Make a new XPath rule with the given version + expression - * - * @param version Version of the XPath language - * @param expression XPath expression - * - * @throws NullPointerException If any of the arguments is null - */ - public XPathRule(XPathVersion version, String expression) { - this(); - - Objects.requireNonNull(version, "XPath version is null"); - Objects.requireNonNull(expression, "XPath expression is null"); - - setProperty(XPathRule.XPATH_DESCRIPTOR, expression); - } - - - @Override - public Rule deepCopy() { - XPathRule rule = (XPathRule) super.deepCopy(); - rule.attrLogger = this.attrLogger; - return rule; - } - - /** - * Returns the XPath expression that implements this rule. - */ - public String getXPathExpression() { - return getProperty(XPATH_DESCRIPTOR); - } - - - @Override - public void apply(Node target, RuleContext ctx) { - SaxonXPathRuleQuery query = getQueryMaybeInitialize(); - - List nodesWithViolation; - try { - nodesWithViolation = query.evaluate(target); - } catch (PmdXPathException e) { - throw addExceptionContext(e); - } - - for (Node nodeWithViolation : nodesWithViolation) { - // see Deprecate getImage/@Image #4787 https://github.com/pmd/pmd/issues/4787 - String messageArg = nodeWithViolation.getImage(); - // Nodes might already have been refactored to not use getImage anymore. - // Therefore, try several other common names - if (messageArg == null) { - messageArg = getFirstMessageArgFromNode(nodeWithViolation, "Name", "SimpleName", "MethodName"); - } - ctx.addViolation(nodeWithViolation, messageArg); - } - } - - private String getFirstMessageArgFromNode(Node node, String... attributeNames) { - List nameList = Arrays.asList(attributeNames); - return IteratorUtil.toStream(node.getXPathAttributesIterator()) - .filter(a -> nameList.contains(a.getName())) - .findFirst() - .map(Attribute::getStringValue) - .orElse(null); - } - - private ContextedRuntimeException addExceptionContext(PmdXPathException e) { - return e.addRuleName(getName()); - } - - @Override - public void initialize(LanguageProcessor languageProcessor) { - String xpath = getXPathExpression(); - XPathVersion version = XPathVersion.DEFAULT; - - try { - xpathRuleQuery = new SaxonXPathRuleQuery(xpath, - version, - getPropertiesByPropertyDescriptor(), - languageProcessor.services().getXPathHandler(), - attrLogger); - } catch (PmdXPathException e) { - throw addExceptionContext(e); - } - } - - private SaxonXPathRuleQuery getQueryMaybeInitialize() throws PmdXPathException { - if (xpathRuleQuery == null) { - throw new IllegalStateException("Not initialized"); - } - return xpathRuleQuery; - } - - - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - - List visits = getQueryMaybeInitialize().getRuleChainVisits(); - - logXPathRuleChainUsage(!visits.isEmpty()); - - return visits.isEmpty() ? RuleTargetSelector.forRootOnly() - : RuleTargetSelector.forXPathNames(visits); - } - - - private void logXPathRuleChainUsage(boolean usesRuleChain) { - LOG.debug("{} rule chain for XPath rule: {} ({})", - usesRuleChain ? "Using" : "no", - getName(), - getRuleSetName()); - } - - - @Override - public String dysfunctionReason() { - if (StringUtils.isBlank(getXPathExpression())) { - return "Missing XPath expression"; - } - return null; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/renderers/Renderer.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/renderers/Renderer.java deleted file mode 100644 index 3013dbc7d1..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/renderers/Renderer.java +++ /dev/null @@ -1,288 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT -// Changes: renderFileReport - -package net.sourceforge.pmd.renderers; - -import java.io.IOException; -import java.io.Writer; - -import net.sourceforge.pmd.annotation.Experimental; -import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.benchmark.TimedOperation; -import net.sourceforge.pmd.benchmark.TimedOperationCategory; -import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertySource; -import net.sourceforge.pmd.reporting.FileAnalysisListener; -import net.sourceforge.pmd.reporting.FileNameRenderer; -import net.sourceforge.pmd.reporting.GlobalAnalysisListener; -import net.sourceforge.pmd.reporting.ListenerInitializer; -import net.sourceforge.pmd.reporting.Report; -import net.sourceforge.pmd.reporting.Report.ConfigurationError; -import net.sourceforge.pmd.reporting.Report.GlobalReportBuilderListener; -import net.sourceforge.pmd.reporting.Report.ProcessingError; -import net.sourceforge.pmd.reporting.Report.ReportBuilderListener; -import net.sourceforge.pmd.reporting.Report.SuppressedViolation; -import net.sourceforge.pmd.reporting.RuleViolation; - -/** - * This is an interface for rendering a Report. When a Renderer is being - * invoked, the sequence of method calls is something like the following: - *
    - *
  1. Renderer construction/initialization
  2. - *
  3. {@link Renderer#setShowSuppressedViolations(boolean)}
  4. - *
  5. {@link Renderer#setWriter(Writer)}
  6. - *
  7. {@link Renderer#start()}
  8. - *
  9. {@link Renderer#startFileAnalysis(TextFile)} for each source file - * processed
  10. - *
  11. {@link Renderer#renderFileReport(Report)} for each Report instance
  12. - *
  13. {@link Renderer#end()}
  14. - *
- *

- * An implementation of the Renderer interface is expected to have a default - * constructor. Properties should be defined using the - * {@link #definePropertyDescriptor(PropertyDescriptor)} - * method. After the instance is created, the property values are set. This - * means, you won't have access to property values in your constructor. - */ -// TODO Are implementations expected to be thread-safe? -public interface Renderer extends PropertySource { - - /** - * Get the name of the Renderer. - * - * @return The name of the Renderer. - */ - @Override - String getName(); - - /** - * Set the name of the Renderer. - * - * @param name - * The name of the Renderer. - */ - void setName(String name); - - /** - * Get the description of the Renderer. - * - * @return The description of the Renderer. - */ - String getDescription(); - - /** - * Return the default filename extension to use. - * - * @return String - */ - String defaultFileExtension(); - - /** - * Set the description of the Renderer. - * - * @param description - * The description of the Renderer. - */ - void setDescription(String description); - - /** - * Get the indicator for whether to show suppressed violations. - * - * @return true if suppressed violations should show, - * false otherwise. - */ - boolean isShowSuppressedViolations(); - - /** - * Set the indicator for whether to show suppressed violations. - * - * @param showSuppressedViolations - * Whether to show suppressed violations. - */ - void setShowSuppressedViolations(boolean showSuppressedViolations); - - /** - * Get the Writer for the Renderer. - * - * @return The Writer. - */ - Writer getWriter(); - - /** - * Set the {@link FileNameRenderer} used to render file paths to the report. - * Note that this renderer does not have to use the parameter to output paths. - * Some report formats require a specific format for paths (eg a URI), and are - * allowed to circumvent the provided strategy. - * - * @param fileNameRenderer a non-null file name renderer - */ - void setFileNameRenderer(FileNameRenderer fileNameRenderer); - - /** - * Set the Writer for the Renderer. - * - * @param writer The Writer. - */ - void setWriter(Writer writer); - - /** - * This method is called before any source files are processed. The Renderer - * will have been fully initialized by the time this method is called, so - * the Writer and other state will be available. - * - * @throws IOException - */ - void start() throws IOException; - - /** - * This method is called each time a source file is processed. It is called - * after {@link Renderer#start()}, but before - * {@link Renderer#renderFileReport(Report)} and {@link Renderer#end()}. - * - * This method may be invoked by different threads which are processing - * files independently. Therefore, any non-trivial implementation of this - * method needs to be thread-safe. - * - * @param dataSource - * The source file. - */ - void startFileAnalysis(TextFile dataSource); - - /** - * Render the given file Report. There may be multiple Report instances - * which need to be rendered if produced by different threads. It is called - * after {@link Renderer#start()} and - * {@link Renderer#startFileAnalysis(TextFile)}, but before - * {@link Renderer#end()}. - * - * @param report - * A file Report. - * @throws IOException - * - * @see Report - */ - void renderFileReport(Report report) throws IOException; - - /** - * This method is at the very end of the Rendering process, after - * {@link Renderer#renderFileReport(Report)}. - */ - void end() throws IOException; - - void flush() throws IOException; - - /** - * Sets the filename where the report should be written to. If no filename is provided, - * the renderer should write to stdout. - * - *

Implementations must initialize the writer of the renderer. - * - *

See {@link AbstractRenderer#setReportFile(String)} for the default impl. - * - * @param reportFilename the filename (optional). - */ - @Experimental - void setReportFile(String reportFilename); - - - - /** - * Returns a new analysis listener, that handles violations by rendering - * them in an implementation-defined way. - */ - // TODO the default implementation matches the current behavior, - // ie violations are batched by file and forwarded to the renderer - // when the file is done. Many renderers could directly handle - // violations as they come though. - default GlobalAnalysisListener newListener() throws IOException { - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { - this.start(); - } - - return new GlobalAnalysisListener() { - - // guard for the close routine - final Object reportMergeLock = new Object(); - - final GlobalReportBuilderListener configErrorReport = new GlobalReportBuilderListener(); - - @Override - public void onConfigError(ConfigurationError error) { - configErrorReport.onConfigError(error); - } - - @Override - public ListenerInitializer initializer() { - return new ListenerInitializer() { - @Override - public void setFileNameRenderer(FileNameRenderer fileNameRenderer) { - Renderer.this.setFileNameRenderer(fileNameRenderer); - } - }; - } - - @Override - public FileAnalysisListener startFileAnalysis(TextFile file) { - Renderer renderer = Renderer.this; - - renderer.startFileAnalysis(file); // this routine is thread-safe by contract - return new FileAnalysisListener() { - final ReportBuilderListener reportBuilder = new ReportBuilderListener(); - - @Override - public void onRuleViolation(RuleViolation violation) { - reportBuilder.onRuleViolation(violation); - } - - @Override - public void onSuppressedRuleViolation(SuppressedViolation violation) { - reportBuilder.onSuppressedRuleViolation(violation); - } - - @Override - public void onError(ProcessingError error) { - reportBuilder.onError(error); - } - - @Override - public void close() throws Exception { - reportBuilder.close(); - synchronized (reportMergeLock) { - // TODO renderFileReport should be thread-safe instead - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { - renderer.renderFileReport(reportBuilder.getResult()); - } - } - } - - @Override - public String toString() { - return "FileRendererListener[" + Renderer.this + "]"; - } - }; - } - - @Override - public void close() throws Exception { - configErrorReport.close(); - Renderer.this.renderFileReport(configErrorReport.getResult()); - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { - end(); - flush(); - } - } - }; - } - - // --- compat - default void renderFileReport(net.sourceforge.pmd.Report report) throws IOException { - Report newReport = new Report(); - newReport.merge(report); - renderFileReport(newReport); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/Report.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/Report.java deleted file mode 100644 index db6ae84f83..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/Report.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.reporting; - -import java.util.function.Predicate; - -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.lang.document.FileId; - -public class Report extends net.sourceforge.pmd.Report { - - public static class ConfigurationError extends net.sourceforge.pmd.Report.ConfigurationError { - public ConfigurationError(Rule theRule, String theIssue) { - super(theRule, theIssue); - } - - @Override - public net.sourceforge.pmd.lang.rule.Rule rule() { - return (net.sourceforge.pmd.lang.rule.Rule) super.rule(); - } - } - - public static class ProcessingError extends net.sourceforge.pmd.Report.ProcessingError { - public ProcessingError(Throwable error, FileId file) { - super(error, file); - } - } - - public static class SuppressedViolation extends net.sourceforge.pmd.Report.SuppressedViolation { - private final RuleViolation rv; - - public SuppressedViolation(RuleViolation rv, ViolationSuppressor suppressor, String userMessage) { - super(rv, suppressor, userMessage); - this.rv = rv; - } - - @Override - public net.sourceforge.pmd.reporting.RuleViolation getRuleViolation() { - return rv; - } - } - - public static final class GlobalReportBuilderListener extends net.sourceforge.pmd.Report.GlobalReportBuilderListener { - } - - public static final class ReportBuilderListener extends net.sourceforge.pmd.Report.ReportBuilderListener { - } - - @Override - public Report filterViolations(Predicate filter) { - Report copy = new Report(); - - for (net.sourceforge.pmd.RuleViolation violation : violations) { - if (filter.test(violation)) { - copy.addRuleViolation(violation); - } - } - - copy.suppressedRuleViolations.addAll(suppressedRuleViolations); - copy.errors.addAll(errors); - copy.configErrors.addAll(configErrors); - return copy; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/RuleViolation.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/RuleViolation.java deleted file mode 100644 index d501b9820e..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/reporting/RuleViolation.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.reporting; - -public interface RuleViolation extends net.sourceforge.pmd.RuleViolation { -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/Predicate.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/Predicate.java deleted file mode 100644 index a6d0e68ce1..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/Predicate.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This class has been taken from 7.0.0-SNAPSHOT - -package net.sourceforge.pmd.util; - -/** - * Simple predicate of one argument. - * - * @param the type of the input to the predicate - */ -//@Experimental -public interface Predicate { - - boolean test(T t); -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractCompoundFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractCompoundFilter.java deleted file mode 100644 index 851c4bb492..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractCompoundFilter.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A base class for Filters which implements behavior using a List of other - * Filters. - * - * @param - * The underlying type on which the filter applies. - * @deprecated See {@link Filter} - */ -@Deprecated -public abstract class AbstractCompoundFilter implements Filter { - - protected List> filters; - - public AbstractCompoundFilter() { - filters = new ArrayList<>(2); - } - - public AbstractCompoundFilter(Filter... filters) { - this.filters = Arrays.asList(filters); - } - - public List> getFilters() { - return filters; - } - - public void setFilters(List> filters) { - this.filters = filters; - } - - public void addFilter(Filter filter) { - filters.add(filter); - } - - protected abstract String getOperator(); - - @Override - public String toString() { - - if (filters.isEmpty()) { - return "()"; - } - - StringBuilder builder = new StringBuilder(); - builder.append('(').append(filters.get(0)); - - for (int i = 1; i < filters.size(); i++) { - builder.append(' ').append(getOperator()).append(' '); - builder.append(filters.get(i)); - } - builder.append(')'); - return builder.toString(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractDelegateFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractDelegateFilter.java deleted file mode 100644 index e6ca4e47d2..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AbstractDelegateFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -/** - * A base class for Filters which implements behavior using delegation to an - * underlying filter. - * - * @param - * The underlying type on which the filter applies. - * @deprecated See {@link Filter} - */ -@Deprecated -public abstract class AbstractDelegateFilter implements Filter { - protected Filter filter; - - public AbstractDelegateFilter() { - // default constructor - } - - public AbstractDelegateFilter(Filter filter) { - this.filter = filter; - } - - public Filter getFilter() { - return filter; - } - - public void setFilter(Filter filter) { - this.filter = filter; - } - - // Subclass should override to do something other the simply delegate. - @Override - public boolean filter(T obj) { - return filter.filter(obj); - } - - // Subclass should override to do something other the simply delegate. - @Override - public String toString() { - return filter.toString(); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AndFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AndFilter.java deleted file mode 100644 index be8ff31fea..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/AndFilter.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -/** - * A logical AND of a list of Filters. This implementation is short circuiting. - * - * @param - * The underlying type on which the filter applies. - * @deprecated See {@link Filter} - */ -@Deprecated -public class AndFilter extends AbstractCompoundFilter { - - public AndFilter() { - super(); - } - - public AndFilter(Filter... filters) { - super(filters); - } - - @Override - public boolean filter(T obj) { - boolean match = true; - for (Filter filter : filters) { - if (!filter.filter(obj)) { - match = false; - break; - } - } - return match; - } - - @Override - protected String getOperator() { - return "and"; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/DirectoryFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/DirectoryFilter.java deleted file mode 100644 index c1669e91d3..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/DirectoryFilter.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -import java.io.File; - -/** - * Directory filter. - * @deprecated See {@link Filter} - */ -@Deprecated -public final class DirectoryFilter implements Filter { - public static final DirectoryFilter INSTANCE = new DirectoryFilter(); - - private DirectoryFilter() { - } - - @Override - public boolean filter(File file) { - return file.isDirectory(); - } - - @Override - public String toString() { - return "is Directory"; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/FileExtensionFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/FileExtensionFilter.java deleted file mode 100644 index 06259afce3..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/FileExtensionFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -import java.io.File; -import java.util.Locale; - -/** - * @deprecated See {@link Filter} - */ -@Deprecated -public class FileExtensionFilter implements Filter { - protected final String[] extensions; - protected final boolean ignoreCase; - - /** - * Matches any files with the given extensions, ignoring case - */ - public FileExtensionFilter(String... extensions) { - this(true, extensions); - } - - /** - * Matches any files with the given extensions, optionally ignoring case. - */ - public FileExtensionFilter(boolean ignoreCase, String... extensions) { - this.extensions = extensions; - this.ignoreCase = ignoreCase; - if (ignoreCase) { - for (int i = 0; i < this.extensions.length; i++) { - this.extensions[i] = this.extensions[i].toUpperCase(Locale.ROOT); - } - } - } - - @Override - public boolean filter(File file) { - boolean accept = extensions == null; - if (!accept) { - for (String extension : extensions) { - String name = file.getName(); - if (ignoreCase ? name.toUpperCase(Locale.ROOT).endsWith(extension) : name.endsWith(extension)) { - accept = true; - break; - } - } - } - return accept; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filter.java deleted file mode 100644 index e99a14bb44..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filter.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -/** - * A Filter interface, used for filtering arbitrary objects. - * - * @param - * The underlying type on which the filter applies. - * - * @deprecated Will be replaced with standard java.util.function.Predicate with 7.0.0 - */ -@Deprecated -public interface Filter { - boolean filter(T obj); -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filters.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filters.java deleted file mode 100644 index 699b614308..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/Filters.java +++ /dev/null @@ -1,245 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; - -/** - * Utility class for working with Filters. Contains builder style methods, apply - * methods, as well as mechanisms for adapting Filters and FilenameFilters. - * - * @deprecated Internal API, see {@link Filter} - */ -@Deprecated -@InternalApi -public final class Filters { - - private Filters() { } - - /** - * Filter a given Collection. - * - * @param - * Type of the Collection. - * @param filter - * A Filter upon the Type of objects in the Collection. - * @param collection - * The Collection to filter. - * @return A List containing only those objects for which the Filter - * returned true. - */ - public static List filter(Filter filter, Collection collection) { - List list = new ArrayList<>(); - for (T obj : collection) { - if (filter.filter(obj)) { - list.add(obj); - } - } - return list; - } - - /** - * Get a File Filter for files with the given extensions, ignoring case. - * - * @param extensions - * The extensions to filter. - * @return A File Filter. - */ - public static Filter getFileExtensionFilter(String... extensions) { - return new FileExtensionFilter(extensions); - } - - /** - * Get a File Filter for directories. - * - * @return A File Filter. - */ - public static Filter getDirectoryFilter() { - return DirectoryFilter.INSTANCE; - } - - /** - * Get a File Filter for directories or for files with the given extensions, - * ignoring case. - * - * @param extensions - * The extensions to filter. - * @return A File Filter. - */ - public static Filter getFileExtensionOrDirectoryFilter(String... extensions) { - return new OrFilter<>(getFileExtensionFilter(extensions), getDirectoryFilter()); - } - - /** - * Given a String Filter, expose as a File Filter. The File paths are - * normalized to a standard pattern using / as a path separator - * which can be used cross platform easily in a regular expression based - * String Filter. - * - * @param filter - * A String Filter. - * @return A File Filter. - */ - public static Filter toNormalizedFileFilter(final Filter filter) { - return new Filter() { - @Override - public boolean filter(File file) { - String path = file.getPath(); - path = path.replace('\\', '/'); - return filter.filter(path); - } - - @Override - public String toString() { - return filter.toString(); - } - }; - } - - /** - * Given a String Filter, expose as a Filter on another type. The - * toString() method is called on the objects of the other type - * and delegated to the String Filter. - * - * @param - * The desired type. - * @param filter - * The existing String Filter. - * @return A Filter on the desired type. - */ - public static Filter fromStringFilter(final Filter filter) { - return new Filter() { - @Override - public boolean filter(T obj) { - return filter.filter(obj.toString()); - } - - @Override - public String toString() { - return filter.toString(); - } - }; - } - - /** - * Given a File Filter, expose as a FilenameFilter. - * - * @param filter - * The File Filter. - * @return A FilenameFilter. - */ - public static FilenameFilter toFilenameFilter(final Filter filter) { - return new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return filter.filter(new File(dir, name)); - } - - @Override - public String toString() { - return filter.toString(); - } - }; - } - - /** - * Given a FilenameFilter, expose as a File Filter. - * - * @param filter - * The FilenameFilter. - * @return A File Filter. - */ - public static Filter toFileFilter(final FilenameFilter filter) { - return new Filter() { - @Override - public boolean filter(File file) { - return filter.accept(file.getParentFile(), file.getName()); - } - - @Override - public String toString() { - return filter.toString(); - } - }; - } - - /** - * Construct a String Filter using set of include and exclude regular - * expressions. If there are no include regular expressions provide, then a - * regular expression is added which matches every String by default. A - * String is included as long as it matches an include regular expression - * and does not match an exclude regular expression. - *

- * In other words, exclude patterns override include patterns. - * - * @param includeRegexes - * The include regular expressions. May be null. - * @param excludeRegexes - * The exclude regular expressions. May be null. - * @return A String Filter. - */ - public static Filter buildRegexFilterExcludeOverInclude(List includeRegexes, - List excludeRegexes) { - OrFilter includeFilter = new OrFilter<>(); - if (includeRegexes == null || includeRegexes.isEmpty()) { - includeFilter.addFilter(new RegexStringFilter(".*")); - } else { - for (String includeRegex : includeRegexes) { - includeFilter.addFilter(new RegexStringFilter(includeRegex)); - } - } - - OrFilter excludeFilter = new OrFilter<>(); - if (excludeRegexes != null) { - for (String excludeRegex : excludeRegexes) { - excludeFilter.addFilter(new RegexStringFilter(excludeRegex)); - } - } - - return new AndFilter<>(includeFilter, new NotFilter<>(excludeFilter)); - } - - /** - * Construct a String Filter using set of include and exclude regular - * expressions. If there are no include regular expressions provide, then a - * regular expression is added which matches every String by default. A - * String is included as long as the case that there is an include which - * matches or there is not an exclude which matches. - *

- * In other words, include patterns override exclude patterns. - * - * @param includeRegexes - * The include regular expressions. May be null. - * @param excludeRegexes - * The exclude regular expressions. May be null. - * @return A String Filter. - */ - public static Filter buildRegexFilterIncludeOverExclude(List includeRegexes, - List excludeRegexes) { - OrFilter includeFilter = new OrFilter<>(); - if (includeRegexes != null) { - for (String includeRegex : includeRegexes) { - includeFilter.addFilter(new RegexStringFilter(includeRegex)); - } - } - - OrFilter excludeFilter = new OrFilter<>(); - if (excludeRegexes != null) { - for (String excludeRegex : excludeRegexes) { - excludeFilter.addFilter(new RegexStringFilter(excludeRegex)); - } - } - - return new OrFilter<>(includeFilter, new NotFilter<>(excludeFilter)); - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/NotFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/NotFilter.java deleted file mode 100644 index 26cc124571..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/NotFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -/** - * A logical NEGATION of a Filter. - * - * @param - * The underlying type on which the filter applies. - * @deprecated See {@link Filter} - */ -@Deprecated -public class NotFilter extends net.sourceforge.pmd.util.filter.AbstractDelegateFilter { - public NotFilter() { - super(); - } - - public NotFilter(Filter filter) { - super(filter); - } - - @Override - public boolean filter(T obj) { - return !filter.filter(obj); - } - - @Override - public String toString() { - return "not (" + filter + ")"; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/OrFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/OrFilter.java deleted file mode 100644 index 9c57d99a1f..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/OrFilter.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -/** - * A logical OR of a list of Filters. This implementation is short circuiting. - * - * @param - * The underlying type on which the filter applies. - * @deprecated See {@link Filter} - */ -@Deprecated -public class OrFilter extends AbstractCompoundFilter { - - public OrFilter() { - super(); - } - - public OrFilter(Filter... filters) { - super(filters); - } - - @Override - public boolean filter(T obj) { - boolean match = false; - for (Filter filter : filters) { - if (filter.filter(obj)) { - match = true; - break; - } - } - return match; - } - - @Override - protected String getOperator() { - return "or"; - } -} diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/RegexStringFilter.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/RegexStringFilter.java deleted file mode 100644 index 010f8f6065..0000000000 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/util/filter/RegexStringFilter.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -// This file has been taken from 6.55.0 - -package net.sourceforge.pmd.util.filter; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * A filter which uses a regular expression to match Strings. Invalid regular - * expressions will match nothing. - *

- * Because regular expression matching is slow, and a common usage is to match - * some sort of relative file path, the regular expression is checked to see if - * it can be evaluated using much faster calls to - * {@link String#endsWith(String)}. - * @deprecated See {@link Filter} - */ -@Deprecated -public class RegexStringFilter implements Filter { - /** - * Matches regular expressions begin with an optional {@code ^}, then - * {@code .*}, then a literal path, with an optional file extension, and - * finally an optional {@code $} at the end. The {@code .} in the extension - * may or may not be preceded by a {@code \} escape. The literal path - * portion is determine by the absence of any of the following characters: - * \ [ ( . * ? + | { $ - * - * There are two capturing groups in the expression. The first is for the - * literal path. The second is for the file extension, without the escaping. - * The concatenation of these two captures creates the {@link String} which - * can be used with {@link String#endsWith(String)}. - * - * For ease of reference, the non-Java escaped form of this pattern is: - * \^?\.\*([^\\\[\(\.\*\?\+\|\{\$]+)(?:\\?(\.\w+))?\$? - */ - private static final Pattern ENDS_WITH = Pattern - .compile("\\^?\\.\\*([^\\\\\\[\\(\\.\\*\\?\\+\\|\\{\\$]+)(?:\\\\?(\\.\\w+))?\\$?"); - - protected String regex; - protected Pattern pattern; - protected String endsWith; - - public RegexStringFilter(String regex) { - this.regex = regex; - optimize(); - } - - public String getRegex() { - return this.regex; - } - - public String getEndsWith() { - return this.endsWith; - } - - protected void optimize() { - final Matcher matcher = ENDS_WITH.matcher(this.regex); - if (matcher.matches()) { - final String literalPath = matcher.group(1); - final String fileExtension = matcher.group(2); - if (fileExtension != null) { - this.endsWith = literalPath + fileExtension; - } else { - this.endsWith = literalPath; - } - } else { - try { - this.pattern = Pattern.compile(this.regex); - } catch (PatternSyntaxException ignored) { - // If the regular expression is invalid, then pattern will be - // null. - } - } - } - - @Override - public boolean filter(String obj) { - if (this.endsWith != null) { - return obj.endsWith(this.endsWith); - } - - return this.pattern != null && this.pattern.matcher(obj).matches(); - // if pattern was null, - // The regular expression must have been bad, so it will match - // nothing. - } - - @Override - public String toString() { - return "matches " + this.regex; - } -} diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index b5140a25fa..a0067a6174 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index f79609ad9c..0960a0fd48 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -46,6 +46,8 @@ public abstract class AbstractConfiguration { private Path ignoreFilePath; private List excludes = new ArrayList<>(); private boolean collectRecursive = true; + private boolean failOnViolation = true; + private boolean failOnError = true; protected AbstractConfiguration(LanguageRegistry languageRegistry, PmdReporter messageReporter) { @@ -377,4 +379,68 @@ public abstract class AbstractConfiguration { public void collectFilesRecursively(boolean collectRecursive) { this.collectRecursive = collectRecursive; } + + /** + * Whether PMD should exit with status 4 (the default behavior, true) if + * violations are found or just with 0 (to not break the build, e.g.). + * + *

Note: If additionally recoverable errors occurred, the exit status is 5. See + * {@link #isFailOnError()}. + * + * @return failOnViolation + * + * @see #isFailOnError() + * @since 6.0.0 + */ + public boolean isFailOnViolation() { + return failOnViolation; + } + + /** + * Sets whether PMD should exit with status 4 (the default behavior, true) + * if violations are found or just with 0 (to not break the build, e.g.). + * + *

Note: If additionally recoverable errors occurred, the exit status is 5. See + * {@link #isFailOnError()}. + * + * @param failOnViolation whether to exit with 4 and fail the build if violations are found. + * + * @see #isFailOnError() + * @since 6.0.0 + */ + public void setFailOnViolation(boolean failOnViolation) { + this.failOnViolation = failOnViolation; + } + + /** + * Whether PMD should exit with status 5 (the default behavior, true) if + * recoverable errors occurred or just with 0 (to not break the build, e.g.). + * + *

Note: If only violations are found, the exit status is 4. See + * {@link #isFailOnViolation()}. + * + * @return failOnError + * + * @see #isFailOnViolation() + * @since 7.3.0 + */ + public boolean isFailOnError() { + return failOnError; + } + + /** + * Sets whether PMD should exit with status 5 (the default behavior, true) + * if recoverable errors occurred or just with 0 (to not break the build, e.g.). + * + *

Note: If only violations are found, the exit status is 4. See + * {@link #isFailOnViolation()}. + * + * @param failOnError whether to exit with 5 and fail the build if recoverable errors occurred. + * + * @see #isFailOnViolation() + * @since 7.3.0 + */ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } } 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 17a7557761..287d001f2a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -108,7 +108,6 @@ public class PMDConfiguration extends AbstractConfiguration { private String reportFormat; private Properties reportProperties = new Properties(); private boolean showSuppressedViolations = false; - private boolean failOnViolation = true; private AnalysisCache analysisCache = new NoopAnalysisCache(); private boolean ignoreIncrementalAnalysis; @@ -362,27 +361,6 @@ public class PMDConfiguration extends AbstractConfiguration { this.reportProperties = reportProperties; } - /** - * Whether PMD should exit with status 4 (the default behavior, true) if - * violations are found or just with 0 (to not break the build, e.g.). - * - * @return failOnViolation - */ - public boolean isFailOnViolation() { - return failOnViolation; - } - - /** - * Sets whether PMD should exit with status 4 (the default behavior, true) - * if violations are found or just with 0 (to not break the build, e.g.). - * - * @param failOnViolation - * failOnViolation - */ - public void setFailOnViolation(boolean failOnViolation) { - this.failOnViolation = failOnViolation; - } - /** * Retrieves the currently used analysis cache. Will never be null. * 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 f29b64b8d6..3826781b86 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 @@ -66,6 +66,7 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean ignoreIdentifierAndLiteralSequences = false; + @Deprecated private boolean skipLexicalErrors = false; private boolean noSkipBlocks = false; @@ -74,8 +75,6 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean help; - private boolean failOnViolation = true; - public CPDConfiguration() { this(LanguageRegistry.CPD); @@ -229,10 +228,20 @@ public class CPDConfiguration extends AbstractConfiguration { this.ignoreIdentifierAndLiteralSequences = ignoreIdentifierAndLiteralSequences; } + /** + * @deprecated This option will be removed. With {@link #isFailOnError()}, you can + * control whether lexical errors should fail the build or not. + */ + @Deprecated public boolean isSkipLexicalErrors() { return skipLexicalErrors; } + /** + * @deprecated This option will be removed. With {@link #setFailOnError(boolean)}, you can + * control whether lexical errors should fail the build or not. + */ + @Deprecated public void setSkipLexicalErrors(boolean skipLexicalErrors) { this.skipLexicalErrors = skipLexicalErrors; } @@ -261,14 +270,6 @@ public class CPDConfiguration extends AbstractConfiguration { this.skipBlocksPattern = skipBlocksPattern; } - public boolean isFailOnViolation() { - return failOnViolation; - } - - public void setFailOnViolation(boolean failOnViolation) { - this.failOnViolation = failOnViolation; - } - @Override protected void checkLanguageIsAcceptable(Language lang) throws UnsupportedOperationException { if (!(lang instanceof CpdCapableLanguage)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 457194166a..1df33f5274 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -153,6 +153,9 @@ public final class CpdAnalysis implements AutoCloseable { @SuppressWarnings("PMD.CloseResource") public void performAnalysis(Consumer consumer) { + if (configuration.isSkipLexicalErrors()) { + LOGGER.warn("The option skipLexicalErrors is deprecated. Use failOnError instead."); + } try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { Map tokenizers = diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java index 36c6e99c2f..d2d4264f2b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java @@ -68,7 +68,7 @@ class MatchCollector { * - BC * It should be reduced to a single match with 3 marks */ - if (tokenMatchSets.computeIfAbsent(mark1.getIndex(), HashSet::new).contains(mark2.getIndex())) { + if (tokenMatchSets.computeIfAbsent(mark1.getIndex(), (i) -> new HashSet<>()).contains(mark2.getIndex())) { return; } @@ -76,7 +76,7 @@ class MatchCollector { // always rely on the lowest mark index, as that's the order in which process them final int lowestKey = tokenMatchSets.get(mark1.getIndex()).stream().reduce(mark1.getIndex(), Math::min); - List matches = matchTree.computeIfAbsent(lowestKey, ArrayList::new); + List matches = matchTree.computeIfAbsent(lowestKey, (i) -> new ArrayList<>()); Iterator matchIterator = matches.iterator(); while (matchIterator.hasNext()) { Match m = matchIterator.next(); @@ -116,8 +116,8 @@ class MatchCollector { } private void registerTokenMatch(TokenEntry mark1, TokenEntry mark2) { - tokenMatchSets.computeIfAbsent(mark1.getIndex(), HashSet::new).add(mark2.getIndex()); - tokenMatchSets.computeIfAbsent(mark2.getIndex(), HashSet::new).add(mark1.getIndex()); + tokenMatchSets.computeIfAbsent(mark1.getIndex(), (i) -> new HashSet<>()).add(mark2.getIndex()); + tokenMatchSets.computeIfAbsent(mark2.getIndex(), (i) -> new HashSet<>()).add(mark1.getIndex()); } List getMatches() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrToken.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrToken.java index 8a7d08c1f5..4a12e8388d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrToken.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrToken.java @@ -17,9 +17,13 @@ import net.sourceforge.pmd.lang.document.TextRegion; */ public class AntlrToken implements GenericToken { - private final Token token; private final AntlrToken previousComment; private final TextDocument textDoc; + private final String image; + private final int endOffset; + private final int startOffset; + private final int channel; + private final int kind; AntlrToken next; @@ -29,11 +33,18 @@ public class AntlrToken implements GenericToken { * @param token The antlr token implementation * @param previousComment The previous comment * @param textDoc The text document + * + * @deprecated Don't create antlr tokens directly, use an {@link AntlrTokenManager} */ + @Deprecated public AntlrToken(final Token token, final AntlrToken previousComment, TextDocument textDoc) { - this.token = token; this.previousComment = previousComment; this.textDoc = textDoc; + this.image = token.getText(); + this.startOffset = token.getStartIndex(); + this.endOffset = token.getStopIndex() + 1; // exclusive + this.channel = token.getChannel(); + this.kind = token.getType(); } @Override @@ -48,13 +59,13 @@ public class AntlrToken implements GenericToken { @Override public CharSequence getImageCs() { - return token.getText(); + return image; } /** Returns a text region with the coordinates of this token. */ @Override public TextRegion getRegion() { - return TextRegion.fromBothOffsets(token.getStartIndex(), token.getStopIndex() + 1); + return TextRegion.fromBothOffsets(startOffset, endOffset); } @Override @@ -74,7 +85,7 @@ public class AntlrToken implements GenericToken { @Override public int getKind() { - return token.getType(); + return kind; } public boolean isHidden() { @@ -82,6 +93,6 @@ public class AntlrToken implements GenericToken { } public boolean isDefault() { - return token.getChannel() == Lexer.DEFAULT_TOKEN_CHANNEL; + return channel == Lexer.DEFAULT_TOKEN_CHANNEL; } } 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 957bb8ad14..70bbc59b8a 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 @@ -147,6 +147,16 @@ public class JavaccToken implements GenericToken { return image.toString(); } + /** + * Returns the original text of the token. + * The image may be normalized, e.g. for case-insensitive languages. + * + * @since 7.3.0 + */ + public Chars getText() { + return document.getTextDocument().sliceOriginalText(getRegion()); + } + @Override public final TextRegion getRegion() { return TextRegion.fromBothOffsets(startOffset, endOffset); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java index 001e32572c..3ca70f0686 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.lang.rule.xpath.impl; -import static net.sourceforge.pmd.util.CollectionUtil.emptyList; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import java.lang.invoke.MethodHandle; @@ -91,7 +90,7 @@ public class AttributeAxisIterator implements Iterator { .filter(m -> isAttributeAccessor(nodeClass, m)) .map(m -> { try { - return new MethodWrapper(m, nodeClass); + return new MethodWrapper(m); } catch (ReflectiveOperationException e) { throw AssertionUtil.shouldNotReachHere("Method '" + m + "' should be accessible, but: " + e, e); } @@ -210,19 +209,13 @@ public class AttributeAxisIterator implements Iterator { public final String name; - MethodWrapper(Method m, Class nodeClass) throws IllegalAccessException, NoSuchMethodException { + MethodWrapper(Method m) throws IllegalAccessException { this.method = m; this.name = truncateMethodName(m.getName()); - - if (!Modifier.isPublic(m.getDeclaringClass().getModifiers())) { - // This is a public method of a non-public class. - // To call it from reflection we need to call it via invokevirtual, - // whereas the default handle would use invokespecial. - MethodType methodType = MethodType.methodType(m.getReturnType(), emptyList()); - this.methodHandle = MethodWrapper.LOOKUP.findVirtual(nodeClass, m.getName(), methodType).asType(GETTER_TYPE); - } else { - this.methodHandle = LOOKUP.unreflect(m).asType(GETTER_TYPE); - } + // Note: We only support public methods on public types. If the method being called is implemented + // in a package-private class, this won't work. + // See git history here and https://github.com/pmd/pmd/issues/4885 + this.methodHandle = LOOKUP.unreflect(m).asType(GETTER_TYPE); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Report.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Report.java index a13d468952..10f0058e94 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Report.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Report.java @@ -22,8 +22,8 @@ import net.sourceforge.pmd.lang.rule.Rule; import net.sourceforge.pmd.util.BaseResultProducingCloseable; /** - * A {@link Report} collects all information during a PMD execution. This - * includes violations, suppressed violations, metrics, error during processing + * A {@link Report} collects all information during a PMD execution. This includes violations, + * suppressed violations, metrics, recoverable errors (that occurred during processing) * and configuration errors. * *

A report may be created by a {@link GlobalReportBuilderListener} that you @@ -50,7 +50,10 @@ public final class Report { } /** - * Represents a configuration error. + * Represents a configuration error for a specific rule. + * + *

This might be a missing rule property + * or rule property with pointless values. */ public static class ConfigurationError { @@ -90,7 +93,15 @@ public final class Report { } /** - * Represents a processing error, such as a parse error. + * Represents a recovered error that occurred during analysis. + * + *

This might be a parse error or an unexpected error originating from a rule. + * Such errors are called recoverable, because PMD can just skip + * the problematic file, continue the analysis with the other files and still create a report. + * However, due to these errors, the report might be incomplete. + * + *

Some report formats, such as {@link net.sourceforge.pmd.renderers.XMLRenderer}, include these + * errors for further investigation. */ public static class ProcessingError { @@ -98,7 +109,7 @@ public final class Report { private final FileId file; /** - * Creates a new processing error + * Creates a new processing error. * * @param error * the error diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java index eef7cd7dc5..9ac07837a3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java @@ -23,10 +23,16 @@ public final class ReportStats { return new ReportStats(0, 0); } + /** + * Count of processing errors. + */ public int getNumErrors() { return numErrors; } + /** + * Count of found rule violations. + */ public int getNumViolations() { return numViolations; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/AbstractNode.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/AbstractNode.java index 7f1d4a366e..7ca1219450 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/AbstractNode.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/AbstractNode.java @@ -8,16 +8,16 @@ import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.document.Chars; // This class is package private -// and provides the implementation for getValue(). This -// class is the DeclaringClass for that method. -class AbstractNode extends DummyNode implements ValueNode { +// and provides the implementation for getValue(). +// This method is not accessible from outside this package, +// it is made available in the subclass ConcreteNode. +class AbstractNode extends DummyNode { AbstractNode() { } - @Override - public final Chars getValue() { + Chars getValue() { return Chars.wrap("actual_value"); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/ConcreteNode.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/ConcreteNode.java index bd2b214529..ebc4c3d634 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/ConcreteNode.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/dummyast/ConcreteNode.java @@ -4,5 +4,11 @@ package net.sourceforge.pmd.lang.rule.xpath.impl.dummyast; +import net.sourceforge.pmd.lang.document.Chars; + public final class ConcreteNode extends AbstractNode implements ValueNode { + @Override + public Chars getValue() { + return super.getValue(); + } } diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 9c430cc4f4..921d7515cd 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 026de2bee2..3641191746 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-dart/pom.xml b/pmd-dart/pom.xml index 47d2c87ad6..e5e54e3987 100644 --- a/pmd-dart/pom.xml +++ b/pmd-dart/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 9f8e02fdf8..4ca2949d3a 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java index 50bbbfccd0..1b5c701c54 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/BinaryDistributionIT.java @@ -191,7 +191,7 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { ExecutionResult result = PMDExecutor.runPMDRules(createTemporaryReportFile(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); - result.assertExitCode(0).assertStdErr(containsString("Run in verbose mode to see a stack-trace.")); + result.assertExitCode(5).assertStdErr(containsString("Run in verbose mode to see a stack-trace.")); } @Test diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 7ebb6d3d00..7aa5ad9343 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index 86e87edcb9..128c8c857a 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-gherkin/pom.xml b/pmd-gherkin/pom.xml index a62a575cda..b054c8552f 100644 --- a/pmd-gherkin/pom.xml +++ b/pmd-gherkin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index d69f0d58af..4bdaa9b8e3 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index a700b821d9..40fad655a7 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-html/pom.xml b/pmd-html/pom.xml index 223deed571..06ca2d4c23 100644 --- a/pmd-html/pom.xml +++ b/pmd-html/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index a3fe724801..5aad15aea3 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1863,7 +1863,7 @@ void Pattern() #void: void TypePattern(): {} { - LocalVarModifierList() FormalParamType() VariableDeclaratorId() + LocalVarModifierList() LocalVariableType() VariableDeclaratorId() } void RecordPattern(): diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index ea6d0daefb..354802ca58 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteral.java index 694ae630fb..619cca36f4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteral.java @@ -6,10 +6,12 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.lang.document.Chars; + /** * The boolean literal, either "true" or "false". */ -public final class ASTBooleanLiteral extends AbstractLiteral { +public final class ASTBooleanLiteral extends AbstractLiteral implements ASTLiteral { private boolean isTrue; @@ -32,6 +34,11 @@ public final class ASTBooleanLiteral extends AbstractLiteral { return isTrue; } + @Override + public Chars getLiteralText() { + return super.getLiteralText(); + } + @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/ASTCharLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCharLiteral.java index 5cc8e60b6f..aa0e25d324 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCharLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCharLiteral.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.document.Chars; * retrieve the actual runtime value. Use {@link #getLiteralText()} to * retrieve the text. */ -public final class ASTCharLiteral extends AbstractLiteral { +public final class ASTCharLiteral extends AbstractLiteral implements ASTLiteral { ASTCharLiteral(int id) { @@ -39,4 +39,8 @@ public final class ASTCharLiteral extends AbstractLiteral { return StringEscapeUtils.UNESCAPE_JAVA.translate(woDelims).charAt(0); } + @Override + public Chars getLiteralText() { + return super.getLiteralText(); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNullLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNullLiteral.java index 05a1cb1b24..da168327c2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNullLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNullLiteral.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.document.Chars; + /** * The null literal. * @@ -15,7 +17,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; * * */ -public final class ASTNullLiteral extends AbstractLiteral { +public final class ASTNullLiteral extends AbstractLiteral implements ASTLiteral { ASTNullLiteral(int id) { super(id); } @@ -35,4 +37,9 @@ public final class ASTNullLiteral extends AbstractLiteral { public @Nullable Object getConstValue() { return null; } + + @Override + public Chars getLiteralText() { + return super.getLiteralText(); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java index c0e4d21383..c8f711fdb5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java @@ -14,7 +14,7 @@ import net.sourceforge.pmd.lang.java.types.JPrimitiveType; /** * A numeric literal of any type (double, int, long, float, etc). */ -public final class ASTNumericLiteral extends AbstractLiteral { +public final class ASTNumericLiteral extends AbstractLiteral implements ASTLiteral { /** * True if this is an integral literal, ie int OR long, @@ -36,6 +36,11 @@ public final class ASTNumericLiteral extends AbstractLiteral { return visitor.visit(this, data); } + @Override + public Chars getLiteralText() { + return super.getLiteralText(); + } + @Override public @NonNull Number getConstValue() { return (Number) super.getConstValue(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java index eeedf1aaa8..71ebc2d95e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java @@ -14,12 +14,14 @@ package net.sourceforge.pmd.lang.java.ast; * *

  *
- * Pattern ::=   {@linkplain ASTTypePattern TypePattern} | {@linkplain ASTRecordPattern RecordPattern}
+ * Pattern ::=   {@linkplain ASTTypePattern TypePattern}
+ *           | {@linkplain ASTRecordPattern RecordPattern}
+ *           | {@linkplain ASTUnnamedPattern UnnamedPattern}
  *
  * 
* * @see JEP 394: Pattern Matching for instanceof (Java 16) * @see JEP 440: Record Patterns (Java 21) */ -public interface ASTPattern extends JavaNode { +public interface ASTPattern extends TypeNode { } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordComponent.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordComponent.java index f1dbdbdf12..c9b9bbe8b4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordComponent.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordComponent.java @@ -8,6 +8,7 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.VariableIdOwner; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; /** * Defines a single component of a {@linkplain ASTRecordDeclaration RecordDeclaration} (JDK 16 feature). @@ -22,6 +23,7 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; *
    *
  • The symbol exposed by the {@link ASTVariableId} is the field * symbol. + *
  • The symbol exposed by this node (ASTRecordComponent) is a {@link JRecordComponentSymbol}. *
  • The formal parameter symbol is accessible in the formal parameter * list of the {@link JConstructorSymbol} for the {@linkplain ASTRecordComponentList#getSymbol() canonical constructor}. *
  • The symbol for the accessor method can be found in the {@link JClassSymbol#getDeclaredMethods() declared methods} @@ -34,7 +36,8 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; * * */ -public final class ASTRecordComponent extends AbstractJavaNode implements ModifierOwner, VariableIdOwner { +public final class ASTRecordComponent extends AbstractTypedSymbolDeclarator + implements ModifierOwner, VariableIdOwner, SymbolDeclaratorNode { ASTRecordComponent(int id) { super(id); @@ -45,7 +48,6 @@ public final class ASTRecordComponent extends AbstractJavaNode implements Modifi return visitor.visit(this, data); } - /** * Returns true if this component's corresponding formal parameter * in the canonical constructor of the record is varargs. The type diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordPattern.java index f16730342f..874570e383 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordPattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordPattern.java @@ -17,7 +17,7 @@ package net.sourceforge.pmd.lang.java.ast; * @see ASTRecordDeclaration * @see JEP 440: Record Patterns (Java 21) */ -public final class ASTRecordPattern extends AbstractJavaNode implements ASTPattern { +public final class ASTRecordPattern extends AbstractJavaPattern { ASTRecordPattern(int id) { super(id); @@ -29,13 +29,31 @@ public final class ASTRecordPattern extends AbstractJavaNode implements ASTPatte } /** - * Gets the type against which the expression is tested. + * Return the type of the record. */ public ASTReferenceType getTypeNode() { return firstChild(ASTReferenceType.class); } - /** Returns the declared variable. */ + + /** + * Return the patterns for each record component. + * + * @since 7.3.0 + */ + public ASTPatternList getComponentPatterns() { + return firstChild(ASTPatternList.class); + } + + + /** + * Returns the declared variable. + * + * @deprecated This method was added here by mistake. Record patterns don't declare a pattern variable + * for the whole pattern, but rather for individual record components, which can be accessed via + * {@link #getComponentPatterns()}. + */ + @Deprecated public ASTVariableId getVarId() { return firstChild(ASTVariableId.class); } 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 928138bb5f..ab3684fb36 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 @@ -18,7 +18,7 @@ import net.sourceforge.pmd.util.StringUtil; * in the source ({@link #getLiteralText()}). {@link #getConstValue()} allows to recover * the actual runtime value, by processing escapes. */ -public final class ASTStringLiteral extends AbstractLiteral { +public final class ASTStringLiteral extends AbstractLiteral implements ASTLiteral { private static final String TEXTBLOCK_DELIMITER = "\"\"\""; @@ -36,6 +36,11 @@ public final class ASTStringLiteral extends AbstractLiteral { return getText().toString(); } + @Override + public Chars getLiteralText() { + return super.getLiteralText(); + } + void setTextBlock() { this.isTextBlock = true; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java index 3a99a89ea2..203ea3c554 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java @@ -21,7 +21,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; * * @see JEP 394: Pattern Matching for instanceof (Java 16) */ -public final class ASTTypePattern extends AbstractJavaNode implements ASTPattern, ModifierOwner { +public final class ASTTypePattern extends AbstractJavaPattern implements ModifierOwner { ASTTypePattern(int id) { super(id); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnnamedPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnnamedPattern.java index f1570f91f6..b3650b2c6a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnnamedPattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnnamedPattern.java @@ -16,7 +16,7 @@ package net.sourceforge.pmd.lang.java.ast; * * @see JEP 456: Unnamed Variables & Patterns (Java 22) */ -public final class ASTUnnamedPattern extends AbstractJavaNode implements ASTPattern { +public final class ASTUnnamedPattern extends AbstractJavaPattern { ASTUnnamedPattern(int id) { super(id); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaPattern.java new file mode 100644 index 0000000000..caacac7312 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaPattern.java @@ -0,0 +1,16 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +/** + * @author ClΓ©ment Fournier + */ +abstract class AbstractJavaPattern extends AbstractJavaTypeNode implements ASTPattern { + + AbstractJavaPattern(int i) { + super(i); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractLiteral.java index 7c48889275..677204c967 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractLiteral.java @@ -10,8 +10,11 @@ import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; /** * @author ClΓ©ment Fournier + * @see ASTLiteral#getLiteralText() + * @see #getLiteralText() */ -abstract class AbstractLiteral extends AbstractJavaExpr implements ASTLiteral { +// Note: This class must not implement ASTLiteral, see comment on #getLiteralText() +abstract class AbstractLiteral extends AbstractJavaExpr { private JavaccToken literalToken; @@ -41,13 +44,16 @@ abstract class AbstractLiteral extends AbstractJavaExpr implements ASTLiteral { return firstToken.getImageCs(); } - @Override - public final Chars getLiteralText() { + // This method represents ASTLiteral#getLiteralText(). + // However, since this class is package private, this method is not reliably accessible + // via reflection/method handles (see https://github.com/pmd/pmd/issues/4885). + // Subclasses of this class need to implement ASTLiteral and override this method + // as public. + Chars getLiteralText() { assert literalToken.getImageCs() != null; return literalToken.getImageCs(); } - @Override public boolean isCompileTimeConstant() { return true; // note: NullLiteral overrides this to false 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 a5ce0b574e..ff5e5eac56 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 @@ -16,6 +16,7 @@ import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; @@ -64,6 +65,8 @@ public final class InternalApiBridge { ((ASTTypeParameter) node).setSymbol((JTypeParameterSymbol) symbol); } else if (node instanceof ASTRecordComponentList) { ((ASTRecordComponentList) node).setSymbol((JConstructorSymbol) symbol); + } else if (node instanceof ASTRecordComponent) { + ((ASTRecordComponent) node).setSymbol((JRecordComponentSymbol) symbol); } else { throw new AssertionError("Cannot set symbol " + symbol + " on node " + node); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsRule.java new file mode 100644 index 0000000000..a90b971544 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsRule.java @@ -0,0 +1,50 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.JClassType; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +/** + * Detect cases where EnumSet and EnumMap can be used. + * + * @author ClΓ©ment Fournier + */ +public class UseEnumCollectionsRule extends AbstractJavaRulechainRule { + + public UseEnumCollectionsRule() { + super(ASTConstructorCall.class); + } + + + @Override + public Object visit(ASTConstructorCall call, Object data) { + JTypeMirror builtType = call.getTypeMirror(); + + if (!builtType.isRaw()) { + boolean isMap = TypeTestUtil.isExactlyA(HashMap.class, builtType); + if (isMap || TypeTestUtil.isExactlyA(HashSet.class, builtType)) { + + List typeArgs = ((JClassType) builtType).getTypeArgs(); + JTypeDeclSymbol keySymbol = typeArgs.get(0).getSymbol(); + + if (keySymbol instanceof JClassSymbol && ((JClassSymbol) keySymbol).isEnum()) { + String enumCollectionReplacement = isMap ? "EnumMap" : "EnumSet"; + asCtx(data).addViolation(call.getTypeNode(), enumCollectionReplacement); + } + } + } + return null; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java index 51f36c9b0d..1d14ca0e07 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java @@ -9,6 +9,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.TypeOps; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; @@ -26,7 +27,8 @@ public class StringInstantiationRule extends AbstractJavaRule { && TypeTestUtil.isExactlyA(String.class, node.getTypeNode())) { if (args.size() == 1 && ( TypeTestUtil.isExactlyA(byte[].class, args.get(0)) - || TypeTestUtil.isExactlyA(char[].class, args.get(0)))) { + || TypeTestUtil.isExactlyA(char[].class, args.get(0)) + || TypeOps.isUnresolved(args.get(0).getTypeMirror()))) { // byte/char array ctor is ok return data; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java index 62ab953a07..cb46932023 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java @@ -173,7 +173,7 @@ public interface JClassSymbol extends JTypeDeclSymbol, /** * Returns a list with all enum constants. If this symbol does - * not represent an enum, returns an empty set. The returned list + * not represent an enum, returns an empty list. The returned list * is a subset of {@link #getDeclaredFields()}. The order of fields * denotes the normal order of enum constants. */ @@ -182,6 +182,16 @@ public interface JClassSymbol extends JTypeDeclSymbol, } + /** + * Returns a list with all record components. If this symbol does + * not represent a record, returns an empty list. The order of values + * denotes the normal order of components. + */ + default @NonNull List getRecordComponents() { + return Collections.emptyList(); + } + + /** Returns the list of super interface types, under the given substitution. */ List getSuperInterfaceTypes(Substitution substitution); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java index 4ddc77ad33..f24bdb5421 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java @@ -25,7 +25,6 @@ public interface JFieldSymbol extends JVariableSymbol, JAccessibleElementSymbol /** Returns true if this field is an enum constant. */ boolean isEnumConstant(); - @Override default boolean isFinal() { return Modifier.isFinal(getModifiers()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JRecordComponentSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JRecordComponentSymbol.java new file mode 100644 index 0000000000..ef5d130868 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JRecordComponentSymbol.java @@ -0,0 +1,61 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +package net.sourceforge.pmd.lang.java.symbols; + +import java.lang.reflect.Modifier; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTRecordComponent; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.Substitution; + +/** + * Represents a record component. Record components are associated + * with a private final {@link JFieldSymbol} and a public + * {@linkplain JMethodSymbol accessor method}. + * + * @since 7.3.0 + */ +public interface JRecordComponentSymbol extends JAccessibleElementSymbol, BoundToNode { + + /** + * Record components use these modifiers by convention, although + * they cannot have explicit modifiers in source, and + * the associated field and method symbol have different + * modifiers. + */ + int RECORD_COMPONENT_MODIFIERS = Modifier.PUBLIC; + + + /** + * Returns the type of this value, under the given substitution. + */ + JTypeMirror getTypeMirror(Substitution substitution); + + + @Override + default int getModifiers() { + return RECORD_COMPONENT_MODIFIERS; + } + + + @Override + @NonNull + JClassSymbol getEnclosingClass(); + + + @Override + default @NonNull String getPackageName() { + return getEnclosingClass().getPackageName(); + } + + + @Override + default R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitRecordComponent(this, param); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java index 5922392d4a..1bac58d414 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java @@ -56,6 +56,11 @@ public interface SymbolVisitor { return visitVariable(sym, param); } + /** Delegates to {@link #visitSymbol(JElementSymbol, Object)}. */ + default R visitRecordComponent(JRecordComponentSymbol sym, P param) { + return visitSymbol(sym, param); + } + /** Delegates to {@link #visitVariable(JVariableSymbol, Object) visitVariable}. */ default R visitLocal(JLocalVariableSymbol sym, P param) { return visitVariable(sym, param); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ImplicitMemberSymbols.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ImplicitMemberSymbols.java index 8aed162f47..abee69b5be 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ImplicitMemberSymbols.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ImplicitMemberSymbols.java @@ -22,6 +22,7 @@ import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.JTypeVar; import net.sourceforge.pmd.lang.java.types.Substitution; @@ -105,7 +106,7 @@ public final class ImplicitMemberSymbols { /** Symbol for the canonical record constructor. */ public static JConstructorSymbol recordConstructor(JClassSymbol recordSym, - List recordComponents, + List recordComponents, boolean isVarargs) { assert recordSym.isRecord() : "Not a record symbol " + recordSym; @@ -117,7 +118,7 @@ public final class ImplicitMemberSymbols { modifiers, CollectionUtil.map( recordComponents, - f -> c -> new FakeFormalParamSym(c, f.getSimpleName(), f.tryGetNode(), (ts, sym) -> f.getTypeMirror(Substitution.EMPTY)) + f -> c -> new FakeFormalParamSym(c, f.getSimpleName(), f.tryGetNode().getVarId(), (ts, sym) -> f.getTypeMirror(Substitution.EMPTY)) ) ); } @@ -126,7 +127,7 @@ public final class ImplicitMemberSymbols { * Symbol for a record component accessor. * Only synthesized if it is not explicitly declared. */ - public static JMethodSymbol recordAccessor(JClassSymbol recordSym, JFieldSymbol recordComponent) { + public static JMethodSymbol recordAccessor(JClassSymbol recordSym, JRecordComponentSymbol recordComponent) { // See https://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html#jls-8.10.3 assert recordSym.isRecord() : "Not a record symbol " + recordSym; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolEquality.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolEquality.java index a6fb90b024..2f009f7304 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolEquality.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolEquality.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot; @@ -49,7 +50,7 @@ public final class SymbolEquality { } JTypeParameterSymbol m2 = (JTypeParameterSymbol) o; - return m1.nameEquals(m2.getSimpleName()) + return Objects.equals(m1.getSimpleName(), m2.getSimpleName()) && m1.getDeclaringSymbol().equals(m2.getDeclaringSymbol()); } }; @@ -135,7 +136,7 @@ public final class SymbolEquality { return false; } JFieldSymbol f2 = (JFieldSymbol) o; - return f1.nameEquals(f2.getSimpleName()) + return Objects.equals(f1.getSimpleName(), f2.getSimpleName()) && f1.getEnclosingClass().equals(f2.getEnclosingClass()); } @@ -170,12 +171,31 @@ public final class SymbolEquality { return false; } JFormalParamSymbol f2 = (JFormalParamSymbol) o; - return f1.nameEquals(f2.getSimpleName()) + return Objects.equals(f1.getSimpleName(), f2.getSimpleName()) && f1.getDeclaringSymbol().equals(f2.getDeclaringSymbol()); } }; + + public static final EqAndHash RECORD_COMPONENT = new EqAndHash() { + @Override + public int hash(JRecordComponentSymbol t1) { + return 31 * t1.getEnclosingClass().hashCode() + t1.getSimpleName().hashCode(); + } + + @Override + public boolean equals(JRecordComponentSymbol f1, Object o) { + if (!(o instanceof JRecordComponentSymbol)) { + return false; + } + JRecordComponentSymbol f2 = (JRecordComponentSymbol) o; + return Objects.equals(f1.getSimpleName(), f2.getSimpleName()) + && f1.getEnclosingClass().equals(f2.getEnclosingClass()); + + } + }; + private static final EqAndHash IDENTITY = new EqAndHash() { @Override public int hash(Object t1) { @@ -262,6 +282,12 @@ public final class SymbolEquality { public EqAndHash visitFormal(JFormalParamSymbol sym, Void param) { return FORMAL_PARAM; } + + + @Override + public EqAndHash visitRecordComponent(JRecordComponentSymbol sym, Void param) { + return RECORD_COMPONENT; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolToStrings.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolToStrings.java index 2526b248e2..5c0a1ade51 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolToStrings.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/SymbolToStrings.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; @@ -116,6 +117,13 @@ public class SymbolToStrings { return withImpl(param, "field", sym.getSimpleName(), sym.getEnclosingClass()); } + + @Override + public StringBuilder visitRecordComponent(JRecordComponentSymbol sym, StringBuilder param) { + return withImpl(param, "record component", sym.getSimpleName(), sym.getEnclosingClass()); + } + + @Override public StringBuilder visitLocal(JLocalVariableSymbol sym, StringBuilder param) { return withImpl(param, "local", sym.getSimpleName()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java index 813a6390f9..6a050ca5b3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java @@ -25,6 +25,7 @@ import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; import net.sourceforge.pmd.lang.java.symbols.SymbolicValue; @@ -61,6 +62,7 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { private List memberClasses = new ArrayList<>(); private List methods = new ArrayList<>(); private List ctors = new ArrayList<>(); + private List recordComponents = null; private List enumConstants = null; private PSet annotations = HashTreePSet.empty(); @@ -120,6 +122,7 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { fields = Collections.unmodifiableList(fields); memberClasses = Collections.unmodifiableList(memberClasses); enumConstants = CollectionUtil.makeUnmodifiableAndNonNull(enumConstants); + recordComponents = CollectionUtil.makeUnmodifiableAndNonNull(recordComponents); if (EnclosingInfo.NO_ENCLOSING.equals(enclosingInfo)) { if (names.canonicalName == null || names.simpleName == null) { @@ -227,6 +230,9 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { if ((accessFlags & Opcodes.ACC_ENUM) != 0) { this.enumConstants = new ArrayList<>(); } + if ((accessFlags & Opcodes.ACC_RECORD) != 0) { + this.recordComponents = new ArrayList<>(); + } } void setOuterClass(ClassStub outer, @Nullable String methodName, @Nullable String methodDescriptor) { @@ -262,6 +268,13 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { ctors.add(methodStub); } + void addRecordComponent(RecordComponentStub recordComponentStub) { + if (recordComponents == null) { + recordComponents = new ArrayList<>(); + } + recordComponents.add(recordComponentStub); + } + @Override public void addAnnotation(SymAnnot annot) { annotations = annotations.plus(annot); @@ -379,6 +392,14 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { return enumConstants; } + + @Override + public @NonNull List getRecordComponents() { + parseLock.ensureParsed(); + return recordComponents; + } + + @Override public JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() { parseLock.ensureParsed(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubBuilder.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubBuilder.java index a48c70644f..0addcb0e7a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubBuilder.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubBuilder.java @@ -10,6 +10,7 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; import org.objectweb.asm.TypeReference; @@ -48,6 +49,27 @@ class ClassStubBuilder extends ClassVisitor { return new AnnotationBuilderVisitor(myStub, resolver, visible, descriptor); } + + @Override + public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { + RecordComponentStub componentStub = new RecordComponentStub(myStub, name, descriptor, signature); + myStub.addRecordComponent(componentStub); + return new RecordComponentVisitor(api) { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + return new AnnotationBuilderVisitor(componentStub, resolver, visible, descriptor); + } + + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, @Nullable TypePath typePath, String descriptor, boolean visible) { + assert new TypeReference(typeRef).getSort() == TypeReference.FIELD : typeRef; + return new AnnotationBuilderVisitor.TypeAnnotBuilderImpl(resolver, componentStub, typeRef, typePath, visible, descriptor); + } + + }; + } + + @Override public void visitOuterClass(String ownerInternalName, @Nullable String methodName, @Nullable String methodDescriptor) { isInnerNonStaticClass = true; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/LazyTypeSig.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/LazyTypeSig.java index 922c5c2078..8fbaf61721 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/LazyTypeSig.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/LazyTypeSig.java @@ -39,6 +39,9 @@ class LazyTypeSig { JTypeMirror get(Substitution subst) { + if (Substitution.isEmptySubst(subst)) { + return get(); + } return get().subst(subst); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/RecordComponentStub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/RecordComponentStub.java new file mode 100644 index 0000000000..7020b14b75 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/RecordComponentStub.java @@ -0,0 +1,41 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.asm; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.TypeReference; + +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; +import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.Substitution; + +/** + * @author ClΓ©ment Fournier + */ +class RecordComponentStub extends MemberStubBase implements JRecordComponentSymbol, TypeAnnotationReceiver { + + private final LazyTypeSig type; + + + RecordComponentStub(ClassStub classStub, String name, String descriptor, String signature) { + super(classStub, name, JRecordComponentSymbol.RECORD_COMPONENT_MODIFIERS); + this.type = new LazyTypeSig(classStub, descriptor, signature); + } + + + @Override + public JTypeMirror getTypeMirror(Substitution substitution) { + return type.get(substitution); + } + + + @Override + public void acceptTypeAnnotation(int typeRef, @Nullable TypePath path, SymAnnot annot) { + assert new TypeReference(typeRef).getSort() == TypeReference.FIELD : typeRef; + this.type.addTypeAnnotation(path, annot); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstClassSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstClassSym.java index 647924c3ab..fc324065c5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstClassSym.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstClassSym.java @@ -33,6 +33,7 @@ import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; import net.sourceforge.pmd.lang.java.symbols.internal.ImplicitMemberSymbols; @@ -54,6 +55,7 @@ final class AstClassSym private final List declaredCtors; private final List declaredFields; private final List enumConstants; // subset of declaredFields + private final List recordComponents; private final PSet annotAttributes; AstClassSym(ASTTypeDeclaration node, @@ -70,13 +72,12 @@ final class AstClassSym final List myCtors = new ArrayList<>(); final List myFields = new ArrayList<>(); final List enumConstants; - final List recordComponents; + final List recordComponents; if (isRecord()) { ASTRecordComponentList components = Objects.requireNonNull(node.getRecordComponents(), "Null component list for " + node); - recordComponents = mapComponentsToMutableList(factory, components); - myFields.addAll(recordComponents); + recordComponents = mapComponentsToMutableList(factory, components, myFields); JConstructorSymbol canonicalRecordCtor = ImplicitMemberSymbols.recordConstructor(this, recordComponents, components.isVarargs()); myCtors.add(canonicalRecordCtor); @@ -123,7 +124,7 @@ final class AstClassSym // then the recordsComponents contains all record components // for which we must synthesize an accessor (explicitly declared // accessors have been filtered out) - for (JFieldSymbol component : recordComponents) { + for (JRecordComponentSymbol component : recordComponents) { myMethods.add(ImplicitMemberSymbols.recordAccessor(this, component)); } } @@ -142,15 +143,19 @@ final class AstClassSym this.declaredCtors = Collections.unmodifiableList(myCtors); this.declaredFields = Collections.unmodifiableList(myFields); this.enumConstants = CollectionUtil.makeUnmodifiableAndNonNull(enumConstants); + this.recordComponents = CollectionUtil.makeUnmodifiableAndNonNull(recordComponents); this.annotAttributes = isAnnotation() ? getDeclaredMethods().stream().filter(JMethodSymbol::isAnnotationAttribute).map(JElementSymbol::getSimpleName).collect(CollectionUtil.toPersistentSet()) : HashTreePSet.empty(); } - private List mapComponentsToMutableList(AstSymFactory factory, ASTRecordComponentList components) { - List list = new ArrayList<>(); + private List mapComponentsToMutableList(AstSymFactory factory, + ASTRecordComponentList components, + List fieldSyms) { + List list = new ArrayList<>(); for (ASTRecordComponent comp : components) { - list.add(new AstFieldSym(comp.getVarId(), factory, this)); + list.add(new AstRecordComponentSym(comp, factory, this)); + fieldSyms.add(new AstFieldSym(comp.getVarId(), factory, this)); } return list; } @@ -216,7 +221,12 @@ final class AstClassSym public @NonNull List getEnumConstants() { return enumConstants; } - + + @Override + public @NonNull List getRecordComponents() { + return recordComponents; + } + @Override public @Nullable JClassType getSuperclassType(Substitution substitution) { TypeSystem ts = getTypeSystem(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstFieldSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstFieldSym.java index 53c29ee8c8..b372546f2f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstFieldSym.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstFieldSym.java @@ -48,6 +48,7 @@ final class AstFieldSym extends AbstractAstVariableSym implements JFieldSymbol { return node.isEnumConstant(); } + @Override public @NonNull JClassSymbol getEnclosingClass() { return owner; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstRecordComponentSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstRecordComponentSym.java new file mode 100644 index 0000000000..c68a459015 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstRecordComponentSym.java @@ -0,0 +1,49 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.ast; + +import static net.sourceforge.pmd.lang.java.types.TypeOps.subst; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTRecordComponent; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; +import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.Substitution; + +/** + * @author ClΓ©ment Fournier + */ +class AstRecordComponentSym extends AbstractAstAnnotableSym implements JRecordComponentSymbol { + + private final JClassSymbol owner; + + AstRecordComponentSym(ASTRecordComponent node, AstSymFactory symFactory, JClassSymbol owner) { + super(node, symFactory); + this.owner = owner; + } + + @Override + public String getSimpleName() { + return node.getVarId().getName(); + } + + @Override + public @NonNull JClassSymbol getEnclosingClass() { + return owner; + } + + @Override + public JTypeMirror getTypeMirror(Substitution subst) { + return subst(node.getVarId().getTypeMirror(), subst); + } + + @Override + public R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitRecordComponent(this, param); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsUtil.java index ee25027de4..1a5ab9f5c1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsUtil.java @@ -14,6 +14,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPatternExpression; import net.sourceforge.pmd.lang.java.ast.ASTRecordPattern; import net.sourceforge.pmd.lang.java.ast.ASTTypePattern; import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTUnnamedPattern; import net.sourceforge.pmd.lang.java.ast.ASTVariableId; import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.ast.UnaryOp; @@ -89,14 +90,16 @@ final class PatternBindingsUtil { static BindSet bindersOfPattern(ASTPattern pattern) { if (pattern instanceof ASTTypePattern) { - return BindSet.whenTrue(HashTreePSet.singleton(((ASTTypePattern) pattern).getVarId())); - } else if (pattern instanceof ASTRecordPattern) { - // record pattern might not bind a variable for the whole record... - ASTVariableId varId = ((ASTRecordPattern) pattern).getVarId(); - if (varId == null) { - return BindSet.whenTrue(BindSet.noBindings()); + if (!((ASTTypePattern) pattern).getVarId().isUnnamed()) { + return BindSet.whenTrue(HashTreePSet.singleton(((ASTTypePattern) pattern).getVarId())); } - return BindSet.whenTrue(HashTreePSet.singleton(varId)); + return BindSet.EMPTY; + } else if (pattern instanceof ASTRecordPattern) { + return ((ASTRecordPattern) pattern) + .getComponentPatterns().toStream() + .reduce(BindSet.EMPTY, (bs, pat) -> bs.union(bindersOfPattern(pat))); + } else if (pattern instanceof ASTUnnamedPattern) { + return BindSet.EMPTY; } else { throw AssertionUtil.shouldNotReachHere("no other instances of pattern should exist: " + pattern); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java index 72f095e2fd..573b218059 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.symbols.table.internal; import static net.sourceforge.pmd.lang.java.symbols.table.internal.AbruptCompletionAnalysis.canCompleteNormally; import static net.sourceforge.pmd.lang.java.symbols.table.internal.PatternBindingsUtil.bindersOfExpr; +import static net.sourceforge.pmd.lang.java.symbols.table.internal.PatternBindingsUtil.bindersOfPattern; import java.util.ArrayDeque; import java.util.Collections; @@ -50,6 +51,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTLocalClassStatement; import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTLoopStatement; import net.sourceforge.pmd.lang.java.ast.ASTModifierList; +import net.sourceforge.pmd.lang.java.ast.ASTPattern; import net.sourceforge.pmd.lang.java.ast.ASTResource; import net.sourceforge.pmd.lang.java.ast.ASTResourceList; import net.sourceforge.pmd.lang.java.ast.ASTStatement; @@ -363,8 +365,8 @@ public final class SymbolTableResolver { ASTSwitchLabel label = branch.getLabel(); // collect all bindings. Maybe it's illegal to use composite label with bindings, idk BindSet bindings = - label.getExprList().reduce(BindSet.EMPTY, - (bindSet, expr) -> bindSet.union(bindersOfExpr(expr))); + label.children(ASTPattern.class) + .reduce(BindSet.EMPTY, (bindSet, pat) -> bindSet.union(bindersOfPattern(pat))); // visit guarded patterns in label setTopSymbolTableAndVisit(label, ctx); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/CaptureMatcher.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/CaptureMatcher.java index 22ecaab3be..bb84c06ceb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/CaptureMatcher.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/CaptureMatcher.java @@ -126,7 +126,7 @@ final class CaptureMatcher implements JTypeVar { @Override public String toString() { - return captured == null ? "unbound capture matcher" - : "bound(" + captured.toString() + ")"; + return captured == null ? "unbound capture matcher of (" + wild + ")" + : "bound(" + captured + ")"; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/JVariableSig.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/JVariableSig.java index ea5fe8bc65..38dec0a31f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/JVariableSig.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/JVariableSig.java @@ -88,8 +88,13 @@ public class JVariableSig { ? ((JClassType) declarator).getTypeParamSubst() : Substitution.EMPTY; // array - return declarator.isRaw() ? ClassTypeImpl.eraseToRaw(sym.getTypeMirror(Substitution.EMPTY), subst) + JTypeMirror symType = declarator.isRaw() + ? ClassTypeImpl.eraseToRaw(sym.getTypeMirror(Substitution.EMPTY), subst) : sym.getTypeMirror(subst); + if (symType instanceof JWildcardType) { + throw new IllegalStateException("Forgotten capture of " + this.declarator + " for symbol " + sym); + } + return symType; } static JVariableSig.FieldSig forField(JTypeMirror declarator, JFieldSymbol sym) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java index 5a47ae6223..93af7d4a89 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java @@ -11,6 +11,8 @@ import static net.sourceforge.pmd.lang.java.types.TypeConversion.capture; import static net.sourceforge.pmd.lang.java.types.TypeConversion.unaryNumericPromotion; import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import java.util.List; + import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -42,8 +44,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPattern; import net.sourceforge.pmd.lang.java.ast.ASTPatternExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPatternList; +import net.sourceforge.pmd.lang.java.ast.ASTRecordComponent; +import net.sourceforge.pmd.lang.java.ast.ASTRecordPattern; import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; import net.sourceforge.pmd.lang.java.ast.ASTSuperExpression; import net.sourceforge.pmd.lang.java.ast.ASTSwitchExpression; @@ -53,9 +57,11 @@ import net.sourceforge.pmd.lang.java.ast.ASTTemplateExpression; import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter; import net.sourceforge.pmd.lang.java.ast.ASTTypePattern; import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTUnnamedPattern; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTVariableId; @@ -69,6 +75,7 @@ import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.table.coreimpl.NameResolver; import net.sourceforge.pmd.lang.java.types.JArrayType; @@ -178,6 +185,43 @@ public final class LazyTypeResolver extends JavaVisitorBase components = ((JClassSymbol) recordType).getRecordComponents(); + if (compIndex < components.size()) { + JRecordComponentSymbol sym = components.get(compIndex); + return ((JClassType) type).getDeclaredField(sym.getSimpleName()).getTypeMirror(); + } + } + return ts.ERROR; + } + + + @Override + public @NonNull JTypeMirror visit(ASTRecordComponent node, TypingContext data) { + return node.getVarId().getTypeMirror(); + } + /* EXPRESSIONS */ @@ -410,11 +494,7 @@ public final class LazyTypeResolver extends JavaVisitorBase { + if (!(s instanceof InferenceVar)) { + return s; + } else { + InferenceVar ivar = (InferenceVar) s; + return ivar.getInst() != null ? ivar.getInst() : s.getTypeSystem().UNBOUNDED_WILD; + } + }); + } + /** * Copy variable in this inference context to the given context */ diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 8db08d0343..a8a3169254 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1723,6 +1723,40 @@ public class Foo { + + + + Wherever possible, use `EnumSet` or `EnumMap` instead of `HashSet` and `HashMap` when the keys + are of an enum type. The specialized enum collections are more space- and time-efficient. + This rule reports constructor expressions for hash sets or maps whose key + type is an enum type. + + 3 + + newSet() { + return new HashSet<>(); // Could be EnumSet.noneOf(Example.class) + } + + public static Map newMap() { + return new HashMap<>(); // Could be new EnumMap<>(Example.class) + } + } + ]]> + + + + + + diff --git a/pmd-java/src/main/resources/rulesets/java/quickstart.xml b/pmd-java/src/main/resources/rulesets/java/quickstart.xml index da02719225..4afb964163 100644 --- a/pmd-java/src/main/resources/rulesets/java/quickstart.xml +++ b/pmd-java/src/main/resources/rulesets/java/quickstart.xml @@ -52,6 +52,7 @@ + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsTest.java new file mode 100644 index 0000000000..5dcb333fc4 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseEnumCollectionsTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import net.sourceforge.pmd.test.PmdRuleTst; + +class UseEnumCollectionsTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java index a3fdc175ab..dd63b8d47d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java @@ -4,14 +4,31 @@ package net.sourceforge.pmd.lang.java.symbols.internal.asm; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +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.lang.reflect.Modifier; +import java.util.List; + +import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; import org.pcollections.PSet; import net.sourceforge.pmd.lang.java.JavaParsingHelper; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JRecordComponentSymbol; +import net.sourceforge.pmd.lang.java.types.JClassType; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.Substitution; import net.sourceforge.pmd.lang.java.types.TypeSystem; +import net.sourceforge.pmd.util.CollectionUtil; class ClassStubTest { // while parsing the annotation type, ClassStub's parseLock.ensureParsed() @@ -20,9 +37,102 @@ class ClassStubTest { @Test void loadAndParseAnnotation() { // class stub - annotation type - TypeSystem typeSystem = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); - JClassSymbol classSymbol = typeSystem.getClassSymbol("java.lang.Deprecated"); + TypeSystem ts = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol classSymbol = ts.getClassSymbol("java.lang.Deprecated"); PSet annotationAttributeNames = classSymbol.getAnnotationAttributeNames(); assertFalse(annotationAttributeNames.isEmpty()); } + + + @Test + void recordReflectionTest() { + TypeSystem ts = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol pointRecord = loadRecordClass(ts, "Point"); + List components = pointRecord.getRecordComponents(); + assertThat(components, hasSize(2)); + assertThat(components.get(0).getSimpleName(), equalTo("x")); + assertThat(components.get(1).getSimpleName(), equalTo("y")); + int modifiers = components.get(0).getModifiers(); + assertEquals(JRecordComponentSymbol.RECORD_COMPONENT_MODIFIERS, modifiers, Modifier.toString(modifiers)); + + JClassType ty = (JClassType) ts.typeOf(pointRecord, false); + assertEquals(ty.getDeclaredField("x").getTypeMirror(), ts.INT); + assertEquals(ty.getDeclaredField("y").getTypeMirror(), ts.INT); + + } + + + + @Test + void varargsRecordReflectionTest() { + TypeSystem ts = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol record = loadRecordClass(ts, "Varargs"); + List components = record.getRecordComponents(); + assertThat(components, hasSize(1)); + assertThat(components.get(0).getSimpleName(), equalTo("varargs")); + JClassType ty = (JClassType) ts.typeOf(record, false); + assertEquals(ty.getDeclaredField("varargs").getTypeMirror(), ts.arrayType(ts.FLOAT)); + + List ctors = record.getConstructors(); + assertThat(ctors, hasSize(1)); + assertTrue(ctors.get(0).isVarargs(), "varargs"); + assertEquals(ctors.get(0).getFormalParameterTypes(Substitution.EMPTY).get(0), ts.arrayType(ts.FLOAT)); + } + + + @Test + void annotatedRecordReflectionTest() { + TypeSystem ts = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol record = loadRecordClass(ts, "Annotated"); + List components = record.getRecordComponents(); + assertThat(components, hasSize(2)); + + assertThat(components.get(0).getSimpleName(), equalTo("x")); + // Interestingly record components cannot be deprecated. + // The field and accessor method are marked with the deprecated annotation though + assertNull(components.get(0).getDeclaredAnnotation(Deprecated.class), "should not be deprecated"); + assertNotNull(record.getDeclaredField("x").getDeclaredAnnotation(Deprecated.class), "should be deprecated"); + + JClassSymbol annot = ts.getClassSymbol("net.sourceforge.pmd.lang.java.symbols.recordclasses.TypeAnnotation"); + assertNotNull(annot, "annot should exist"); + + JClassType ty = (JClassType) ts.typeOf(record, false); + JClassType withTyAnnotation = (JClassType) ty.getDeclaredField("strings").getTypeMirror(); + assertIsListWithTyAnnotation(withTyAnnotation); + + List ctors = record.getConstructors(); + assertThat(ctors, hasSize(1)); + JClassType secondParm = (JClassType) ctors.get(0).getFormalParameterTypes(Substitution.EMPTY).get(1); + assertIsListWithTyAnnotation(secondParm); + } + + @Test + void targetRecordComponentReflectionTest() { + TypeSystem ts = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol record = loadRecordClass(ts, "AnnotatedForRecord"); + List components = record.getRecordComponents(); + assertThat(components, hasSize(1)); + + assertThat(components.get(0).getSimpleName(), equalTo("x")); + assertThat(components.get(0).getDeclaredAnnotations(), hasSize(1)); + + } + + + private static void assertIsListWithTyAnnotation(JClassType withTyAnnotation) { + assertThat(withTyAnnotation.getSymbol().getBinaryName(), equalTo("java.util.List")); + JTypeMirror tyArg = withTyAnnotation.getTypeArgs().get(0); + assertThat(tyArg.getTypeAnnotations(), hasSize(1)); + assertThat(CollectionUtil.asSingle(tyArg.getTypeAnnotations()).getAnnotationSymbol().getBinaryName(), + equalTo("net.sourceforge.pmd.lang.java.symbols.recordclasses.TypeAnnotation")); + } + + + private static @NonNull JClassSymbol loadRecordClass(TypeSystem typeSystem, String simpleName) { + String binaryName = "net.sourceforge.pmd.lang.java.symbols.recordclasses." + simpleName; + JClassSymbol sym = typeSystem.getClassSymbol(binaryName); + assertNotNull(sym, binaryName + " not found"); + assertTrue(sym.isRecord(), "is a record"); + return sym; + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java index 6827cc2a92..f69b79479c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java @@ -32,7 +32,7 @@ class TypesTreeDumpTest extends BaseTreeDumpTest { @Override public @NonNull BaseParsingHelper getParser() { - return JavaParsingHelper.DEFAULT.withResourceContext(getClass()); + return JavaParsingHelper.DEFAULT.withResourceContext(getClass(), "dumptests"); } @Test @@ -40,6 +40,16 @@ class TypesTreeDumpTest extends BaseTreeDumpTest { doTest("IteratorUtilCopy"); } + @Test + void testSwitchExpressionWithPatterns() { + doTest("SwitchExpressionWithPatterns"); + } + + @Test + void testUnnamedPatterns() { + doTest("UnnamedPatterns"); + } + @Override protected @NonNull String normalize(@NonNull String str) { return super.normalize(str) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternVarScopingTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternVarScopingTests.kt index 341aa08587..ee3bd999e4 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternVarScopingTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternVarScopingTests.kt @@ -5,9 +5,15 @@ package net.sourceforge.pmd.lang.java.symbols.table.internal import io.kotest.assertions.withClue +import io.kotest.matchers.collections.shouldBeSingleton +import io.kotest.matchers.collections.shouldMatchEach +import net.sourceforge.pmd.lang.java.ast.* +import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.since +import net.sourceforge.pmd.lang.java.ast.JavaVersion.J17 +import net.sourceforge.pmd.lang.java.ast.JavaVersion.J22 +import net.sourceforge.pmd.lang.java.types.* import net.sourceforge.pmd.lang.test.ast.shouldBe import net.sourceforge.pmd.lang.test.ast.shouldBeA -import net.sourceforge.pmd.lang.java.ast.* /** * @@ -38,7 +44,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTestContainer("Bindings with if/else", javaVersion = JavaVersion.J17) { + parserTestContainer("Bindings with if/else", javaVersion = J17) { doTest("If with then that falls through") { checkVars(firstIsPattern = true, secondIsPattern = false) { """ @@ -107,7 +113,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTestContainer("Bindings within condition", javaVersion = JavaVersion.J17) { + parserTestContainer("Bindings within condition", javaVersion = J17) { doTest("Condition with and") { checkVars(firstIsPattern = true, secondIsPattern = false) { """ @@ -145,7 +151,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTestContainer("Bindings within ternary", javaVersion = JavaVersion.J17) { + parserTestContainer("Bindings within ternary", javaVersion = J17) { doTest("Positive cond") { checkVars(firstIsPattern = true, secondIsPattern = false) { """ @@ -165,7 +171,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTest("Bindings within labeled stmt", javaVersion = JavaVersion.J17) { + parserTest("Bindings within labeled stmt", javaVersion = J17) { checkVars(firstIsPattern = false, secondIsPattern = true) { """ a -> { @@ -180,7 +186,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTest("Bindings within switch expr with yield", javaVersion = JavaVersion.J17) { + parserTest("Bindings within switch expr with yield", javaVersion = J17) { checkVars(firstIsPattern = false, secondIsPattern = true) { """ a -> switch (1) { @@ -198,7 +204,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } - parserTestContainer("Bindings within for loop", javaVersion = JavaVersion.J17) { + parserTestContainer("Bindings within for loop", javaVersion = J17) { doTest("Positive cond") { checkVars(firstIsPattern = true, secondIsPattern = false) { """ @@ -272,7 +278,7 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } - parserTestContainer("Bindings within while loop", javaVersion = JavaVersion.J17) { + parserTestContainer("Bindings within while loop", javaVersion = J17) { doTest("Positive cond") { checkVars(firstIsPattern = true, secondIsPattern = false) { """ @@ -329,4 +335,109 @@ class PatternVarScopingTests : ProcessorTestSpec({ } } } + + parserTestContainer("Bindings in switch", javaVersions = since(J22)) { + + doTest("Type tests") { + val switch = parser.parse( + """ + class Foo { + void foo(Object foo) { + return switch (foo) { + case char[] array -> new String(array); + case String string -> string; + default -> throw new RuntimeException(); + }; + } + } + """ + ).descendants(ASTSwitchExpression::class.java).firstOrThrow() + + switch.withTypeDsl { + switch.varAccesses("array").shouldBeSingleton { + it shouldHaveType char.toArray() + } + switch.varAccesses("string").shouldBeSingleton { + it shouldHaveType ts.STRING + } + } + } + + doTest("Record tests") { + val acu = parser.parse( + """ + class Foo { + record Bar(int x, float... ys) {} + void foo(Object foo) { + return switch (foo) { + case Bar bar -> bar; + case Bar(int x, float[] ys) -> { + System.out.println(ys); + yield x; + } + case Bar(var x, var ys) -> { + System.out.println(ys); + yield x; + } + default -> throw new RuntimeException(); + }; + } + } + """ + ) + val (_, bar) = acu.declaredTypeSignatures() + val switch = acu.descendants(ASTSwitchExpression::class.java).firstOrThrow() + + switch.withTypeDsl { + switch.varAccesses("bar").shouldBeSingleton { + it shouldHaveType bar + } + switch.varAccesses("x").forEach { + withClue(it) { + it shouldHaveType int + } + } + switch.varAccesses("ys").forEach { + withClue(it) { + it shouldHaveType float.toArray() + } + } + } + } + doTest("Generic record") { + val acu = parser.parse( + """ + class Foo { + record Bar(T y) {} + void foo(Object foo) { + Object xx = switch (foo) { + case Bar bar -> bar; + case Bar(var yy) -> { // yy : Object, pat: Bar + yield yy; + }}; + Bar bar2 = new Bar(null); + switch (bar2) { + case Bar(var xx) -> { // xx : Serializable, pat: Bar + throw xx; + } + } + } + } + """ + ) + val (_, bar) = acu.declaredTypeSignatures() + + acu.withTypeDsl { + acu.varAccesses("bar").shouldBeSingleton { + it shouldHaveType bar.erasure + } + acu.varAccesses("yy").shouldBeSingleton { + it shouldHaveType captureMatcher(`?`) + } + acu.varAccesses("xx").shouldBeSingleton { + it shouldHaveType captureMatcher(`?` extends Exception::class) + } + } + } + } }) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeCreationDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeCreationDsl.kt index e0afd0284a..af098e32f3 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeCreationDsl.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeCreationDsl.kt @@ -149,4 +149,6 @@ class WildcardDsl(override val ts: TypeSystem) : JWildcardType by ts.UNBOUNDED_W other is JWildcardType && isSameType(this, other) override fun hashCode(): Int = ts.UNBOUNDED_WILD.hashCode() + + override fun toString(): String = TypePrettyPrint.prettyPrint(this) } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/EnhancedTypeCheckingSwitch.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/EnhancedTypeCheckingSwitch.txt index 29cee4fc6f..86a1b73891 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/EnhancedTypeCheckingSwitch.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/EnhancedTypeCheckingSwitch.txt @@ -72,7 +72,7 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Color: ", @Empty = false, @Image = "\"Color: \"", @Length = 7, @LiteralText = "\"Color: \"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- MethodCall[@CompileTimeConstant = false, @Image = "toString", @MethodName = "toString", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- SwitchArrowBranch[@Default = false] | | +- SwitchLabel[@Default = false] @@ -88,7 +88,7 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Record class: ", @Empty = false, @Image = "\"Record class: \"", @Length = 14, @LiteralText = "\"Record class: \"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- MethodCall[@CompileTimeConstant = false, @Image = "toString", @MethodName = "toString", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- SwitchArrowBranch[@Default = false] | | +- SwitchLabel[@Default = false] @@ -107,7 +107,7 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Array of ints of length", @Empty = false, @Image = "\"Array of ints of length\"", @Length = 23, @LiteralText = "\"Array of ints of length\"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "length", @Name = "length", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "ia", @Name = "ia", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "ia", @Name = "ia", @ParenthesisDepth = 0, @Parenthesized = false] | +- SwitchArrowBranch[@Default = true] | +- SwitchLabel[@Default = true] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ExhaustiveSwitch.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ExhaustiveSwitch.txt index b8e61b1265..53e55e5fd5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ExhaustiveSwitch.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ExhaustiveSwitch.txt @@ -21,7 +21,7 @@ | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "String"] | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | +- MethodCall[@CompileTimeConstant = false, @Image = "length", @MethodName = "length", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "s", @Name = "s", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "s", @Name = "s", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- SwitchArrowBranch[@Default = false] | | +- SwitchLabel[@Default = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/Jep440_RecordPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/Jep440_RecordPatterns.txt index 9978eb88b9..ddeabb4331 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/Jep440_RecordPatterns.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/Jep440_RecordPatterns.txt @@ -147,20 +147,16 @@ | | | | +- PatternList[@Empty = false, @Size = 2] | | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "x", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "x", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "y", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "y", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "c", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "c", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "lr", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "lr", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -270,12 +266,10 @@ | | +- PatternList[@Empty = false, @Size = 2] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "f", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "f", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] | +- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false] | | +- TypeExpression[@CompileTimeConstant = false, @ParenthesisDepth = 0, @Parenthesized = false] @@ -321,8 +315,7 @@ | | +- PatternList[@Empty = false, @Size = 1] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -358,8 +351,7 @@ | +- PatternList[@Empty = false, @Size = 1] | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] +- Block[@Empty = false, @Size = 1, @containsComment = false] +- ExpressionStatement[] +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RecordPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RecordPatterns.txt index 55cceb35ad..de19e8eb32 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RecordPatterns.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RecordPatterns.txt @@ -159,7 +159,7 @@ | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "System"] | +- ArgumentList[@Empty = false, @Size = 1] | +- MethodCall[@CompileTimeConstant = false, @Image = "c", @MethodName = "c", @ParenthesisDepth = 0, @Parenthesized = false] - | +- AmbiguousName[@CompileTimeConstant = false, @Image = "ul", @Name = "ul", @ParenthesisDepth = 0, @Parenthesized = false] + | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "ul", @Name = "ul", @ParenthesisDepth = 0, @Parenthesized = false] | +- ArgumentList[@Empty = true, @Size = 0] +- MethodDeclaration[@Abstract = false, @Arity = 1, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Name = "printColorOfUpperLeftPoint", @Overridden = false, @Static = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true] | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] @@ -281,20 +281,16 @@ | | | | +- PatternList[@Empty = false, @Size = 2] | | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "x", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "x", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "y", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "y", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "c", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "c", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "lr", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "lr", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -433,8 +429,7 @@ | | +- PatternList[@Empty = false, @Size = 1] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -465,8 +460,7 @@ | | +- PatternList[@Empty = false, @Size = 1] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -506,8 +500,7 @@ | | +- PatternList[@Empty = false, @Size = 1] | | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | +- Block[@Empty = false, @Size = 1, @containsComment = false] | +- ExpressionStatement[] | +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] @@ -543,8 +536,7 @@ | +- PatternList[@Empty = false, @Size = 1] | +- TypePattern[@EffectiveVisibility = Visibility.V_PACKAGE, @Visibility = Visibility.V_PACKAGE] | +- ModifierList[@EffectiveModifiers = (), @ExplicitModifiers = ()] - | +- ClassType[@FullyQualified = false, @PackageQualifier = null, @SimpleName = "var"] - | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] + | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "s", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = true, @Unnamed = false, @Visibility = Visibility.V_LOCAL] +- Block[@Empty = false, @Size = 1, @containsComment = false] +- ExpressionStatement[] +- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RefiningPatternsInSwitch.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RefiningPatternsInSwitch.txt index 7ec2b04049..7034cc2713 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RefiningPatternsInSwitch.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/RefiningPatternsInSwitch.txt @@ -64,7 +64,7 @@ | | +- IfStatement[@Else = false] | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.GT, @ParenthesisDepth = 0, @Parenthesized = false] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "calculateArea", @MethodName = "calculateArea", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "t", @Name = "t", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "t", @Name = "t", @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | +- NumericLiteral[@Base = 10, @CompileTimeConstant = true, @DoubleLiteral = false, @FloatLiteral = false, @Image = "100", @IntLiteral = true, @Integral = true, @LiteralText = "100", @LongLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @ValueAsDouble = 100.0, @ValueAsFloat = 100.0, @ValueAsInt = 100, @ValueAsLong = 100] | | +- Block[@Empty = false, @Size = 2, @containsComment = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ScopeOfPatternVariableDeclarations.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ScopeOfPatternVariableDeclarations.txt index 78864f8985..120881a881 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ScopeOfPatternVariableDeclarations.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21/ScopeOfPatternVariableDeclarations.txt @@ -57,7 +57,7 @@ | | +- IfStatement[@Else = false] | | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.EQ, @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- MethodCall[@CompileTimeConstant = false, @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] | | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | | +- NumericLiteral[@Base = 10, @CompileTimeConstant = true, @DoubleLiteral = false, @FloatLiteral = false, @Image = "7", @IntLiteral = true, @Integral = true, @LiteralText = "7", @LongLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @ValueAsDouble = 7.0, @ValueAsFloat = 7.0, @ValueAsInt = 7, @ValueAsLong = 7] | | | +- Block[@Empty = false, @Size = 1, @containsComment = false] @@ -88,7 +88,7 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Invalid Integer argument of value ", @Empty = false, @Image = "\"Invalid Integer argument of value \"", @Length = 34, @LiteralText = "\"Invalid Integer argument of value \"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- MethodCall[@CompileTimeConstant = false, @Image = "intValue", @MethodName = "intValue", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "i", @Name = "i", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "i", @Name = "i", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- SwitchArrowBranch[@Default = true] | +- SwitchLabel[@Default = true] @@ -114,7 +114,7 @@ | | +- IfStatement[@Else = false] | | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.EQ, @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- MethodCall[@CompileTimeConstant = false, @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] | | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | | +- NumericLiteral[@Base = 10, @CompileTimeConstant = true, @DoubleLiteral = false, @FloatLiteral = false, @Image = "7", @IntLiteral = true, @Integral = true, @LiteralText = "7", @LongLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @ValueAsDouble = 7.0, @ValueAsFloat = 7.0, @ValueAsInt = 7, @ValueAsLong = 7] | | | +- Block[@Empty = false, @Size = 1, @containsComment = false] @@ -128,7 +128,7 @@ | | +- IfStatement[@Else = false] | | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.EQ, @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- MethodCall[@CompileTimeConstant = false, @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "c", @Name = "c", @ParenthesisDepth = 0, @Parenthesized = false] | | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | | +- NumericLiteral[@Base = 10, @CompileTimeConstant = true, @DoubleLiteral = false, @FloatLiteral = false, @Image = "9", @IntLiteral = true, @Integral = true, @LiteralText = "9", @LongLiteral = false, @ParenthesisDepth = 0, @Parenthesized = false, @ValueAsDouble = 9.0, @ValueAsFloat = 9.0, @ValueAsInt = 9, @ValueAsLong = 9] | | | +- Block[@Empty = false, @Size = 1, @containsComment = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21p/Jep443_UnnamedPatternsAndVariables.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21p/Jep443_UnnamedPatternsAndVariables.txt index b8bb972f31..1c3cbe4f50 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21p/Jep443_UnnamedPatternsAndVariables.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java21p/Jep443_UnnamedPatternsAndVariables.txt @@ -88,11 +88,11 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "x", @MethodName = "x", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = " ", @Empty = false, @Image = "\" \"", @Length = 1, @LiteralText = "\" \"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- MethodCall[@CompileTimeConstant = false, @Image = "y", @MethodName = "y", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- IfStatement[@Else = false] | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.INSTANCEOF, @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22/Jep456_UnnamedPatternsAndVariables.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22/Jep456_UnnamedPatternsAndVariables.txt index 70cc4a9389..e176618f5c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22/Jep456_UnnamedPatternsAndVariables.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22/Jep456_UnnamedPatternsAndVariables.txt @@ -88,11 +88,11 @@ | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.ADD, @ParenthesisDepth = 0, @Parenthesized = false] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "x", @MethodName = "x", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | +- StringLiteral[@CompileTimeConstant = true, @ConstValue = " ", @Empty = false, @Image = "\" \"", @Length = 1, @LiteralText = "\" \"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false] | | +- MethodCall[@CompileTimeConstant = false, @Image = "y", @MethodName = "y", @ParenthesisDepth = 0, @Parenthesized = false] - | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] + | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "p", @Name = "p", @ParenthesisDepth = 0, @Parenthesized = false] | | +- ArgumentList[@Empty = true, @Size = 0] | +- IfStatement[@Else = false] | +- InfixExpression[@CompileTimeConstant = false, @Operator = BinaryOp.INSTANCEOF, @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22p/Jep447_StatementsBeforeSuper.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22p/Jep447_StatementsBeforeSuper.txt index 9d5d763bd7..bd6303a9ed 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22p/Jep447_StatementsBeforeSuper.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java22p/Jep447_StatementsBeforeSuper.txt @@ -112,7 +112,7 @@ | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "rsaKey", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "getBytes", @MethodName = "getBytes", @ParenthesisDepth = 0, @Parenthesized = false] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "toString", @MethodName = "toString", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "rsaKey", @Name = "rsaKey", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "rsaKey", @Name = "rsaKey", @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | +- ArgumentList[@Empty = false, @Size = 1] | | | +- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "UTF_8", @Name = "UTF_8", @ParenthesisDepth = 0, @Parenthesized = false] @@ -126,7 +126,7 @@ | | | | +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_LOCAL, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = false, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "dsaKey", @PatternBinding = true, @RecordComponent = false, @ResourceDeclaration = false, @Static = false, @TypeInferred = false, @Unnamed = false, @Visibility = Visibility.V_LOCAL] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "getBytes", @MethodName = "getBytes", @ParenthesisDepth = 0, @Parenthesized = false] | | | +- MethodCall[@CompileTimeConstant = false, @Image = "toString", @MethodName = "toString", @ParenthesisDepth = 0, @Parenthesized = false] - | | | | +- AmbiguousName[@CompileTimeConstant = false, @Image = "dsaKey", @Name = "dsaKey", @ParenthesisDepth = 0, @Parenthesized = false] + | | | | +- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "dsaKey", @Name = "dsaKey", @ParenthesisDepth = 0, @Parenthesized = false] | | | | +- ArgumentList[@Empty = true, @Size = 0] | | | +- ArgumentList[@Empty = false, @Size = 1] | | | +- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "UTF_8", @Name = "UTF_8", @ParenthesisDepth = 0, @Parenthesized = false] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseEnumCollections.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseEnumCollections.xml new file mode 100644 index 0000000000..c98b1d7124 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseEnumCollections.xml @@ -0,0 +1,68 @@ + + + + + Use enumset + 1 + 8 + + This collection could be an EnumSet + + set = new HashSet<>(); + return set.contains(E.A); + } + } + ]]> + + + + + + Use enummap + 1 + 7 + + This collection could be an EnumMap + + bar() { + return new HashMap<>(); + } + } + ]]> + + + + Neg, LinkedHashSet or LinkedHashMap + 0 + bar() { + Set set = new LinkedHashSet<>(); + return new LinkedHashMap<>(); + } + } + ]]> + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/JUnitStaticSuite.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/JUnitStaticSuite.xml index 9493e6498f..657f0f3d4a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/JUnitStaticSuite.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/JUnitStaticSuite.xml @@ -12,7 +12,7 @@ import junit.framework.TestCase; import junit.framework.TestSuite; public class Foo extends TestCase { - public TestSuite suite() {} + public TestSuite suite() {} // oops, should be static } ]]> @@ -37,7 +37,7 @@ public class Foo extends TestCase { import junit.framework.TestCase; import junit.framework.TestSuite; public class Foo extends TestCase { - private static TestSuite suite() {} + private static TestSuite suite() {} // oops, should be public } ]]> @@ -64,4 +64,28 @@ public class Foo { } ]]> + + + should be static - void suite() + 1 + + + + + should be public - void suite() + 1 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/StringInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/StringInstantiation.xml index 5a41cc3102..c38e4cb5f6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/StringInstantiation.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/StringInstantiation.xml @@ -161,4 +161,19 @@ public class Foo { } ]]> + + FP in switch #5050 + 0 + new String(array); + case String string -> string; + default -> throw new RuntimeException(); + }; + } + } + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Annotated.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Annotated.class new file mode 100644 index 0000000000..d86a911c8c Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Annotated.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/AnnotatedForRecord.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/AnnotatedForRecord.class new file mode 100644 index 0000000000..638730a3bc Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/AnnotatedForRecord.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/GenericBox.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/GenericBox.class new file mode 100644 index 0000000000..47420f346a Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/GenericBox.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Point.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Point.class new file mode 100644 index 0000000000..54c0564e9d Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Point.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/RecordAnnot.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/RecordAnnot.class new file mode 100644 index 0000000000..d45b4f4d02 Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/RecordAnnot.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/SomeRecordsTestData.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/SomeRecordsTestData.java new file mode 100644 index 0000000000..b4e1304b61 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/SomeRecordsTestData.java @@ -0,0 +1,33 @@ + +package net.sourceforge.pmd.lang.java.symbols.recordclasses; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Note: this is compiled manually as it uses features of Java 21. + * Compile with + * javac --release 21 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/SomeRecordsTestData.java + */ + + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface TypeAnnotation {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.RECORD_COMPONENT) +@interface RecordAnnot {} + +record Point(int x, int y) {} +record Varargs(float... varargs) {} + +record Annotated(@Deprecated int x, + java.util.List<@TypeAnnotation String> strings) {} + + +record AnnotatedForRecord(@RecordAnnot int x) {} + +record GenericBox(T serializable, X x) {} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/TypeAnnotation.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/TypeAnnotation.class new file mode 100644 index 0000000000..82334a4926 Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/TypeAnnotation.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Varargs.class b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Varargs.class new file mode 100644 index 0000000000..47e8e8a28b Binary files /dev/null and b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/symbols/recordclasses/Varargs.class differ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/IteratorUtilCopy.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/IteratorUtilCopy.java similarity index 100% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/IteratorUtilCopy.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/IteratorUtilCopy.java diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/IteratorUtilCopy.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/IteratorUtilCopy.txt similarity index 100% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/IteratorUtilCopy.txt rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/IteratorUtilCopy.txt diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.java new file mode 100644 index 0000000000..91280de3ce --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.java @@ -0,0 +1,9 @@ +class Example { + String foo(Object foo) { + return switch (foo) { + case char[] array -> new String(array); + case String string -> string; + default -> throw new RuntimeException(); + }; + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.txt new file mode 100644 index 0000000000..e1cc096dfc --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/SwitchExpressionWithPatterns.txt @@ -0,0 +1,42 @@ ++- CompilationUnit[] + +- ClassDeclaration[@TypeMirror = "Example"] + +- ModifierList[] + +- ClassBody[] + +- MethodDeclaration[@Name = "foo"] + +- ModifierList[] + +- ClassType[@TypeMirror = "java.lang.String"] + +- FormalParameters[] + | +- FormalParameter[@TypeMirror = "java.lang.Object"] + | +- ModifierList[] + | +- ClassType[@TypeMirror = "java.lang.Object"] + | +- VariableId[@Name = "foo", @TypeMirror = "java.lang.Object"] + +- Block[] + +- ReturnStatement[] + +- SwitchExpression[@TypeMirror = "java.lang.String"] + +- VariableAccess[@Name = "foo", @TypeMirror = "java.lang.Object"] + +- SwitchArrowBranch[] + | +- SwitchLabel[] + | | +- TypePattern[@TypeMirror = "char[]"] + | | +- ModifierList[] + | | +- ArrayType[@TypeMirror = "char[]"] + | | | +- PrimitiveType[@TypeMirror = "char"] + | | | +- ArrayDimensions[] + | | | +- ArrayTypeDim[] + | | +- VariableId[@Name = "array", @TypeMirror = "char[]"] + | +- ConstructorCall[@Failed = false, @Function = "java.lang.String.new(char[]) -> java.lang.String", @MethodName = "new", @TypeMirror = "java.lang.String", @Unchecked = false, @VarargsCall = false] + | +- ClassType[@TypeMirror = "java.lang.String"] + | +- ArgumentList[] + | +- VariableAccess[@Name = "array", @TypeMirror = "char[]"] + +- SwitchArrowBranch[] + | +- SwitchLabel[] + | | +- TypePattern[@TypeMirror = "java.lang.String"] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "java.lang.String"] + | | +- VariableId[@Name = "string", @TypeMirror = "java.lang.String"] + | +- VariableAccess[@Name = "string", @TypeMirror = "java.lang.String"] + +- SwitchArrowBranch[] + +- SwitchLabel[] + +- ThrowStatement[] + +- ConstructorCall[@Failed = false, @Function = "java.lang.RuntimeException.new() -> java.lang.RuntimeException", @MethodName = "new", @TypeMirror = "java.lang.RuntimeException", @Unchecked = false, @VarargsCall = false] + +- ClassType[@TypeMirror = "java.lang.RuntimeException"] + +- ArgumentList[] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.java new file mode 100644 index 0000000000..6d89e30ef3 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.java @@ -0,0 +1,124 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +import java.util.ArrayDeque; +import java.util.List; +import java.util.Queue; +import java.util.stream.Collectors; + +/** + * @see JEP 443: Unnamed Patterns and Variables (Preview) (Java 21) + * @see JEP 456: Unnamed Variables & Patterns (Java 22) + */ +class Jep456_UnamedPatternsAndVariables { + record Point(int x, int y) { } + enum Color { RED, GREEN, BLUE } + record ColoredPoint(Point p, Color c) { } + + void unnamedPatterns1() { + ColoredPoint r = new ColoredPoint(new Point(3,4), Color.GREEN); + + if (r instanceof ColoredPoint(Point p, Color _)) { + System.out.println(p.x() + " " + p.y()); + } + + if (r instanceof ColoredPoint(Point(int x, int y), _)) { + System.out.println(x + " " + y); + } + } + + sealed abstract class Ball permits RedBall, BlueBall, GreenBall { } + final class RedBall extends Ball { } + final class BlueBall extends Ball { } + final class GreenBall extends Ball { } + + record Box(T content) { } + + void unnamedPatterns2() { + Box b = new Box<>(new RedBall()); + switch (b) { + case Box(RedBall _) -> processBox(b); + case Box(BlueBall _) -> processBox(b); + case Box(GreenBall _) -> stopProcessing(); + } + + switch (b) { + case Box(RedBall _), Box(BlueBall _) -> processBox(b); + case Box(GreenBall _) -> stopProcessing(); + case Box(_) -> pickAnotherBox(); + } + + int x = 42; + switch (b) { + // multiple patterns guarded by one guard + case Box(RedBall _), Box(BlueBall _) when x == 42 -> processBox(b); + case Box(_) -> pickAnotherBox(); + } + } + + private void processBox(Box b) {} + private void stopProcessing() {} + private void pickAnotherBox() {} + + class Order {} + private static final int LIMIT = 10; + private int sideEffect() { + return 0; + } + + void unnamedVariables(List orders) { + int total = 0; + for (Order _ : orders) { + if (total < LIMIT) { + total++; + } + } + System.out.println("total: " + total); + + for (int i = 0, _ = sideEffect(); i < 10; i++) { + System.out.println(i); + } + + Queue q = new ArrayDeque<>(); // x1, y1, z1, x2, y2, z2 .. + while (q.size() >= 3) { + int x = q.remove(); + int y = q.remove(); + int _ = q.remove(); // z is unused + Point p = new Point(x, y); + } + while (q.size() >= 3) { + var x = q.remove(); + var _ = q.remove(); + var _ = q.remove(); + Point p = new Point(x, 0); + } + } + + static class ScopedContext implements AutoCloseable { + @Override + public void close() { } + public static ScopedContext acquire() { + return new ScopedContext(); + } + } + + void unusedVariables2() { + try (var _ = ScopedContext.acquire()) { + //... acquiredContext not used ... + } + + String s = "123"; + try { + int i = Integer.parseInt(s); + System.out.println(i); + } catch (NumberFormatException _) { + System.out.println("Bad number: " + s); + } catch (Exception _) { + System.out.println("error..."); + } + + List.of("a", "b").stream().collect(Collectors.toMap(String::toUpperCase, _ -> "NO_DATA")); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt new file mode 100644 index 0000000000..25eb7dd0d7 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt @@ -0,0 +1,608 @@ ++- CompilationUnit[] + +- ImportDeclaration[] + +- ImportDeclaration[] + +- ImportDeclaration[] + +- ImportDeclaration[] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables"] + +- ModifierList[] + +- ClassBody[] + +- RecordDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | +- ModifierList[] + | +- RecordComponentList[] + | | +- RecordComponent[@TypeMirror = "int"] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableId[@Name = "x", @TypeMirror = "int"] + | | +- RecordComponent[@TypeMirror = "int"] + | | +- ModifierList[] + | | +- PrimitiveType[@TypeMirror = "int"] + | | +- VariableId[@Name = "y", @TypeMirror = "int"] + | +- RecordBody[] + +- EnumDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- ModifierList[] + | +- EnumBody[] + | +- EnumConstant[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Color.new() -> Jep456_UnamedPatternsAndVariables$Color", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color", @Unchecked = false, @VarargsCall = false] + | | +- ModifierList[] + | | +- VariableId[@Name = "RED", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- EnumConstant[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Color.new() -> Jep456_UnamedPatternsAndVariables$Color", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color", @Unchecked = false, @VarargsCall = false] + | | +- ModifierList[] + | | +- VariableId[@Name = "GREEN", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- EnumConstant[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Color.new() -> Jep456_UnamedPatternsAndVariables$Color", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color", @Unchecked = false, @VarargsCall = false] + | +- ModifierList[] + | +- VariableId[@Name = "BLUE", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + +- RecordDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | +- ModifierList[] + | +- RecordComponentList[] + | | +- RecordComponent[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- ModifierList[] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- VariableId[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | +- RecordComponent[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | +- VariableId[@Name = "c", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- RecordBody[] + +- MethodDeclaration[@Name = "unnamedPatterns1"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | +- Block[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "r", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$ColoredPoint.new(Jep456_UnamedPatternsAndVariables$Point, Jep456_UnamedPatternsAndVariables$Color) -> Jep456_UnamedPatternsAndVariables$ColoredPoint", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint", @Unchecked = false, @VarargsCall = false] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- ArgumentList[] + | | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Point.new(int, int) -> Jep456_UnamedPatternsAndVariables$Point", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point", @Unchecked = false, @VarargsCall = false] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- ArgumentList[] + | | | +- NumericLiteral[@TypeMirror = "int"] + | | | +- NumericLiteral[@TypeMirror = "int"] + | | +- FieldAccess[@Name = "GREEN", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | +- TypeExpression[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- IfStatement[] + | | +- InfixExpression[@TypeMirror = "boolean"] + | | | +- VariableAccess[@Name = "r", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | | +- PatternExpression[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | | +- PatternList[] + | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | | +- VariableId[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | | +- ModifierList[] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | | +- Block[] + | | +- ExpressionStatement[] + | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.String) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | | +- ClassType[@TypeMirror = "java.lang.System"] + | | +- ArgumentList[] + | | +- InfixExpression[@TypeMirror = "java.lang.String"] + | | +- InfixExpression[@TypeMirror = "java.lang.String"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Point.x() -> int", @MethodName = "x", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | | | +- VariableAccess[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | | +- ArgumentList[] + | | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Point.y() -> int", @MethodName = "y", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | +- VariableAccess[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | +- ArgumentList[] + | +- IfStatement[] + | +- InfixExpression[@TypeMirror = "boolean"] + | | +- VariableAccess[@Name = "r", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- PatternExpression[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ColoredPoint"] + | | +- PatternList[] + | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | | +- PatternList[] + | | | +- TypePattern[@TypeMirror = "int"] + | | | | +- ModifierList[] + | | | | +- PrimitiveType[@TypeMirror = "int"] + | | | | +- VariableId[@Name = "x", @TypeMirror = "int"] + | | | +- TypePattern[@TypeMirror = "int"] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableId[@Name = "y", @TypeMirror = "int"] + | | +- UnnamedPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Color"] + | +- Block[] + | +- ExpressionStatement[] + | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.String) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | +- ClassType[@TypeMirror = "java.lang.System"] + | +- ArgumentList[] + | +- InfixExpression[@TypeMirror = "java.lang.String"] + | +- InfixExpression[@TypeMirror = "java.lang.String"] + | | +- VariableAccess[@Name = "x", @TypeMirror = "int"] + | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | +- VariableAccess[@Name = "y", @TypeMirror = "int"] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | +- ModifierList[] + | +- PermitsList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | +- ClassBody[] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | +- ModifierList[] + | +- ExtendsList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | +- ClassBody[] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | +- ModifierList[] + | +- ExtendsList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | +- ClassBody[] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | +- ModifierList[] + | +- ExtendsList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | +- ClassBody[] + +- RecordDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | +- ModifierList[] + | +- TypeParameters[] + | | +- TypeParameter[@TypeMirror = "T"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | +- RecordComponentList[] + | | +- RecordComponent[@TypeMirror = "T"] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "T"] + | | +- VariableId[@Name = "content", @TypeMirror = "T"] + | +- RecordBody[] + +- MethodDeclaration[@Name = "unnamedPatterns2"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | +- Block[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- TypeArguments[] + | | | +- WildcardType[@TypeMirror = "? extends Jep456_UnamedPatternsAndVariables#Ball"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Box.new(Jep456_UnamedPatternsAndVariables#RedBall) -> Jep456_UnamedPatternsAndVariables$Box", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box", @Unchecked = false, @VarargsCall = false] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- TypeArguments[] + | | +- ArgumentList[] + | | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables#RedBall.new() -> Jep456_UnamedPatternsAndVariables#RedBall", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall", @Unchecked = false, @VarargsCall = false] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | +- ArgumentList[] + | +- SwitchStatement[] + | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- SwitchArrowBranch[] + | | | +- SwitchLabel[] + | | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.processBox(Jep456_UnamedPatternsAndVariables$Box) -> void", @MethodName = "processBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | | +- ArgumentList[] + | | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- SwitchArrowBranch[] + | | | +- SwitchLabel[] + | | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.processBox(Jep456_UnamedPatternsAndVariables$Box) -> void", @MethodName = "processBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | | +- ArgumentList[] + | | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- SwitchArrowBranch[] + | | +- SwitchLabel[] + | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- PatternList[] + | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | | +- ModifierList[] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.stopProcessing() -> void", @MethodName = "stopProcessing", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- ArgumentList[] + | +- SwitchStatement[] + | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- SwitchArrowBranch[] + | | | +- SwitchLabel[] + | | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | | +- PatternList[] + | | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | | +- ModifierList[] + | | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.processBox(Jep456_UnamedPatternsAndVariables$Box) -> void", @MethodName = "processBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | | +- ArgumentList[] + | | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- SwitchArrowBranch[] + | | | +- SwitchLabel[] + | | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#GreenBall"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.stopProcessing() -> void", @MethodName = "stopProcessing", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | | +- ArgumentList[] + | | +- SwitchArrowBranch[] + | | +- SwitchLabel[] + | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- PatternList[] + | | | +- UnnamedPattern[@TypeMirror = "capture#... of ? extends Jep456_UnamedPatternsAndVariables#Ball"] + | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.pickAnotherBox() -> void", @MethodName = "pickAnotherBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- ArgumentList[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- PrimitiveType[@TypeMirror = "int"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "x", @TypeMirror = "int"] + | | +- NumericLiteral[@TypeMirror = "int"] + | +- SwitchStatement[] + | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | +- SwitchArrowBranch[] + | | +- SwitchLabel[] + | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#RedBall"] + | | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | | +- PatternList[] + | | | | +- TypePattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- ModifierList[] + | | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#BlueBall"] + | | | +- Guard[] + | | | +- InfixExpression[@TypeMirror = "boolean"] + | | | +- VariableAccess[@Name = "x", @TypeMirror = "int"] + | | | +- NumericLiteral[@TypeMirror = "int"] + | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.processBox(Jep456_UnamedPatternsAndVariables$Box) -> void", @MethodName = "processBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- ArgumentList[] + | | +- VariableAccess[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | +- SwitchArrowBranch[] + | +- SwitchLabel[] + | | +- RecordPattern[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- PatternList[] + | | +- UnnamedPattern[@TypeMirror = "capture#... of ? extends Jep456_UnamedPatternsAndVariables#Ball"] + | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.pickAnotherBox() -> void", @MethodName = "pickAnotherBox", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | +- ArgumentList[] + +- MethodDeclaration[@Name = "processBox"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | | +- FormalParameter[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | | | +- TypeArguments[] + | | | +- WildcardType[@TypeMirror = "? extends Jep456_UnamedPatternsAndVariables#Ball"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Ball"] + | | +- VariableId[@Name = "b", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Box"] + | +- Block[] + +- MethodDeclaration[@Name = "stopProcessing"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | +- Block[] + +- MethodDeclaration[@Name = "pickAnotherBox"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | +- Block[] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Order"] + | +- ModifierList[] + | +- ClassBody[] + +- FieldDeclaration[] + | +- ModifierList[] + | +- PrimitiveType[@TypeMirror = "int"] + | +- VariableDeclarator[] + | +- VariableId[@Name = "LIMIT", @TypeMirror = "int"] + | +- NumericLiteral[@TypeMirror = "int"] + +- MethodDeclaration[@Name = "sideEffect"] + | +- ModifierList[] + | +- PrimitiveType[@TypeMirror = "int"] + | +- FormalParameters[] + | +- Block[] + | +- ReturnStatement[] + | +- NumericLiteral[@TypeMirror = "int"] + +- MethodDeclaration[@Name = "unnamedVariables"] + | +- ModifierList[] + | +- VoidType[@TypeMirror = "void"] + | +- FormalParameters[] + | | +- FormalParameter[@TypeMirror = "java.util.List"] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "java.util.List"] + | | | +- TypeArguments[] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Order"] + | | +- VariableId[@Name = "orders", @TypeMirror = "java.util.List"] + | +- Block[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- PrimitiveType[@TypeMirror = "int"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "total", @TypeMirror = "int"] + | | +- NumericLiteral[@TypeMirror = "int"] + | +- ForeachStatement[] + | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables#Order"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables#Order"] + | | +- VariableAccess[@Name = "orders", @TypeMirror = "java.util.List"] + | | +- Block[] + | | +- IfStatement[] + | | +- InfixExpression[@TypeMirror = "boolean"] + | | | +- VariableAccess[@Name = "total", @TypeMirror = "int"] + | | | +- VariableAccess[@Name = "LIMIT", @TypeMirror = "int"] + | | +- Block[] + | | +- ExpressionStatement[] + | | +- UnaryExpression[@TypeMirror = "int"] + | | +- VariableAccess[@Name = "total", @TypeMirror = "int"] + | +- ExpressionStatement[] + | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.String) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | | +- ClassType[@TypeMirror = "java.lang.System"] + | | +- ArgumentList[] + | | +- InfixExpression[@TypeMirror = "java.lang.String"] + | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | | +- VariableAccess[@Name = "total", @TypeMirror = "int"] + | +- ForStatement[] + | | +- ForInit[] + | | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | | +- VariableId[@Name = "i", @TypeMirror = "int"] + | | | | +- NumericLiteral[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "_", @TypeMirror = "int"] + | | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables.sideEffect() -> int", @MethodName = "sideEffect", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | | +- ArgumentList[] + | | +- InfixExpression[@TypeMirror = "boolean"] + | | | +- VariableAccess[@Name = "i", @TypeMirror = "int"] + | | | +- NumericLiteral[@TypeMirror = "int"] + | | +- ForUpdate[] + | | | +- StatementExpressionList[] + | | | +- UnaryExpression[@TypeMirror = "int"] + | | | +- VariableAccess[@Name = "i", @TypeMirror = "int"] + | | +- Block[] + | | +- ExpressionStatement[] + | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(int) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | | +- ClassType[@TypeMirror = "java.lang.System"] + | | +- ArgumentList[] + | | +- VariableAccess[@Name = "i", @TypeMirror = "int"] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "java.util.Queue"] + | | | +- TypeArguments[] + | | | +- ClassType[@TypeMirror = "java.lang.Integer"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "q", @TypeMirror = "java.util.Queue"] + | | +- ConstructorCall[@Failed = false, @Function = "java.util.ArrayDeque.new() -> java.util.ArrayDeque", @MethodName = "new", @TypeMirror = "java.util.ArrayDeque", @Unchecked = false, @VarargsCall = false] + | | +- ClassType[@TypeMirror = "java.util.ArrayDeque"] + | | | +- TypeArguments[] + | | +- ArgumentList[] + | +- WhileStatement[] + | | +- InfixExpression[@TypeMirror = "boolean"] + | | | +- MethodCall[@Failed = false, @Function = "java.util.Collection.size() -> int", @MethodName = "size", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | | | +- ArgumentList[] + | | | +- NumericLiteral[@TypeMirror = "int"] + | | +- Block[] + | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "x", @TypeMirror = "int"] + | | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | | +- ArgumentList[] + | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "y", @TypeMirror = "int"] + | | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | | +- ArgumentList[] + | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "_", @TypeMirror = "int"] + | | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | | +- ArgumentList[] + | | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Point.new(int, int) -> Jep456_UnamedPatternsAndVariables$Point", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point", @Unchecked = false, @VarargsCall = false] + | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | | +- ArgumentList[] + | | +- VariableAccess[@Name = "x", @TypeMirror = "int"] + | | +- VariableAccess[@Name = "y", @TypeMirror = "int"] + | +- WhileStatement[] + | +- InfixExpression[@TypeMirror = "boolean"] + | | +- MethodCall[@Failed = false, @Function = "java.util.Collection.size() -> int", @MethodName = "size", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | | +- ArgumentList[] + | | +- NumericLiteral[@TypeMirror = "int"] + | +- Block[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "x", @TypeMirror = "java.lang.Integer"] + | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | +- ArgumentList[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "_", @TypeMirror = "java.lang.Integer"] + | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | +- ArgumentList[] + | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "_", @TypeMirror = "java.lang.Integer"] + | | +- MethodCall[@Failed = false, @Function = "java.util.Queue.remove() -> java.lang.Integer", @MethodName = "remove", @TypeMirror = "java.lang.Integer", @Unchecked = false, @VarargsCall = false] + | | +- VariableAccess[@Name = "q", @TypeMirror = "java.util.Queue"] + | | +- ArgumentList[] + | +- LocalVariableDeclaration[] + | +- ModifierList[] + | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | +- VariableDeclarator[] + | +- VariableId[@Name = "p", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$Point.new(int, int) -> Jep456_UnamedPatternsAndVariables$Point", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$Point", @Unchecked = false, @VarargsCall = false] + | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$Point"] + | +- ArgumentList[] + | +- VariableAccess[@Name = "x", @TypeMirror = "java.lang.Integer"] + | +- NumericLiteral[@TypeMirror = "int"] + +- ClassDeclaration[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | +- ModifierList[] + | +- ImplementsList[] + | | +- ClassType[@TypeMirror = "java.lang.AutoCloseable"] + | +- ClassBody[] + | +- MethodDeclaration[@Name = "close"] + | | +- ModifierList[] + | | | +- Annotation[@TypeMirror = "java.lang.Override"] + | | | +- ClassType[@TypeMirror = "java.lang.Override"] + | | +- VoidType[@TypeMirror = "void"] + | | +- FormalParameters[] + | | +- Block[] + | +- MethodDeclaration[@Name = "acquire"] + | +- ModifierList[] + | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | +- FormalParameters[] + | +- Block[] + | +- ReturnStatement[] + | +- ConstructorCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$ScopedContext.new() -> Jep456_UnamedPatternsAndVariables$ScopedContext", @MethodName = "new", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext", @Unchecked = false, @VarargsCall = false] + | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | +- ArgumentList[] + +- MethodDeclaration[@Name = "unusedVariables2"] + +- ModifierList[] + +- VoidType[@TypeMirror = "void"] + +- FormalParameters[] + +- Block[] + +- TryStatement[] + | +- ResourceList[] + | | +- Resource[] + | | +- LocalVariableDeclaration[] + | | +- ModifierList[] + | | +- VariableDeclarator[] + | | +- VariableId[@Name = "_", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | | +- MethodCall[@Failed = false, @Function = "Jep456_UnamedPatternsAndVariables$ScopedContext.acquire() -> Jep456_UnamedPatternsAndVariables$ScopedContext", @MethodName = "acquire", @TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext", @Unchecked = false, @VarargsCall = false] + | | +- TypeExpression[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | | | +- ClassType[@TypeMirror = "Jep456_UnamedPatternsAndVariables$ScopedContext"] + | | +- ArgumentList[] + | +- Block[] + +- LocalVariableDeclaration[] + | +- ModifierList[] + | +- ClassType[@TypeMirror = "java.lang.String"] + | +- VariableDeclarator[] + | +- VariableId[@Name = "s", @TypeMirror = "java.lang.String"] + | +- StringLiteral[@TypeMirror = "java.lang.String"] + +- TryStatement[] + | +- Block[] + | | +- LocalVariableDeclaration[] + | | | +- ModifierList[] + | | | +- PrimitiveType[@TypeMirror = "int"] + | | | +- VariableDeclarator[] + | | | +- VariableId[@Name = "i", @TypeMirror = "int"] + | | | +- MethodCall[@Failed = false, @Function = "java.lang.Integer.parseInt(java.lang.String) -> int", @MethodName = "parseInt", @TypeMirror = "int", @Unchecked = false, @VarargsCall = false] + | | | +- TypeExpression[@TypeMirror = "java.lang.Integer"] + | | | | +- ClassType[@TypeMirror = "java.lang.Integer"] + | | | +- ArgumentList[] + | | | +- VariableAccess[@Name = "s", @TypeMirror = "java.lang.String"] + | | +- ExpressionStatement[] + | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(int) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | | +- ClassType[@TypeMirror = "java.lang.System"] + | | +- ArgumentList[] + | | +- VariableAccess[@Name = "i", @TypeMirror = "int"] + | +- CatchClause[] + | | +- CatchParameter[] + | | | +- ModifierList[] + | | | +- ClassType[@TypeMirror = "java.lang.NumberFormatException"] + | | | +- VariableId[@Name = "_", @TypeMirror = "java.lang.NumberFormatException"] + | | +- Block[] + | | +- ExpressionStatement[] + | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.String) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | | +- ClassType[@TypeMirror = "java.lang.System"] + | | +- ArgumentList[] + | | +- InfixExpression[@TypeMirror = "java.lang.String"] + | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | | +- VariableAccess[@Name = "s", @TypeMirror = "java.lang.String"] + | +- CatchClause[] + | +- CatchParameter[] + | | +- ModifierList[] + | | +- ClassType[@TypeMirror = "java.lang.Exception"] + | | +- VariableId[@Name = "_", @TypeMirror = "java.lang.Exception"] + | +- Block[] + | +- ExpressionStatement[] + | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.String) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false] + | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"] + | | +- TypeExpression[@TypeMirror = "java.lang.System"] + | | +- ClassType[@TypeMirror = "java.lang.System"] + | +- ArgumentList[] + | +- StringLiteral[@TypeMirror = "java.lang.String"] + +- ExpressionStatement[] + +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream. collect(java.util.stream.Collector>) -> java.util.Map", @MethodName = "collect", @TypeMirror = "java.util.Map", @Unchecked = false, @VarargsCall = false] + +- MethodCall[@Failed = false, @Function = "java.util.Collection.stream() -> java.util.stream.Stream", @MethodName = "stream", @TypeMirror = "java.util.stream.Stream", @Unchecked = false, @VarargsCall = false] + | +- MethodCall[@Failed = false, @Function = "java.util.List. of(java.lang.String, java.lang.String) -> java.util.List", @MethodName = "of", @TypeMirror = "java.util.List", @Unchecked = false, @VarargsCall = false] + | | +- TypeExpression[@TypeMirror = "java.util.List"] + | | | +- ClassType[@TypeMirror = "java.util.List"] + | | +- ArgumentList[] + | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | | +- StringLiteral[@TypeMirror = "java.lang.String"] + | +- ArgumentList[] + +- ArgumentList[] + +- MethodCall[@Failed = false, @Function = "java.util.stream.Collectors. toMap(java.util.function.Function, java.util.function.Function) -> java.util.stream.Collector>", @MethodName = "toMap", @TypeMirror = "java.util.stream.Collector>", @Unchecked = false, @VarargsCall = false] + +- TypeExpression[@TypeMirror = "java.util.stream.Collectors"] + | +- ClassType[@TypeMirror = "java.util.stream.Collectors"] + +- ArgumentList[] + +- MethodReference[@TypeMirror = "java.util.function.Function"] + | +- TypeExpression[@TypeMirror = "java.lang.String"] + | +- ClassType[@TypeMirror = "java.lang.String"] + +- LambdaExpression[@TypeMirror = "java.util.function.Function"] + +- LambdaParameterList[] + | +- LambdaParameter[@TypeMirror = "java.lang.String"] + | +- ModifierList[] + | +- VariableId[@Name = "_", @TypeMirror = "java.lang.String"] + +- StringLiteral[@TypeMirror = "java.lang.String"] diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 4fa7fda466..2fef18a0b4 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 68d2a90afb..a4452b174e 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-julia/pom.xml b/pmd-julia/pom.xml index bf449e6a41..72bb59e026 100644 --- a/pmd-julia/pom.xml +++ b/pmd-julia/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index 47bb2e54d2..0a7cf896ef 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index a4a634f09c..fe39bb602d 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -12,7 +12,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-languages-deps/pom.xml b/pmd-languages-deps/pom.xml index 501f61a841..358ecf6234 100644 --- a/pmd-languages-deps/pom.xml +++ b/pmd-languages-deps/pom.xml @@ -4,7 +4,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT pmd-languages-deps diff --git a/pmd-lua/pom.xml b/pmd-lua/pom.xml index 23834ba393..e1c07de633 100644 --- a/pmd-lua/pom.xml +++ b/pmd-lua/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 4b4e840b74..4cc4232138 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-modelica/pom.xml b/pmd-modelica/pom.xml index 5ee4d15a7a..83e9f19c5e 100644 --- a/pmd-modelica/pom.xml +++ b/pmd-modelica/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 3dab6880c0..d45fab3959 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index e2a21f93ac..b2d709d222 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 1d98f18865..4aa8fffef4 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 7.3.0-SNAPSHOT + 7.4.0-SNAPSHOT ../pom.xml diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index 93588f9043..0298a75009 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -33,6 +33,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *==================================================================== * Add support for MERGE (INTO) statement * Add support Error Logging for INSERT, UPDATE, DELETE + * Add support for exception handlers in compound triggers, + * Allow multiple exception handlers. Parse DeclarativeSection + * correctly in compound triggers. * * Andreas Dangel 06/2024 *==================================================================== @@ -172,6 +175,11 @@ import net.sourceforge.pmd.lang.plsql.ast.internal.ParsingExclusion; import java.util.ArrayList; import java.util.List; +/** + * @deprecated PLSQLParserImpl should have been package private because this is an implementation class + * that should not be used directly. + */ +@Deprecated public class PLSQLParserImpl { /** @@ -225,7 +233,10 @@ public class PLSQLParserImpl { * Usage: LOOKAHEAD({isKeyword("WAIT")}) KEYWORD("WAIT") */ private boolean isKeyword(String keyword) { - return getToken(1).kind == IDENTIFIER && getToken(1).getImage().equalsIgnoreCase(keyword); + return getToken(1).kind == IDENTIFIER + && getToken(1).getImage().equalsIgnoreCase(keyword) + // quoted identifiers are excluded + && getToken(1).getText().charAt(0) != '"'; } } @@ -466,13 +477,13 @@ ASTBlock Block() : { // Included as part of statement() [ - - DeclarativeSection() + + [ DeclarativeSection() ] ] (Statement())* - (ExceptionHandler())? + [ (ExceptionHandler())+] [ ] // Optional END Identifier has to match the label { return jjtThis ; } } @@ -501,7 +512,7 @@ ASTPackageSpecification PackageSpecification() : ( ( | ) - DeclarativeSection() + [ DeclarativeSection() ] [ID()] ";" ) @@ -527,10 +538,10 @@ ASTPackageBody PackageBody() : ( ( | ) - DeclarativeSection() //SRT 20110524 Allow PLDOc in Type Bodies + [ DeclarativeSection() ] //SRT 20110524 Allow PLDOc in Type Bodies - [ (Statement())* (ExceptionHandler())? ] [ID()] ";" + [ (Statement())* [ (ExceptionHandler())+] ] [ID()] ";" ) ) @@ -538,36 +549,40 @@ ASTPackageBody PackageBody() : { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/block.html#GUID-9ACEB9ED-567E-4E1A-A16A-B8B35214FC9D__CJAIABJJ + */ ASTDeclarativeUnit DeclarativeUnit() : {} { ( - Pragma() | - LOOKAHEAD(2) - ExceptionDeclaration() | - LOOKAHEAD((|) QualifiedID() ( | ) ) //SRT 20110616 - make sue soen't break object type - SubTypeDefinition() | - LOOKAHEAD((|) QualifiedID() ) //SRT 20111117 - Special case of parameterless methods:choose method in preference to variable - ProgramUnit() | - LOOKAHEAD(4) - VariableOrConstantDeclaration() | - CursorSpecification() | - CollectionDeclaration() | - //ProgramUnit() - //|TypeMethod() - MethodDeclaration() - |CompilationDeclarationFragment() + Pragma() | + LOOKAHEAD(2) ExceptionDeclaration() | + LOOKAHEAD((|) QualifiedID() ( | ) ) SubTypeDefinition() | + //SRT 20111117 - Special case of parameterless methods:choose method in preference to variable + LOOKAHEAD((|) QualifiedID() ) ProgramUnit() | + LOOKAHEAD(4) VariableOrConstantDeclaration() | + CursorSpecification() | + CollectionDeclaration() | + //ProgramUnit()| + //TypeMethod()| + MethodDeclaration() | + CompilationDeclarationFragment() ) - { return jjtThis ; } + { return jjtThis; } } -ASTDeclarativeSection DeclarativeSection() : +/** + * "Declare Section" + * + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/block.html#GUID-9ACEB9ED-567E-4E1A-A16A-B8B35214FC9D__CJAIABJJ + */ +ASTDeclarativeSection DeclarativeSection() : {} { - ( - DeclarativeUnit() - )* - { return jjtThis ; } + DeclarativeUnit() + ( LOOKAHEAD(3) DeclarativeUnit() )* + { return jjtThis; } } ASTCompilationDeclarationFragment CompilationDeclarationFragment() : @@ -650,10 +665,10 @@ ASTProgramUnit ProgramUnit() : CallSpecTail() //{ System.err.println("Found CallSpecTail") ; } | ( - DeclarativeSection() + [ DeclarativeSection() ] [Pragma()] // See PMD Bug #1527 - (Statement())* (ExceptionHandler())? [ID()] + (Statement())* [ (ExceptionHandler())+] [ID()] ) ) ] @@ -1069,15 +1084,18 @@ ASTDateTimeLiteral DateTimeLiteral() : } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/exception-handler.html#GUID-3FECF29B-A240-4191-A635-92C612D00C4D__CJAEIGAB + */ ASTExceptionHandler ExceptionHandler() : {} { - ( - - ( LOOKAHEAD(2) QualifiedName() ( QualifiedName())* (Statement())+ )* - [ (Statement())+ ] - ) - { return jjtThis ; } + + ( + QualifiedName() ( QualifiedName())* + ) + (Statement())+ + { return jjtThis; } } void Skip2NextTerminator(String initiator,String terminator) : @@ -1709,18 +1727,18 @@ ASTSimpleExpression SimpleExpression() : LOOKAHEAD(4) ID() "." ( | ) | LOOKAHEAD(6) - SchemaName() { sb.append(token.getImage()); } "." { sb.append(token.getImage()); } - TableName() { sb.append(token.getImage()); } "." { sb.append(token.getImage()); } - ( "*" | Column() ) { sb.append(token.getImage()); } + SchemaName() { sb.append(token.getText()); } "." { sb.append(token.getImage()); } + TableName() { sb.append(token.getText()); } "." { sb.append(token.getImage()); } + ( "*" | Column() ) { sb.append(token.getText()); } | LOOKAHEAD(4) - TableName() { sb.append(token.getImage()); } "." { sb.append(token.getImage()); } - ( "*" | Column() ) { sb.append(token.getImage()); } + TableName() { sb.append(token.getText()); } "." { sb.append(token.getImage()); } + ( "*" | Column() ) { sb.append(token.getText()); } | // Named Cursor: https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/named-cursor-attribute.html#GUID-CD8D8415-FF19-4D81-99BA-7825FD40CC96 // Implicit Cursor: https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/implicit-cursor-attribute.html#GUID-5A938EE7-E8D2-468C-B60F-81898F110BE1 LOOKAHEAD(3) - Column() { sb.append(token.getImage()); } "%" ( LOOKAHEAD({isKeyword("isopen")}) KEYWORD("ISOPEN") + Column() { sb.append(token.getText()); } "%" ( LOOKAHEAD({isKeyword("isopen")}) KEYWORD("ISOPEN") | LOOKAHEAD({isKeyword("found")}) KEYWORD("FOUND") | LOOKAHEAD({isKeyword("notfound")}) KEYWORD("NOTFOUND") | LOOKAHEAD({isKeyword("rowcount")}) KEYWORD("ROWCOUNT") @@ -1729,7 +1747,7 @@ ASTSimpleExpression SimpleExpression() : ) { sb.append('%').append(token.getImage()); } | LOOKAHEAD(2) - ( "*" | Column() ) { sb.append(token.getImage()); } + ( "*" | Column() ) { sb.append(token.getText()); } ) { jjtThis.setImage(sb.toString()); @@ -1738,11 +1756,11 @@ ASTSimpleExpression SimpleExpression() : } ASTOuterJoinExpression OuterJoinExpression() : -{ StringBuilder sb = new StringBuilder(); } +{ StringBuilder sb = new StringBuilder(); PLSQLNode node; } { - [ LOOKAHEAD(6) SchemaName() { sb.append(token.getImage()); } "." { sb.append(token.getImage()); } ] - [ LOOKAHEAD(4) TableName() { sb.append(token.getImage()); } "." { sb.append(token.getImage()); } ] - Column() { sb.append(token.getImage()); } + [ LOOKAHEAD(6) node=SchemaName() { sb.append(node.getImage()); } "." { sb.append(token.getImage()); } ] + [ LOOKAHEAD(4) node=TableName() { sb.append(node.getImage()); } "." { sb.append(token.getImage()); } ] + node=Column() { sb.append(node.getImage()); } "(" "+" ")" { @@ -2502,7 +2520,7 @@ ASTInsertIntoClause InsertIntoClause() : {} { DMLTableExpressionClause() - [ LOOKAHEAD(2) TableAlias() ] + [ LOOKAHEAD(2, {!getToken(1).getImage().equalsIgnoreCase("LOG")}) TableAlias() ] [ LOOKAHEAD(2) "(" [ LOOKAHEAD(2) TableName() "." ] Column() @@ -2527,18 +2545,19 @@ ASTMultiTableInsert MultiTableInsert() : {} { ( - LOOKAHEAD(2) ( InsertIntoClause() [ ValuesClause() ] )+ Subquery() + LOOKAHEAD(2) ( InsertIntoClause() [ ValuesClause() ] [ ErrorLoggingClause() ] )+ | ConditionalInsertClause() ) + Subquery() { return jjtThis; } } ASTConditionalInsertClause ConditionalInsertClause() : {} { - [ | KEYWORD("FIRST") ] ( Condition() ( InsertIntoClause() [ ValuesClause() ] )+ )+ - [ ( InsertIntoClause() [ ValuesClause() ] )+ ] + [ | KEYWORD("FIRST") ] ( Condition() ( InsertIntoClause() [ ValuesClause() ] [ ErrorLoggingClause() ] )+ )+ + [ ( InsertIntoClause() [ ValuesClause() ] [ ErrorLoggingClause() ] )+ ] { return jjtThis; } } @@ -2680,7 +2699,7 @@ ASTDeleteStatement DeleteStatement() : {} { [ ] - ( TableReference() | "(" TableReference() ")" ) + ( LOOKAHEAD(2) "(" TableReference() ")" | TableReference() ) [ WhereClause() ] [ ReturningClause() ] [ ErrorLoggingClause() ] @@ -2926,6 +2945,9 @@ ASTConditionalCompilationStatement ConditionalCompilationStatement() : { return jjtThis ; } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/block.html#GUID-9ACEB9ED-567E-4E1A-A16A-B8B35214FC9D__CJACIHEC + */ ASTSubTypeDefinition SubTypeDefinition() : { Token start, subtype_name=null, constraint=null, base_type=null; @@ -3138,6 +3160,28 @@ ASTExpression Expression() : } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/implicit-cursor-attribute.html + */ +ASTImplicitCursorAttribute ImplicitCursorAttribute() : +{} +{ + "%" + ( + LOOKAHEAD({isKeyword("isopen")}) KEYWORD("ISOPEN") + | LOOKAHEAD({isKeyword("found")}) KEYWORD("FOUND") + | LOOKAHEAD({isKeyword("notfound")}) KEYWORD("NOTFOUND") + | LOOKAHEAD({isKeyword("rowcount")}) KEYWORD("ROWCOUNT") + | LOOKAHEAD({isKeyword("bulk_rowcount")}) KEYWORD("BULK_ROWCOUNT") "(" PrimaryExpression() ")" + | LOOKAHEAD({isKeyword("bulk_exceptions")}) KEYWORD("BULK_EXCEPTIONS") + ( "." "COUNT" + | "(" PrimaryExpression() ")" "." (LOOKAHEAD({isKeyword("error_index")}) KEYWORD("ERROR_INDEX") | LOOKAHEAD({isKeyword("error_code")}) KEYWORD("ERROR_CODE")) + ) + ) + + { return jjtThis; } +} + ASTCompilationExpression CompilationExpression() : { PLSQLNode simpleNode = null; @@ -3499,7 +3543,10 @@ ASTUnaryExpressionNotPlusMinus UnaryExpressionNotPlusMinus() #UnaryExpressionNot LOOKAHEAD(2) simpleNode = ExtractExpression() | LOOKAHEAD(2) simpleNode = IsNullCondition() - ) {sb.append(simpleNode.getImage()); } + | + simpleNode = ImplicitCursorAttribute() + ) + {sb.append(simpleNode.getImage()); } { jjtThis.setImage(sb.toString()); return jjtThis; } @@ -4164,8 +4211,8 @@ ASTTypeMethod TypeMethod() : [ ";" ] // This only exists in the type body | // SRT 20110524 Not really a Declaration any more ... ( - DeclarativeSection() - (Statement())* (ExceptionHandler())? [ID()] + [ DeclarativeSection() ] + (Statement())* [ (ExceptionHandler())+] [ID()] ";" // This only exists in the type body ) ) @@ -4437,9 +4484,9 @@ ASTTypeBody TypeBody() : ( ( | ) - DeclarativeSection() //SRT 20110524 Allow PLDOc in Type Bodies + [ DeclarativeSection() ] //SRT 20110524 Allow PLDOc in Type Bodies - [ (Statement())* (ExceptionHandler())? ] [ID()] ";" + [ (Statement())* [ (ExceptionHandler())+] ] [ID()] ";" ) ) } @@ -4596,7 +4643,7 @@ ASTTriggerUnit TriggerUnit() : // referencing_clause - there may be ZERO subclauses - [ (( | | ) ID())*] + [LOOKAHEAD({isKeyword("REFERENCING")}) KEYWORD("REFERENCING") (( | | ) ID())*] [] // end of simple_dml_trigger (incorporating compound_dml_trigger ) @@ -4621,20 +4668,24 @@ ASTTriggerUnit TriggerUnit() : { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } - +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B__GUID-2CD49225-7507-458B-8BDF-21C56AFC3527 + * + * Note: The DeclarativeSection (declare_section) before BEGIN is not documented, but might be valid. See #4270 + */ ASTTriggerTimingPointSection TriggerTimingPointSection() : { StringBuilder sb = new StringBuilder(); } { - ( ( | | ) { sb.append(token.getImage()) ; } ( | ) {sb.append(" "); sb.append(token.getImage()) ; } + [ DeclarativeSection() ] (Statement())+ + [ (ExceptionHandler())+ ] ( | | ) ( | ) ";" - ) { //Add a TRIGGER ENTRY for each timing point section } @@ -4642,36 +4693,19 @@ ASTTriggerTimingPointSection TriggerTimingPointSection() : } - +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B__CJACFCDJ +*/ ASTCompoundTriggerBlock CompoundTriggerBlock() : +{} { -} -{ - + - ( - //Problems making the declaration optional - //DeclarativeSection() - //(TriggerTimingPointSection())+ - ( - TriggerTimingPointSection()| - Pragma() | - LOOKAHEAD(2) - ExceptionDeclaration() | - LOOKAHEAD(2) - SubTypeDefinition() | - LOOKAHEAD(4) - VariableOrConstantDeclaration() | - CursorSpecification() | - CollectionDeclaration() | - ProgramUnit() - )* + [ DeclarativeSection() ] + (TriggerTimingPointSection())+ - - ) - - [ID()] ";" - { return jjtThis ; } + [ID()] ";" + { return jjtThis; } } @@ -5156,7 +5190,6 @@ TOKEN [IGNORE_CASE]: | | | - | | | | @@ -5206,7 +5239,6 @@ TOKEN [IGNORE_CASE]: | | | - | | | | @@ -5306,24 +5338,18 @@ TOKEN : "\\" > | +< #ID_SIMPLE: ("$" | ":" | ) ( | | "$" | "_" | "#" )* > +| < IDENTIFIER: - ( ("$" | ":" | ) ( | | "$" | "_" | "#" )* ) // 2006-05-17 - Matthias Hendler - Bind variablen werden nun als Identifier akzeptiert. - //SRT Does NOT seem to like identifiers 2 or fewer characters( ( ) ) - //( ( ) ) - //( ( "$" ) ) - //( ( "_" ) ) - //( ( "#" ) ) + | - ( - ( | | "$" | "_" | "#" )* - ) - | - ( ( | "$" ) ( | | "$" | "_" | "#" )* ) - | - ( "\"" ( | | "$" | "_" | "#" )* "\"" ) + ( ( | | "$" | "_" | "#" )* ) + | + // todo separate quoted identifier into other token + ( "\"" "\"" ) > | -< LEXICAL_PARAMETER: +< #LEXICAL_PARAMETER: ( ("&&" | "&") ( @@ -5347,12 +5373,6 @@ TOKEN : | < QUOTED_LITERAL: "\"" (<_WHATEVER_CHARACTER_WO_QUOTE> | | "\\\"")* "\"" > | -< SQLDATA_CLASS: "SQLData" > -| -< CUSTOMDATUM_CLASS: "CustomDatum" > -| -< ORADATA_CLASS: "OraData" > -| < JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) > //| //< #BOOLEAN_LITERAL: "TRUE" | "FALSE" > @@ -5386,8 +5406,8 @@ TOKEN : | (["q","Q"]) "'<" (~[">"] | ">" ~["'"] )* ">" | (["q","Q"]) "'(" (~[")"] | ")" ~["'"] )* ")" > - | <(["n","N"])? "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | | "''")*> : IN_STRING_LITERAL_TOKENIZE - | <(["n","N"])? <_ALTERNATIVE_QUOTING_STRING_LITERAL>> : IN_STRING_LITERAL_TOKENIZE + | <(["n","N"])? "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | | "''")*> { input_stream.backup(1); } : IN_STRING_LITERAL_TOKENIZE + | <(["n","N"])? <_ALTERNATIVE_QUOTING_STRING_LITERAL>> { input_stream.backup(1); } : IN_STRING_LITERAL_TOKENIZE // special handling for custom quote delimiters | <(["n","N"])? (["q","Q"]) "'" (~[" ", "\t", "\r", "\n", "[", "{", "<", "("])> : IN_STRING_LITERAL @@ -5402,21 +5422,24 @@ TOKEN : } int beforeQuote = image.charAt(image.length() - 2); if (quoteDelimiter == beforeQuote) { - input_stream.backup(1); + input_stream.backup(2); SwitchTo(IN_STRING_LITERAL_TOKENIZE); } } } - TOKEN : { : DEFAULT } + TOKEN : { + : DEFAULT +} /** * PL/SQL Reserved words * * https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-reserved-words-keywords.html + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/plsql-language-fundamentals.html#GUID-53E09662-5AD4-4530-8C6B-FF3F7C7430D5 * * Note: This production is not used, it is just here for reference of collecting all reserved words. - * Reserved words cannot be used a identifiers. + * Reserved words _cannot_ be used a identifiers. */ void RESERVED_WORD() #void: {} { @@ -5521,10 +5544,19 @@ void KEYWORD(String id) #void: } -ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} +/** + * PL/SQL Keywords. They can be used as ordinary identifiers, but it is not recommended. + * + * https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/plsql-language-fundamentals.html#GUID-53E09662-5AD4-4530-8C6B-FF3F7C7430D5 + * + * @deprecated This is only used to generate a node class + */ +// @Deprecated +ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {}{KEYWORD_NOT_RESERVED() {return jjtThis;}} + +private void KEYWORD_NOT_RESERVED () #void: {} { // PL/SQL UNRESERVED KEYWORDS - V$RESERVED.RESERVED='N' -( "REF" | "LAST" | "TRIM" | "OVER" | "UNBOUNDED" | "PRECEDING" | "FOLLOWING" | "WITHIN" | "OVERFLOW" | "ERROR" | "WITHOUT" | "COUNT" | "SUBPARTITION" | "LOG" | "ERRORS" | "REJECT" | "UNLIMITED" | @@ -5556,7 +5588,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| //| -//| test_unreserved_keyword.pks +| | | | @@ -5576,7 +5608,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| //| test_unreserved_keyword.pks //| | //-test_unreserved_keyword.pks @@ -5597,7 +5628,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| | //| //| @@ -5618,7 +5649,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| | //| //| @@ -5690,7 +5720,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -| //| //| //| @@ -5710,7 +5739,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| //| //| | @@ -5779,7 +5807,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| //| -//| +| //| //| //| @@ -5854,7 +5882,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| //| //| //| @@ -5862,7 +5889,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| //| @@ -5902,7 +5929,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| //| -//| +| //| //| //| @@ -6156,13 +6183,13 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| //| -//| +| | //| //| //| | -//| +| //| //| //| @@ -6241,7 +6268,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| | -//| +| //| //| //| @@ -6287,7 +6314,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| //| | //| @@ -6399,7 +6425,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| //| //| //| @@ -6416,7 +6441,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| | //| @@ -6445,7 +6470,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| //| //| //| @@ -6585,9 +6609,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //Although RENAME is an Oracle reserved word, it may be used as a PL/SQL name. | //Although RELEASE is an Oracle reserved word, it may be used as a PL/SQL name. | // PRAGMA INLINE is not a PLSQL reserved word -) - -{ jjtThis.setImage(token.getImage()) ; jjtThis.value = token ; return jjtThis ; } } //SRT 2011-04-17 - END */ @@ -6598,7 +6619,7 @@ ASTID ID(): {} { ( | - | KEYWORD_UNRESERVED() //SRT 2011-04-17 + | KEYWORD_NOT_RESERVED() //SRT 2011-04-17 /*KEYWORDS_UNRESERVED | | | | | | | | | | | | @@ -6610,18 +6631,15 @@ ASTID ID(): {} | //SYNTAX //RESERVED WORD | //SYNTAX //RESERVED WORD | //RESERVED WORD - | | //RESERVED WORD //20120429 | | //| // | | - | //SYNTAX | //201020430 | //| //RESERVED WORD //201020430 | - | //SYNTAX | //RESERVED WPRDS | | //RESERVED WPRDS @@ -6635,7 +6653,6 @@ ASTID ID(): {} //20120501 | | | //RESERVED WORD - | //SYNTAX | //RESERVED WORD | //RESERVED WORD | //RESERVED WORD @@ -6647,7 +6664,6 @@ ASTID ID(): {} //| | //| //SYNTAX | //SYNTAX //RESERVED WORD - | //SYNTAX //| //SYNTAX //RESERVED WORD //| //SYNTAX //20120501 | @@ -6660,7 +6676,6 @@ ASTID ID(): {} | //SYNTAX | //RESERVED WORD //20120501 | - | //SYNTAX //20120501 | | //RESERVED WORD //20120501 | @@ -6697,7 +6712,7 @@ ASTID ID(): {} | //20120501 | - | |