diff --git a/.all-contributorsrc b/.all-contributorsrc index 908c5cc8cb..e70e090519 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -492,7 +492,8 @@ "profile": "https://github.com/pzygielo", "contributions": [ "code", - "bug" + "bug", + "doc" ] }, { @@ -3269,7 +3270,8 @@ "profile": "https://github.com/jborgers", "contributions": [ "bug", - "code" + "code", + "talk" ] }, { @@ -6522,7 +6524,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/36415196?v=4", "profile": "https://github.com/dykov", "contributions": [ - "code" + "code", + "bug" ] }, { @@ -6540,7 +6543,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/178883?v=4", "profile": "https://github.com/gredler", "contributions": [ - "code" + "code", + "bug" ] }, { @@ -6559,7 +6563,8 @@ "profile": "https://github.com/JerritEic", "contributions": [ "code", - "doc" + "doc", + "bug" ] }, { @@ -6777,7 +6782,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/90252673?v=4", "profile": "https://github.com/abyss638", "contributions": [ - "code" + "code", + "bug" ] }, { @@ -6798,7 +6804,8 @@ "contributions": [ "doc" ] - },{ + }, + { "login": "pacvz", "name": "pacvz", "avatar_url": "https://avatars.githubusercontent.com/u/35453365?v=4", @@ -6806,6 +6813,134 @@ "contributions": [ "code" ] + }, + { + "login": "mohan-chinnappan-n", + "name": "mohan-chinnappan-n", + "avatar_url": "https://avatars.githubusercontent.com/u/5963194?v=4", + "profile": "https://mohan-chinnappan-n.github.io/about/cv.html", + "contributions": [ + "code" + ] + }, + { + "login": "Suvashri", + "name": "Suvashri", + "avatar_url": "https://avatars.githubusercontent.com/u/112872981?v=4", + "profile": "https://github.com/Suvashri", + "contributions": [ + "doc" + ] + }, + { + "login": "osiegmar", + "name": "Oliver Siegmar", + "avatar_url": "https://avatars.githubusercontent.com/u/1918869?v=4", + "profile": "https://github.com/osiegmar", + "contributions": [ + "financial" + ] + }, + { + "login": "OlegAndreych", + "name": "Oleg Andreych", + "avatar_url": "https://avatars.githubusercontent.com/u/2041351?v=4", + "profile": "https://github.com/OlegAndreych", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "lfalcantar", + "name": "Luis Alcantar", + "avatar_url": "https://avatars.githubusercontent.com/u/13026131?v=4", + "profile": "https://github.com/lfalcantar", + "contributions": [ + "code" + ] + }, + { + "login": "LynnBroe", + "name": "Lynn", + "avatar_url": "https://avatars.githubusercontent.com/u/109954313?v=4", + "profile": "https://github.com/LynnBroe", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "sashashura", + "name": "Alex", + "avatar_url": "https://avatars.githubusercontent.com/u/93376818?v=4", + "profile": "https://github.com/sashashura", + "contributions": [ + "code" + ] + }, + { + "login": "koalalam", + "name": "koalalam", + "avatar_url": "https://avatars.githubusercontent.com/u/5452429?v=4", + "profile": "https://github.com/koalalam", + "contributions": [ + "bug" + ] + }, + { + "login": "garydgregory", + "name": "Gary Gregory", + "avatar_url": "https://avatars.githubusercontent.com/u/1187639?v=4", + "profile": "https://github.com/garydgregory", + "contributions": [ + "bug" + ] + }, + { + "login": "vanguard-1024", + "name": "Austin", + "avatar_url": "https://avatars.githubusercontent.com/u/87691060?v=4", + "profile": "https://github.com/vanguard-1024", + "contributions": [ + "bug" + ] + }, + { + "login": "ewantempero", + "name": "Ewan Tempero", + "avatar_url": "https://avatars.githubusercontent.com/u/8744237?v=4", + "profile": "http://www.cs.auckland.ac.nz/~ewan", + "contributions": [ + "bug" + ] + }, + { + "login": "cbfiddle", + "name": "cbfiddle", + "avatar_url": "https://avatars.githubusercontent.com/u/6628505?v=4", + "profile": "https://github.com/cbfiddle", + "contributions": [ + "bug" + ] + }, + { + "login": "MartGit", + "name": "MartGit", + "avatar_url": "https://avatars.githubusercontent.com/u/1518723?v=4", + "profile": "https://github.com/MartGit", + "contributions": [ + "bug" + ] + }, + { + "login": "Alexx-G", + "name": "Alex", + "avatar_url": "https://avatars.githubusercontent.com/u/3869268?v=4", + "profile": "https://github.com/Alexx-G", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/.ci/build.sh b/.ci/build.sh index 6c6b657654..499cafa39e 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -41,17 +41,20 @@ function build() { ./mvnw clean install --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" pmd_ci_log_group_end - # Danger is executed only on the linux runner - if [ "$(pmd_ci_utils_get_os)" = "linux" ]; then - pmd_ci_log_group_start "Executing danger" - regression_tester_setup_ci - regression_tester_executeDanger - pmd_ci_log_group_end + # Execute danger and dogfood only for pull requests in our own repository + if [[ "${PMD_CI_IS_FORK}" = "false" && -n "${PMD_CI_PULL_REQUEST_NUMBER}" ]]; then + # Danger is executed only on the linux runner + if [ "$(pmd_ci_utils_get_os)" = "linux" ]; then + pmd_ci_log_group_start "Executing danger" + regression_tester_setup_ci + regression_tester_executeDanger + pmd_ci_log_group_end - # also run dogfood for PRs (only on linux) - pmd_ci_log_group_start "Executing PMD dogfood test with ${PMD_CI_MAVEN_PROJECT_VERSION}" - pmd_ci_dogfood - pmd_ci_log_group_end + # also run dogfood for PRs (only on linux) + pmd_ci_log_group_start "Executing PMD dogfood test with ${PMD_CI_MAVEN_PROJECT_VERSION}" + pmd_ci_dogfood + pmd_ci_log_group_end + fi fi exit 0 diff --git a/.ci/files/project-list.xml b/.ci/files/project-list.xml index ce357698bc..dc8eb00bcb 100644 --- a/.ci/files/project-list.xml +++ b/.ci/files/project-list.xml @@ -169,4 +169,12 @@ EOF v2.3.0 samples + + + java-regression-tests + git + https://github.com/pmd/java-regression-tests + main + realpath java-regression-tests-*.jar + diff --git a/.github/ISSUE_TEMPLATE/0rule_violation_false-positive.md b/.github/ISSUE_TEMPLATE/0rule_violation_false-positive.md index e02c12eaa8..8faed7c14a 100644 --- a/.github/ISSUE_TEMPLATE/0rule_violation_false-positive.md +++ b/.github/ISSUE_TEMPLATE/0rule_violation_false-positive.md @@ -19,7 +19,7 @@ Please provide the rule name and a link to the rule documentation: **Code Sample demonstrating the issue:** -``` +```java ``` diff --git a/.github/ISSUE_TEMPLATE/1rule_violation_false-negative.md b/.github/ISSUE_TEMPLATE/1rule_violation_false-negative.md index b9480d2c27..51702e0038 100644 --- a/.github/ISSUE_TEMPLATE/1rule_violation_false-negative.md +++ b/.github/ISSUE_TEMPLATE/1rule_violation_false-negative.md @@ -19,7 +19,7 @@ Please provide the rule name and a link to the rule documentation: **Code Sample demonstrating the issue:** -``` +```java ``` diff --git a/.github/ISSUE_TEMPLATE/2new_rule.md b/.github/ISSUE_TEMPLATE/2new_rule.md index 4e79d37f11..52901a1e22 100644 --- a/.github/ISSUE_TEMPLATE/2new_rule.md +++ b/.github/ISSUE_TEMPLATE/2new_rule.md @@ -17,7 +17,7 @@ assignees: '' **Code Sample:** This should include code, that should be flagged by the rule. If possible, the "correct" code according to this new rule should also be demonstrated. -``` +```java ``` diff --git a/.github/ISSUE_TEMPLATE/4bug_report.md b/.github/ISSUE_TEMPLATE/4bug_report.md index 5c752659ea..0b3ada2739 100644 --- a/.github/ISSUE_TEMPLATE/4bug_report.md +++ b/.github/ISSUE_TEMPLATE/4bug_report.md @@ -25,7 +25,7 @@ A clear and concise description of what the bug is. **Code Sample demonstrating the issue:** -``` +```java ``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f94319eb05..a2e4d53c90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,9 +14,17 @@ on: - cron: '0 4 1 * *' workflow_dispatch: +permissions: + contents: read # to fetch code (actions/checkout) + jobs: build: runs-on: ${{ matrix.os }} + permissions: + # read to fetch code (actions/checkout) + # write to push code to gh-pages, create releases + # note: forked repositories will have maximum read access + contents: write continue-on-error: false strategy: matrix: diff --git a/.github/workflows/git-repo-sync.yml b/.github/workflows/git-repo-sync.yml index 3cfb2e23e9..790922db40 100644 --- a/.github/workflows/git-repo-sync.yml +++ b/.github/workflows/git-repo-sync.yml @@ -9,6 +9,9 @@ on: - '**' workflow_dispatch: +permissions: + contents: read # to fetch code (actions/checkout) + jobs: build: runs-on: ubuntu-latest diff --git a/Gemfile.lock b/Gemfile.lock index 0a16d16858..f47c7c5067 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) claide (1.1.0) claide-plugins (0.9.2) cork @@ -12,7 +12,7 @@ GEM concurrent-ruby (1.1.10) cork (0.3.0) colored2 (~> 3.1) - danger (8.6.1) + danger (9.0.0) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) @@ -23,12 +23,12 @@ GEM kramdown (~> 2.3) kramdown-parser-gfm (~> 1.0) no_proxy_fix - octokit (~> 4.7) + octokit (~> 5.0) terminal-table (>= 1, < 4) differ (0.1.2) et-orbi (1.2.7) tzinfo - faraday (1.10.0) + faraday (1.10.2) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -43,7 +43,7 @@ GEM faraday-em_http (1.0.0) faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) - faraday-http-cache (2.4.0) + faraday-http-cache (2.4.1) faraday (>= 0.8) faraday-httpclient (1.0.1) faraday-multipart (1.0.4) @@ -53,25 +53,26 @@ GEM faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - fugit (1.5.3) + fugit (1.7.1) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) - git (1.11.0) + git (1.12.0) + addressable (~> 2.8) rchardet (~> 1.8) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - liquid (5.3.0) + liquid (5.4.0) logger-colors (1.0.0) mini_portile2 (2.8.0) multipart-post (2.2.3) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.13.7) + nokogiri (1.13.8) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.25.1) + octokit (5.6.1) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) @@ -82,12 +83,12 @@ GEM nokogiri (~> 1.13) rufus-scheduler (~> 3.8) slop (~> 4.6) - public_suffix (4.0.7) + public_suffix (5.0.0) raabro (1.4.0) racc (1.6.0) rchardet (1.8.0) rexml (3.2.5) - rouge (3.29.0) + rouge (4.0.0) ruby2_keywords (0.0.5) rufus-scheduler (3.8.2) fugit (~> 1.1, >= 1.1.6) @@ -100,7 +101,7 @@ GEM unicode-display_width (>= 1.1.1, < 3) tzinfo (2.0.5) concurrent-ruby (~> 1.0) - unicode-display_width (2.2.0) + unicode-display_width (2.3.0) PLATFORMS ruby diff --git a/do-release.sh b/do-release.sh index 3ddb331bbd..99c3c99c4c 100755 --- a/do-release.sh +++ b/do-release.sh @@ -116,7 +116,7 @@ echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --f ) TEMP_RELEASE_NOTES=$(cat docs/pages/release_notes.md) -TEMP_RELEASE_NOTES=${TEMP_RELEASE_NOTES/\{\% endtocmaker \%\}/$STATS$'\n'$'\n'\{\% endtocmaker \%\}$'\n'} +TEMP_RELEASE_NOTES=${TEMP_RELEASE_NOTES/\{\% endtocmaker \%\}/${STATS//\&/\\\&}$'\n'$'\n'\{\% endtocmaker \%\}$'\n'} echo "${TEMP_RELEASE_NOTES}" > docs/pages/release_notes.md echo diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index f85828b039..37ab0dfb1e 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,20 +1,20 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.5.1) + activesupport (6.0.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2, >= 2.2.2) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.11.1) colorator (1.1.0) - commonmarker (0.23.5) + commonmarker (0.23.6) concurrent-ruby (1.1.10) dnsruby (1.61.9) simpleidn (~> 0.1) @@ -25,10 +25,10 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.8.1) - faraday (2.3.0) - faraday-net_http (~> 2.0) + faraday (2.5.2) + faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-net_http (2.0.3) + faraday-net_http (3.0.0) ffi (1.15.5) forwardable-extended (2.6.0) gemoji (3.0.1) @@ -211,8 +211,8 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.16.2) - nokogiri (1.13.7) + minitest (5.16.3) + nokogiri (1.13.8) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.25.1) @@ -222,7 +222,7 @@ GEM forwardable-extended (~> 2.6) public_suffix (4.0.7) racc (1.6.0) - rb-fsevent (0.11.1) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rexml (3.2.5) diff --git a/docs/_config.yml b/docs/_config.yml index a2bce8a5ea..ad7e622526 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,9 @@ repository: pmd/pmd pmd: - version: 6.50.0-SNAPSHOT - previous_version: 6.49.0 - date: 30-September-2022 + version: 6.51.0-SNAPSHOT + previous_version: 6.50.0 + date: 29-October-2022 release_type: minor # release types: major, minor, bugfix diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index f7ab16ddb0..b0d165fb9a 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -58,6 +58,9 @@ entries: - title: PMD Report formats url: /pmd_userdocs_report_formats.html output: web, pdf + - title: 3rd party rulesets + output: web, pdf + url: /pmd_userdocs_3rdpartyrulesets.html - title: null output: web, pdf subfolders: @@ -469,6 +472,9 @@ entries: - title: Old release notes url: /pmd_release_notes_old.html output: web, pdf + - title: Decisions + url: /pmd_projectdocs_decisions.html + output: web, pdf - title: null output: web, pdf subfolders: diff --git a/docs/index.md b/docs/index.md index 6066c103a8..46df788333 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ summary: > Welcome to the documentation site for PMD and CPD!

-last_updated: August 2017 +last_updated: October 2022 author: Jeff Jensen , Andreas Dangel , ClΓ©ment Fournier --- @@ -29,7 +29,7 @@ author: Jeff Jensen , Andreas Dangel
Alan Buttars

πŸ›
Alan Hohn

πŸ›
Alberto FernΓ‘ndez

πŸ’» πŸ› +
Alex

πŸ’» +
Alex

πŸ›
Alex Rentz

πŸ›
Alex Saveau

πŸ› -
Alex Shesterov

πŸ’» πŸ› -
Alexey Markevich

πŸ› +
Alex Shesterov

πŸ’» πŸ› +
Alexey Markevich

πŸ›
Alexey Naumov

πŸ›
Alexey Yudichev

πŸ›
Alix

πŸ›
Alix

πŸ›
Amish Shah

πŸ› -
Amit Prasad

πŸ› -
Amitosh Swain Mahapatra

πŸ› +
Amit Prasad

πŸ› +
Amitosh Swain Mahapatra

πŸ›
Anand Subramanian

πŸ’» πŸ›
Anatoly Trosinenko

πŸ’» πŸ›
Andi Pabst

πŸ’» πŸ›
Andrea

πŸ›
Andrea Aime

πŸ› -
Andreas Dangel

πŸ’» πŸ“– πŸ› 🚧 -
Andreas Markussen

πŸ› +
Andreas Dangel

πŸ’» πŸ“– πŸ› 🚧 +
Andreas Markussen

πŸ›
Andreas Schmid

πŸ›
Andreas Turban

πŸ›
Andrei Paikin

πŸ›
Andrew

πŸ›
Andrew Green

πŸ› -
Andrey Fomin

πŸ› -
Andrey Hitrin

πŸ› +
Andrey Fomin

πŸ› +
Andrey Hitrin

πŸ›
Andrey Mochalov

πŸ’» πŸ›
Andro72

πŸ›
Andrwyw

πŸ›
AndrΓ©s CatalΓ‘n

πŸ›
Andy Pattenden

πŸ› -
Andy Ray

πŸ› -
Andy Robinson

πŸ› +
Andy Ray

πŸ› +
Andy Robinson

πŸ›
Andy-2639

πŸ›
Ankush Somani

πŸ›
Anmol Kumar

πŸ›
Anthony Whitford

πŸ›
AnthonyKot

πŸ› -
Aravind Hegde

πŸ› -
Arda Aslan

πŸ› +
Aravind Hegde

πŸ› +
Arda Aslan

πŸ›
Ari Fogel

πŸ›
Arnaud Jeansen

πŸ’» πŸ›
Arpit Koolwal

πŸ›
Artem

πŸ’» πŸ›
Artem

πŸ› -
Artem Sheremet

πŸ› -
Artur

πŸ› +
Artem Sheremet

πŸ› +
Artur

πŸ›
Artur Bosch

πŸ›
Artur Dryomov

πŸ›
Artur Ossowski

πŸ›
AshTheMash

πŸ›
Ashish Rana

πŸ› -
Atul Kaushal

πŸ› -
August Boland

πŸ› +
Atul Kaushal

πŸ› +
August Boland

πŸ›
Aurel Hudec

πŸ› +
Austin

πŸ›
Austin Shalit

πŸ›
Austin Tice

πŸ›
Ayoub Kaanich

πŸ› + +
BBG

πŸ’» πŸ“– πŸ›
Bailey Tjiong

πŸ’»
BarthΓ©lemy L.

πŸ› - -
Basavaraj K N

πŸ›
Basil Peace

πŸ›
Belle

πŸ›
Ben Lerner

πŸ› + +
Ben Manes

πŸ›
Ben McCann

πŸ›
BendegΓΊz Nagy

πŸ› - -
Bennet S Yee

πŸ›
Benoit Lacelle

πŸ›
Bernardo MacΓͺdo

πŸ›
Bernd Farka

πŸ› + +
Betina Cynthia Mamani

πŸ›
Bhanu Prakash Pamidi

πŸ’» πŸ›
Bhargav Thanki

πŸ› - -
Binu R J

πŸ›
BjΓΆrn Kautler

πŸ’» πŸ›
Blightbuster

πŸ›
Bo Zhang

πŸ› + +
Bob "Wombat" Hogg

πŸ›
Bobby Wertman

πŸ›
Bolarinwa Saheed Olayemi

πŸ’» πŸ› - -
Boris Petrov

πŸ›
Brad Kent

πŸ›
Brandon Mikeska

πŸ›
Brian Batronis

πŸ› + +
Brian Johnson

πŸ›
Brice Dutheil

πŸ’» πŸ›
Bruno Ferreira

πŸ› - -
Bruno Ritz

πŸ›
Cameron Donaldson

πŸ›
Carlos Macasaet

πŸ›
Carsten Otto

πŸ› + +
Charlie Housh

πŸ›
Charlie Jonas

πŸ›
Chas Honton

πŸ› - -
Chen Yang

πŸ›
Chotu

πŸ›
Chris Smith

πŸ›
Christian Hujer

πŸ› + +
Christian Pontesegger

πŸ›
ChristianWulf

πŸ›
Christofer Dutz

πŸ’» - -
Christoffer Anselm

πŸ›
Christophe Vidal

πŸ›
Christopher Dancy

πŸ›
Clemens Prill

πŸ› + +
Clint Chester

πŸ’» πŸ›
ClΓ©ment Fournier

πŸ’» πŸ“– πŸ› 🚧
Codacy Badger

πŸ› - -
Code-Nil

πŸ›
ColColonCleaner

πŸ›
Colin Ingarfield

πŸ›
Craig Andrews

πŸ› + +
Craig Muchinsky

πŸ›
Cyril

πŸ’» πŸ›
Dale

πŸ’» - -
Damien Jiang

πŸ›
Dan Berindei

πŸ›
Dan Rollo

πŸ›
Dan Ziemba

πŸ› -
Daniel Gredler

πŸ’» -
Daniel Jipa

πŸ› -
Daniel Paul Searles

πŸ’» +
Daniel Gredler

πŸ’» πŸ› +
Daniel Jipa

πŸ› +
Daniel Paul Searles

πŸ’»
Daniel Reigada

πŸ›
Danilo Pianini

πŸ›
Darko

πŸ›
David

πŸ› + +
David Atkinson

πŸ›
David BurstrΓΆm

πŸ’» πŸ›
David GoatΓ©

πŸ› - -
David Golpira

πŸ›
David KovaΕ™Γ­k

πŸ›
David M. Karr (fullname at gmail.com)

πŸ›
David Renz

πŸ’» πŸ› + +
David Renz

πŸ›
Deleted user

πŸ›
Dell Green

πŸ› - -
Dem Pilafian

πŸ›
Den

πŸ›
Denis Borovikov

πŸ’» πŸ›
Dennie Reniers

πŸ’» πŸ› + +
Dennis Kieselhorst

πŸ›
Derek P. Moore

πŸ›
Dichotomia

πŸ› - -
Dionisio CortΓ©s FernΓ‘ndez

πŸ’» πŸ›
Dmitri Bourlatchkov

πŸ›
Dmitriy Kuzmin

πŸ›
Dmytro Dashenkov

πŸ› + +
Drew Hall

πŸ›
Dumitru Postoronca

πŸ›
Dylan Adams

πŸ› - -
Eden Hao

πŸ›
Edward Klimoshenko

πŸ› πŸ’»
Egor Bredikhin

πŸ›
Elan P. Kugelmass

πŸ› + +
Elder S.

πŸ›
Emile

πŸ›
Eric

πŸ› - -
Eric Kintzer

πŸ›
Eric Perret

πŸ›
Eric Squires

πŸ›
Erich L Foster

πŸ› -
Erik Bleske

πŸ› -
Ernst Reissner

πŸ› -
F.W. Dekker

πŸ› +
Erik Bleske

πŸ› +
Ernst Reissner

πŸ› +
Ewan Tempero

πŸ› +
F.W. Dekker

πŸ›
FSchliephacke

πŸ›
Facundo

πŸ›
Federico Giust

πŸ› + +
Fedor Sherstobitov

πŸ›
Felix Lampe

πŸ›
Filip Golonka

πŸ›
Filipe Esperandio

πŸ’» πŸ› - -
Filippo Nova

πŸ›
Francesco la Torre

πŸ›
Francisco Duarte

πŸ› + +
Frieder Bluemle

πŸ›
Frits Jalvingh

πŸ’» πŸ›
G. Bazior

πŸ›
Gabe Henkes

πŸ› - - +
Gary Gregory

πŸ›
Genoud Magloire

πŸ›
Geoffrey555

πŸ› + +
Georg Romstorfer

πŸ›
Gio

πŸ›
Gol

πŸ›
Gonzalo Exequiel Ibars Ingman

πŸ’» πŸ›
GooDer

πŸ› - -
Gregor Riegler

πŸ›
Grzegorz Olszewski

πŸ› + +
Gunther Schrijvers

πŸ’» πŸ›
Gustavo Krieger

πŸ›
Guy Elsmore-Paddock

πŸ›
GΓΆrkem MΓΌlayim

πŸ›
Hanzel Godinez

πŸ› - -
Haoliang Chen

πŸ›
Harsh Kukreja

πŸ› + +
Heber

πŸ›
Henning Schmiedehausen

πŸ’» πŸ›
Henning von Bargen

πŸ’»
HervΓ© Boutemy

πŸ›
Himanshu Pandey

πŸ› - -
Hokwang Lee

πŸ›
Hooperbloob

πŸ’» + +
Hung PHAN

πŸ›
IDoCodingStuffs

πŸ’» πŸ›
Iccen Gan

πŸ›
Ignacio Mariano Tirabasso

πŸ›
Igor Melnichenko

πŸ› - -
Igor Moreno

πŸ›
Intelesis-MS

πŸ› + +
Iroha_

πŸ›
Ishan Srivastava

πŸ›
Ivano Guerini

πŸ›
Ivar Andreas Bonsaksen

πŸ›
Ivo Ε mΓ­d

πŸ› - -
JJengility

πŸ›
Jake Hemmerle

πŸ› + +
James Harrison

πŸ› πŸ’»
Jan

πŸ›
Jan Aertgeerts

πŸ’» πŸ›
Jan BrΓΌmmer

πŸ›
Jan TΕ™Γ­ska

πŸ› - -
Jan-Lukas Else

πŸ›
Jason Qiu

πŸ’» πŸ“– + +
Jason Williams

πŸ›
Jean-Paul Mayer

πŸ›
Jean-Simon Larochelle

πŸ›
Jeff Bartolotta

πŸ’» πŸ›
Jeff Hube

πŸ’» πŸ› - -
Jeff Jensen

πŸ›
Jeff May

πŸ› -
Jens Gerdes

πŸ› -
Jeroen Borgers

πŸ› πŸ’» -
Jerome Russ

πŸ› -
JerritEic

πŸ’» πŸ“– -
Jiri Pejchal

πŸ› +
Jens Gerdes

πŸ› +
Jeroen Borgers

πŸ› πŸ’» πŸ“’ +
Jerome Russ

πŸ› +
JerritEic

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

πŸ›
Jithin Sunny

πŸ›
JiΕ™Γ­ Ε korpil

πŸ› + +
Joao Machado

πŸ›
Jochen Krauss

πŸ›
Johan Hammar

πŸ›
John Karp

πŸ›
John Zhang

πŸ› - -
John-Teng

πŸ’» πŸ›
Jon Moroney

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

πŸ›
Jonathan Wiesel

πŸ’» πŸ›
Jordan

πŸ›
Jordi Llach

πŸ›
Jorge SolΓ³rzano

πŸ› - -
JorneVL

πŸ›
Jose Palafox

πŸ› + +
Jose Stovall

πŸ›
Joseph

πŸ’»
Joseph Heenan

πŸ›
Josh Feingold

πŸ’» πŸ›
Josh Holthaus

πŸ› - -
Joshua S Arquilevich

πŸ›
JoΓ£o Ferreira

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

πŸ›
Juan MartΓ­n Sotuyo Dodero

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

πŸ›
Julian Voronetsky

πŸ›
Julien

πŸ› - -
Julius

πŸ›
JustPRV

πŸ› + +
JΓΆrn Huxhorn

πŸ›
KThompso

πŸ›
Kai Amundsen

πŸ›
Karel Vervaeke

πŸ›
Karl-Andero Mere

πŸ› - -
Karl-Philipp Richter

πŸ›
Karsten Silz

πŸ› + +
Kazuma Watanabe

πŸ›
Kev

πŸ›
Keve MΓΌller

πŸ›
Kevin Guerra

πŸ’»
Kevin Jones

πŸ› - -
Kevin Wayne

πŸ›
Kieran Black

πŸ› + +
Kirill Zubov

πŸ›
Kirk Clemens

πŸ’» πŸ›
Klaus Hartl

πŸ›
Koen Van Looveren

πŸ›
Kris Scheibe

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

πŸ›
LaLucid

πŸ’» + +
Larry Diamond

πŸ’» πŸ›
Lars Knickrehm

πŸ›
Leo Gutierrez

πŸ›
LiGaOg

πŸ’»
Lintsi

πŸ› - -
Linus Fernandes

πŸ›
Lixon Lookose

πŸ› + +
Logesh

πŸ›
Lorenzo Gabriele

πŸ›
LoΓ―c Ledoyen

πŸ›
Lucas Silva

πŸ›
Lucas Soncini

πŸ’» πŸ› +
Luis Alcantar

πŸ’» +
Lukasz Slonina

πŸ› -
Lukasz Slonina

πŸ›
Lukebray

πŸ› +
Lynn

πŸ’» πŸ›
Lyor Goldstein

πŸ›
MCMicS

πŸ›
Macarse

πŸ› @@ -447,422 +456,431 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Marquis Wang

πŸ› +
MartGit

πŸ›
Martin Feldsztejn

πŸ›
Martin Lehmann

πŸ›
Martin Spamer

πŸ›
Martin TarjΓ‘nyi

πŸ›
MatFl

πŸ› -
Mateusz Stefanski

πŸ› +
Mateusz Stefanski

πŸ›
Mathieu Gouin

πŸ›
MatiasComercio

πŸ’» πŸ›
Matt Benson

πŸ›
Matt De Poorter

πŸ›
Matt Hargett

πŸ’» πŸ’΅
Matt Harrah

πŸ› -
Matt Nelson

πŸ› +
Matt Nelson

πŸ›
Matthew Amos

πŸ›
Matthew Duggan

πŸ›
Matthew Hall

πŸ›
MatΓ­as Fraga

πŸ’» πŸ›
Maxime Robert

πŸ’» πŸ›
MetaBF

πŸ› -
Michael

πŸ› +
Michael

πŸ›
Michael Bell

πŸ›
Michael Bernstein

πŸ›
Michael Clay

πŸ›
Michael Dombrowski

πŸ›
Michael Hausegger

πŸ›
Michael Hoefer

πŸ› -
Michael MΓΆbius

πŸ› +
Michael MΓΆbius

πŸ›
Michael N. Lipp

πŸ›
Michael Pellegrini

πŸ›
Michal Kordas

πŸ›
MichaΕ‚ Borek

πŸ›
MichaΕ‚ KuliΕ„ski

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

πŸ› -
Mihai Ionut

πŸ› +
Mihai Ionut

πŸ›
Mirek Hankus

πŸ›
Mladjan Gadzic

πŸ›
MrAngry52

πŸ›
Muminur Choudhury

πŸ›
Mykhailo Palahuta

πŸ’» πŸ›
Nagendra Kumar Singh

πŸ› -
Nahuel Barrios

πŸ› +
Nahuel Barrios

πŸ›
Nathan Braun

πŸ›
Nathan Reynolds

πŸ›
Nathan Reynolds

πŸ›
NathanaΓ«l

πŸ›
Naveen

πŸ’»
Nazdravi

πŸ› -
Neha-Dhonde

πŸ› +
Neha-Dhonde

πŸ›
Nicholas Doyle

πŸ›
Nick Butcher

πŸ›
Nico Gallinal

πŸ›
Nicola Dal Maso

πŸ›
Nicolas Filotto

πŸ’»
Nicolas Vuillamy

πŸ“– -
Nikita Chursin

πŸ› +
Nikita Chursin

πŸ›
Niklas Baudy

πŸ›
Nikolas Havrikov

πŸ›
Nilesh Virkar

πŸ›
Nimit Patel

πŸ›
Niranjan Harpale

πŸ›
Noah Sussman

πŸ› -
Noah0120

πŸ› +
Noah0120

πŸ›
Noam Tamim

πŸ›
Noel Grandin

πŸ›
Olaf Haalstra

πŸ› +
Oleg Andreych

πŸ’» πŸ›
Oleg Pavlenko

πŸ› -
Oleksii Dykov

πŸ’» -
Oliver Eikemeier

πŸ› -
Olivier Parent

πŸ’» πŸ› +
Oleksii Dykov

πŸ’» πŸ› +
Oliver Eikemeier

πŸ› +
Oliver Siegmar

πŸ’΅ +
Olivier Parent

πŸ’» πŸ›
Ollie Abbey

πŸ’» πŸ›
OverDrone

πŸ›
Ozan Gulle

πŸ’» πŸ›
PUNEET JAIN

πŸ› + +
Parbati Bose

πŸ›
Paul Berg

πŸ›
Pavel Bludov

πŸ› - -
Pavel Mička

πŸ›
Pedro Nuno Santos

πŸ›
Pedro Rijo

πŸ›
Pelisse Romain

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

πŸ’»
Pete Davids

πŸ›
Peter Bruin

πŸ› - -
Peter Chittum

πŸ’» πŸ›
Peter Cudmore

πŸ›
Peter Kasson

πŸ›
Peter Kofler

πŸ› + +
Peter Paul Bakker

πŸ’»
Pham Hai Trung

πŸ›
Philip Graf

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

πŸ›
Philippe Ozil

πŸ›
Phinehas Artemix

πŸ›
Phokham Nonava

πŸ› -
Piotr SzymaΕ„ski

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

πŸ’» πŸ› -
Pranay Jaiswal

πŸ› +
Piotr SzymaΕ„ski

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

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

πŸ›
Prasad Kamath

πŸ›
Prasanna

πŸ›
Presh-AR

πŸ›
Puneet1726

πŸ› + +
Rafael CortΓͺs

πŸ›
RaheemShaik999

πŸ›
RajeshR

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

πŸ›
Ramel0921

πŸ›
Raquel Pau

πŸ›
Ravikiran Janardhana

πŸ› + +
Reda Benhemmouche

πŸ›
Renato Oliveira

πŸ’» πŸ›
Rich DiCroce

πŸ› - -
Riot R1cket

πŸ›
Rishabh Jain

πŸ›
RishabhDeep Singh

πŸ›
Robbie Martinus

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

πŸ›
Robert Painsi

πŸ›
Robert Russell

πŸ› - -
Robert SΓΆsemann

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

πŸ›
Robin Richtsfeld

πŸ›
Robin Stocker

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

πŸ›
RochusOest

πŸ›
Rodolfo Noviski

πŸ› - -
Rodrigo Casara

πŸ›
Rodrigo Fernandes

πŸ›
Roman Salvador

πŸ’» πŸ›
Ronald Blaschke

πŸ› + +
RΓ³bert Papp

πŸ›
Saikat Sengupta

πŸ›
Saksham Handu

πŸ› - -
Saladoc

πŸ›
Salesforce Bob Lightning

πŸ›
Sam Carlberg

πŸ›
Satoshi Kubo

πŸ› + +
Scott Kennedy

πŸ›
Scott Wells

πŸ› πŸ’»
Scrsloota

πŸ’» - -
Sebastian BΓΆgl

πŸ›
Sebastian Schuberth

πŸ›
Sebastian Schwarz

πŸ›
Sergey Gorbaty

πŸ› + +
Sergey Kozlov

πŸ›
Sergey Yanzin

πŸ’» πŸ›
Seth Wilcox

πŸ’» - -
Shubham

πŸ’» πŸ› -
Simon Abykov

πŸ’» +
Simon Abykov

πŸ’» πŸ›
Simon Xiao

πŸ›
Srinivasan Venkatachalam

πŸ› + +
Stanislav Gromov

πŸ›
Stanislav Myachenkov

πŸ’»
Stefan Birkner

πŸ› - -
Stefan Bohn

πŸ›
Stefan Endrullis

πŸ›
Stefan KlΓΆss-Schuster

πŸ›
Stefan Wolf

πŸ› + +
Stephan H. Wissel

πŸ›
Stephen

πŸ›
Stephen Friedrich

πŸ› - -
Steve Babula

πŸ’»
Stexxe

πŸ›
Stian LΓ₯gstad

πŸ›
StuartClayton5

πŸ› -
Supun Arunoda

πŸ› -
Suren Abrahamyan

πŸ› -
SwatiBGupta1110

πŸ› +
Supun Arunoda

πŸ› +
Suren Abrahamyan

πŸ› +
Suvashri

πŸ“– +
SwatiBGupta1110

πŸ›
SyedThoufich

πŸ›
Szymon Sasin

πŸ›
T-chuangxin

πŸ› + +
TERAI Atsuhiro

πŸ›
TIOBE Software

πŸ’» πŸ›
Taylor Smock

πŸ›
Techeira DamiΓ‘n

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

πŸ›
TehBakker

πŸ›
The Gitter Badger

πŸ› + +
Theodoor

πŸ›
Thiago Henrique HΓΌpner

πŸ›
Thibault Meyer

πŸ›
Thomas GΓΌttler

πŸ› - -
Thomas Jones-Low

πŸ›
Thomas Smith

πŸ’» πŸ›
ThrawnCA

πŸ› + +
Thunderforge

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

πŸ›
Tobias Weimer

πŸ’» πŸ›
Tom Daly

πŸ› - -
Tomer Figenblat

πŸ›
Tomi De Lucca

πŸ’» πŸ›
Torsten Kleiber

πŸ› + +
TrackerSB

πŸ›
Ullrich Hafner

πŸ›
Utku Cuhadaroglu

πŸ’» πŸ›
Valentin Brandl

πŸ› - -
Valeria

πŸ›
Vasily Anisimov

πŸ›
Vibhor Goyal

πŸ› + +
Vickenty Fesunov

πŸ›
Victor NoΓ«l

πŸ›
Vincent Galloy

πŸ’»
Vincent HUYNH

πŸ› - -
Vincent Maurin

πŸ›
Vincent Privat

πŸ›
Vishhwas

πŸ› + +
Vitaly

πŸ›
Vitaly Polonetsky

πŸ›
Vojtech Polivka

πŸ›
Vsevolod Zholobov

πŸ› - -
Vyom Yadav

πŸ’»
Wang Shidong

πŸ›
Waqas Ahmed

πŸ› + +
Wayne J. Earl

πŸ›
Wchenghui

πŸ›
Will Winder

πŸ›
William Brockhus

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

πŸ›
Wim Deblauwe

πŸ›
Woongsik Choi

πŸ› + +
XenoAmess

πŸ’» πŸ›
Yang

πŸ’»
YaroslavTER

πŸ›
Young Chan

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

πŸ›
Yuri Dolzhenko

πŸ›
Yurii Dubinka

πŸ› + +
Zoltan Farkas

πŸ›
Zustin

πŸ›
aaronhurst-google

πŸ› πŸ’»
alexmodis

πŸ› - -
andreoss

πŸ›
andrey81inmd

πŸ’» πŸ›
anicoara

πŸ› + +
arunprasathav

πŸ›
asiercamara

πŸ›
astillich-igniti

πŸ’»
avesolovksyy

πŸ› - -
avishvat

πŸ›
avivmu

πŸ›
axelbarfod1

πŸ› + +
b-3-n

πŸ›
balbhadra9

πŸ›
base23de

πŸ›
bergander

πŸ› - -
berkam

πŸ’» πŸ›
breizh31

πŸ›
caesarkim

πŸ› + +
carolyujing

πŸ› +
cbfiddle

πŸ›
cesares-basilico

πŸ›
chrite

πŸ›
cobratbq

πŸ› - -
coladict

πŸ›
cosmoJFH

πŸ› + +
cristalp

πŸ›
crunsk

πŸ›
cwholmes

πŸ›
cyberjj999

πŸ›
cyw3

πŸ› - -
d1ss0nanz

πŸ›
dalizi007

πŸ’» + +
danbrycefairsailcom

πŸ›
dariansanity

πŸ›
darrenmiliband

πŸ›
davidburstrom

πŸ›
dbirkman-paloalto

πŸ› - -
deepak-patra

πŸ›
dependabot[bot]

πŸ’» πŸ› + +
dinesh150

πŸ›
diziaq

πŸ›
dreaminpast123

πŸ›
duanyanan

πŸ›
dutt-sanjay

πŸ› - -
dylanleung

πŸ›
dzeigler

πŸ› + +
ekkirala

πŸ›
emersonmoura

πŸ›
fairy

πŸ›
filiprafalowicz

πŸ’»
foxmason

πŸ› - -
frankegabor

πŸ›
frankl

πŸ› + +
freafrea

πŸ›
fsapatin

πŸ›
gracia19

πŸ›
guo fei

πŸ›
gurmsc5

πŸ› - -
gwilymatgearset

πŸ’» πŸ›
haigsn

πŸ› + +
hemanshu070

πŸ›
henrik242

πŸ›
hongpuwu

πŸ›
hvbtup

πŸ’» πŸ›
igniti GmbH

πŸ› - -
ilovezfs

πŸ›
itaigilo

πŸ› + +
jakivey32

πŸ›
jbennett2091

πŸ›
jcamerin

πŸ›
jkeener1

πŸ›
jmetertea

πŸ› - -
johnra2

πŸ’»
josemanuelrolon

πŸ’» πŸ› + +
kabroxiko

πŸ’» πŸ›
karwer

πŸ›
kaulonline

πŸ›
kdaemonv

πŸ›
kenji21

πŸ’» πŸ› - -
kfranic

πŸ›
khalidkh

πŸ› + + +
koalalam

πŸ›
krzyk

πŸ›
lasselindqvist

πŸ›
lgemeinhardt

πŸ›
lihuaib

πŸ›
lonelyma1021

πŸ› +
lpeddy

πŸ› -
lpeddy

πŸ›
lujiefsi

πŸ’»
lukelukes

πŸ’»
lyriccoder

πŸ›
marcelmore

πŸ›
matchbox

πŸ›
matthiaskraaz

πŸ› +
meandonlyme

πŸ› -
meandonlyme

πŸ›
mikesive

πŸ›
milossesic

πŸ› +
mohan-chinnappan-n

πŸ’»
mriddell95

πŸ›
mrlzh

πŸ›
msloan

πŸ› diff --git a/docs/pages/pmd/projectdocs/decisions.md b/docs/pages/pmd/projectdocs/decisions.md new file mode 100644 index 0000000000..af0258c29c --- /dev/null +++ b/docs/pages/pmd/projectdocs/decisions.md @@ -0,0 +1,14 @@ +--- +title: Architecture Decisions +sidebar: pmd_sidebar +permalink: pmd_projectdocs_decisions.html +last_updated: July 2022 +--- + +
    +{% for page in site.pages %} + {% if page.adr == true and page.adr_status != "" %} +
  • {{ page.title }} ({{ page.adr_status }})
  • + {% endif %} +{% endfor %} +
diff --git a/docs/pages/pmd/projectdocs/decisions/adr-1.md b/docs/pages/pmd/projectdocs/decisions/adr-1.md new file mode 100644 index 0000000000..85d750b473 --- /dev/null +++ b/docs/pages/pmd/projectdocs/decisions/adr-1.md @@ -0,0 +1,70 @@ +--- +title: ADR 1 - Use architecture decision records +sidebar: pmd_sidebar +permalink: pmd_projectdocs_decisions_adr_1.html +sidebaractiveurl: /pmd_projectdocs_decisions.html +adr: true +# Proposed / Accepted / Deprecated / Superseded +adr_status: "Accepted" +last_updated: September 2022 +--- + +# Context + +PMD has grown over 20 years as an open-source project. Along the way many decisions have been made, but they are not +explicitly documented. PMD is also developed by many individuals and the original developers might +not even be around anymore. + +Without having documentation records about decisions it is hard for new developers to understand the reasons +of past decisions. This might lead to either ignore these past (unknown) decisions and change it without +fully understanding its consequences. This could create new issues down the road, e.g. a decision supporting +a requirement that is not tested. + +On the other hand, accepting the past decisions without challenging it might slow down the project and +possible innovations. It could lead to a situation where the developers are afraid to change anything +in order to not break the system. + +Past decisions have been made within context and the context can change. Therefore, past decisions can still be +valid today, or they don't apply anymore. In that case, the decision should be revisited. + +See also the blog post [Documenting Architecture Decisions](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) +by Michael Nygard. + +There are many templates around to choose from. +gives a nice summary. The page gives a good overview on ADR and for adr-related tooling. + +# Decision + +We will document the decisions we make as a project as a collection of "Architecture Decision Records". +In order to keep it simple, we will use only a simple template proposed by Michael Nygard. +The documents are stored together with the source code and are part of the generated documentation site. + +A new ADR should be proposed with a pull request to open the discussion. +The initial status of the new ADR is "Proposed". When maintainer consensus is reached during the PR +review, then the status is changed to "Accepted" when the PR is merged. +A new entry in the "Change History" section should be added, when the PR is merged. + +In order to propose a change to an existing ADR a new pull request should be opened which modifies the ADR. +The change can be to amend the ADR or to challenge it and maybe deprecate it. A new entry in the +"Change History" section should be added to summary the change. When maintainer consensus is reached +during the PR review, then the PR can be merged and the ADR is updated. + +# Status + +{{ page.adr_status }} (Last updated: {{ page.last_updated }}) + +# Consequences + +Explicitly documenting decisions has the benefit that new developers joining the projects know about the decisions +and can read the context and consequences of the decisions. This will likely also improve the overall quality +as the decisions need to be formulated and written down. Everybody is on the same page. + +However, this also adds additional tasks, and it takes time to write down and document the decisions. + +# Change History + +2022-09-30: Status changed to "Accepted". + +2022-09-06: Added section "Change History" to the template. Added "Last updated" to "Status" section. + +2022-07-28: Proposed initial version. diff --git a/docs/pages/pmd/projectdocs/decisions/adr-2.md b/docs/pages/pmd/projectdocs/decisions/adr-2.md new file mode 100644 index 0000000000..b3f57efd4f --- /dev/null +++ b/docs/pages/pmd/projectdocs/decisions/adr-2.md @@ -0,0 +1,71 @@ +--- +title: ADR 2 - Policy on the use of Kotlin for development +sidebar: pmd_sidebar +permalink: pmd_projectdocs_decisions_adr_2.html +sidebaractiveurl: /pmd_projectdocs_decisions.html +adr: true +# Proposed / Accepted / Deprecated / Superseded +adr_status: "Accepted" +last_updated: September 2022 +--- + +# Context + +We currently use Kotlin only for unit tests at some places (e.g. pmd-lang-test module provides a couple of base +test classes). We were cautious to expand Kotlin because of poor development support outside JetBrain's +IntelliJ IDEA. E.g. the [Kotlin Plugin for Eclipse](https://marketplace.eclipse.org/content/kotlin-plugin-eclipse) +doesn't work properly as described in the reviews. + +For VS Code there is a [Kotlin Plugin](https://marketplace.visualstudio.com/items?itemName=mathiasfrohlich.Kotlin) +with basic features. Online IDEs like gitpod.io and GitHub Codespaces are often based on VS Code. + +Using Kotlin means, that we accept, that PMD can only be developed with IntelliJ IDEA. This feels like a vendor lock-in. + +Also, bringing in a mix of languages might make maintenance a bit harder and make it harder for new contributors. +However - PMD is a tool that deals with many, many languages anyway, so this is maybe not a real argument. + +Nevertheless, extending the usage of Kotlin within PMD can also increase contributions. + +# Decision + +We are generally open to the idea to increase usage of Kotlin within PMD. In order to gain experience +and to keep it within bounds and therefore maintainable we came up with the following rules: + +* The module `pmd-core` should stay in plain Java. This helps in keeping binary compatibility when changing sources. + `pmd-core` contains the main APIs for all language modules. We currently release all modules at the same time, + so this is not a real problem for now. But that might change in the future: Because only few language modules have + actual changes per release, it doesn't really make sense to release everything as long as the modules stay + compatible. But that's another story. +* For (unit) testing, Kotlin can be used in `pmd-core` and in the language modules. The test frameworks can also + use Kotlin (`pmd-test` doesn't yet, `pmd-lang-test` does already). +* Additionally: from now on, we allow to have the individual language modules be implemented in different languages + when it makes sense. So, a language module might decide to use plain Java (like now) or also Kotlin. +* When mixing languages (e.g. Java + Kotlin), we need to care that the modules can still be used with plain Java. + E.g. when writing custom rules: `pmd-java` provides a couple of APIs for rules (like symbol table, type resolution) + and we should not force the users to use Kotlin (at least not for language modules which already exist and + for which users might have written custom rules in Java already). +* It is also possible to write the entire language module in Kotlin only. Then the rules would be written in Kotlin + as well. And the possible problems when mixing languages are gone. But that applies only for new language modules. +* When refactoring an existing language module from Java only to introduce Kotlin, care needs to be taken to + not make incompatible changes. If compatibility (binary or source) can't be maintained, then that would be a + major version change. + +# Status + +{{ page.adr_status }} (Last updated: {{ page.last_updated }}) + +# Consequences + +Allowing more Kotlin in PMD can attract new contributions. It might make it easier to develop small DSLs. +In the future we might also consider to use other languages than Kotlin, e.g. for `pmd-scala` Scala might make sense. + +On the other side, other IDEs than IntelliJ IDEA will have a difficult time to deal with PMD's source code +when Kotlin is used. Eclipse can't be used practically anymore. + +Maintaining a polyglot code base with multiple languages is likely to be more challenging. + +# Change History + +2022-09-30: Changed status to "Accepted". + +2022-07-28: Proposed initial version. diff --git a/docs/pages/pmd/projectdocs/decisions/adr-NNN.md b/docs/pages/pmd/projectdocs/decisions/adr-NNN.md new file mode 100644 index 0000000000..b18a9b2866 --- /dev/null +++ b/docs/pages/pmd/projectdocs/decisions/adr-NNN.md @@ -0,0 +1,34 @@ +--- +title: ADR NNN - Template +sidebar: pmd_sidebar +permalink: pmd_projectdocs_decisions_adr_NNN.html +sidebaractiveurl: /pmd_projectdocs_decisions.html +adr: true +# Proposed / Accepted / Deprecated / Superseded +adr_status: "" +last_updated: July 2022 +--- + + + +# Context + +What is the issue that we're seeing that is motivating this decision or change? + +# Decision + +What is the change that we're proposing and/or doing? + +# Status + +{{ page.adr_status }} (Last updated: {{ page.last_updated }}) + +# Consequences + +What becomes easier or more difficult to do because of this change? + +# Change History + +YYYY-MM-DD: Add xyz. + +YYYY-MM-DD: Proposed initial version. diff --git a/docs/pages/pmd/projectdocs/trivia/news.md b/docs/pages/pmd/projectdocs/trivia/news.md index 8fb06bbd4c..2cf8db566d 100644 --- a/docs/pages/pmd/projectdocs/trivia/news.md +++ b/docs/pages/pmd/projectdocs/trivia/news.md @@ -9,26 +9,43 @@ author: Tom Copeland ### Salesforce / Apex Language Module +* October 2020 - [Salesforce CLI Scanner Custom XPath Rules - Part 1](https://bobbuzzard.blogspot.com/2020/10/salesforce-cli-scanner-custom-xpath.html), + [Salesforce CLI Scanner Custom XPath Rules - Part 2](http://bobbuzzard.blogspot.com/2020/10/salesforce-cli-scanner-custom-xpath_11.html) + by [Keir Bowden](https://twitter.com/bob_buzzard) + * March 2020 - [Helping Salesforce developers create readable and maintainable Apex code](https://gearset.com/blog/helping-sf-developers-create-readable-and-maintainable-apex-code) * July 2019 - [Apex PMD \| Static code analysis - Apex Hours](https://youtu.be/34PxAHtAavU) -* June 2019 - [Pluralsight](https://www.pluralsight.com/authors/don-robins) Course about leveraging PMD usage for Salesforce by [Robert SΓΆsemann](https://github.com/rsoesemann) (Apex Language Module Contributor) [Play by Play: Automated Code Analysis in Salesforce - a Tools Deep-Dive](https://www.pluralsight.com/courses/play-by-play-automated-code-analysis-in-salesforce) +* June 2019 - [Pluralsight](https://www.pluralsight.com/authors/don-robins) Course about leveraging PMD usage for + Salesforce by [Robert SΓΆsemann](https://github.com/rsoesemann) (Apex Language Module Contributor) + [Play by Play: Automated Code Analysis in Salesforce - a Tools Deep-Dive](https://www.pluralsight.com/courses/play-by-play-automated-code-analysis-in-salesforce) -* June 2018 - [Salesforce Way Podcast](https://salesforceway.com/podcast/podcast/) with [Robert SΓΆsemann](https://github.com/rsoesemann) [Static Code Analysis with PMD for Apex](https://salesforceway.com/podcast/podcast/static-code-analysis-with-pmd-for-apex/) +* June 2018 - [Salesforce Way Podcast](https://salesforceway.com/podcast/podcast/) with + [Robert SΓΆsemann](https://github.com/rsoesemann) [Static Code Analysis with PMD for Apex](https://salesforceway.com/podcast/podcast/static-code-analysis-with-pmd-for-apex/) -* January 2018 - [Webinar: How to contribute Apex rules to PMD with Robert SΓΆsemann](https://www.youtube.com/watch?v=7_Ex9WWS_3Q) +* January 2018 - [Webinar: How to contribute Apex rules to PMD with Robert SΓΆsemann](https://www.youtube.com/watch?v=7_Ex9WWS_3Q) -* August 2017 - Webinar about how to use PMD with The Welkin Suite Salesforce IDE - Author [Robert SΓΆsemann](https://github.com/rsoesemann) - [Improving your Apex Code Quality with PMD in The Welkin Suite](https://www.youtube.com/watch?v=Ypyiy5b6huc) +* August 2017 - Webinar about how to use PMD with The Welkin Suite Salesforce IDE - Author + [Robert SΓΆsemann](https://github.com/rsoesemann) - [Improving your Apex Code Quality with PMD in The Welkin Suite](https://www.youtube.com/watch?v=Ypyiy5b6huc) -* November 2016 - Recording of [Robert SΓΆsemann](https://github.com/rsoesemann)'s Session at Salesforce Dreamforce Conference about enforcing Clean Code in the Salesforce world using PMD and other tools [Clean Apex Code with Automatic Code Metrics](https://www.youtube.com/watch?v=bW7m6y6bEug) +* November 2016 - Recording of [Robert SΓΆsemann](https://github.com/rsoesemann)'s Session at Salesforce Dreamforce + Conference about enforcing Clean Code in the Salesforce world using PMD and other tools + [Clean Apex Code with Automatic Code Metrics](https://www.youtube.com/watch?v=bW7m6y6bEug) ### PMD in general and other Language Modules -* February 2021 - Artem Krosheninnikov's talk about Quality Assurance Automation: [Artem Krosheninnikov, Wrike - How static analysis can help in QAA processes](https://www.youtube.com/watch?v=L42zH5ne074) +* February 2021 - Artem Krosheninnikov's talk about Quality Assurance Automation: + [Artem Krosheninnikov, Wrike - How static analysis can help in QAA processes]( + https://www.youtube.com/watch?v=L42zH5ne074) -* May 2019 - [Code quality assurance with PMD – An extensible static code analyser for Java and other languages](https://www.datarespons.com/code-quality-assurance-with-pmd/) +* December 2020 - Jeroen Borgers' talk about finding performance bugs with PMD: + [J-Fall Virtual 2020: Jeroen Borgers - Fixing your performance and concurrency bugs before they bite you]( + https://www.youtube.com/watch?v=Z_sT38KTRNk) + +* May 2019 - [Code quality assurance with PMD – An extensible static code analyser for Java and other languages]( + https://www.datarespons.com/code-quality-assurance-with-pmd/) * February 2012 - Romain Pelisse's lightning talk at FOSDEM 2012 about "PMD5: What can it do for you?". [Video recording is available](http://video.fosdem.org/2012/lightningtalks/PMD5.webm). diff --git a/docs/pages/pmd/userdocs/3rdpartyrulesets.md b/docs/pages/pmd/userdocs/3rdpartyrulesets.md new file mode 100644 index 0000000000..ecbfee2818 --- /dev/null +++ b/docs/pages/pmd/userdocs/3rdpartyrulesets.md @@ -0,0 +1,27 @@ +--- +title: 3rd party rulesets +tags: [rule_references, userdocs] +summary: Lists rulesets and rules from the community +permalink: pmd_userdocs_3rdpartyrulesets.html +last_updated: September 2022 +--- + +## For Java + +* **jPinpoint rules:** PMD rule set for performance aware Java and Kotlin coding. + * +* **arch4u-pmd** is a library with pmd rules that bring new regulations related to known problems in REST API, logging, + monitoring, etc., including reconfigured default pmd rules to decrease false-positive violations during usage of + well-known frameworks like Spring, Quarkus, etc. + * +* Sample ruleset from **maxdocs**, a multi markup wiki engine. + * +* Sample ruleset from **geotools**, an open source Java library that provides tools for geospatial data. + * + * + + +## For Apex +* **unhappy-soup**, a repository with problematic Salesforce code to showcase PMD, the SFDX Scanner CLI + * + diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index 69a29d966d..47e413805c 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -197,18 +197,20 @@ Example: ``` * [apex](pmd_rules_apex.html) (Salesforce Apex) +* [ecmascript](pmd_rules_ecmascript.html) (JavaScript) +* [html](pmd_rules_html.html) * [java](pmd_rules_java.html) * [Supported Versions](pmd_languages_java.html) -* [ecmascript](pmd_rules_ecmascript.html) (JavaScript) * [jsp](pmd_rules_jsp.html) * [modelica](pmd_rules_modelica.html) * [plsql](pmd_rules_plsql.html) +* [pom](pmd_rules_pom.html) (Maven POM) * [scala](pmd_rules_scala.html) * Supported Versions: 2.10, 2.11, 2.12, 2.13 (default) * [vf](pmd_rules_vf.html) (Salesforce VisualForce) * [vm](pmd_rules_vm.html) (Apache Velocity) -* [xml and xsl](pmd_rules_xml.html) - +* [xml](pmd_rules_xml.html) +* [xsl](pmd_rules_xsl.html) ## Available Report Formats diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index 67b81492be..22d594259b 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -122,7 +122,7 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt {% include custom/cli_option_row.html options="--ignore-literal-sequences" description="Ignore sequences of literals (common e.g. in list initializers)" default="false" - languages="C#, C++" + languages="C#, C++, Lua" %} {% include custom/cli_option_row.html options="--ignore-usings" description="Ignore `using` directives in C# when comparing text" diff --git a/docs/pages/pmd/userdocs/installation.md b/docs/pages/pmd/userdocs/installation.md index 7b053bf4e1..89aa58cf3e 100644 --- a/docs/pages/pmd/userdocs/installation.md +++ b/docs/pages/pmd/userdocs/installation.md @@ -15,7 +15,7 @@ sidebar: pmd_sidebar OpenJDK from [Azul](https://www.azul.com/downloads/zulu-community/) or [AdoptOpenJDK](https://adoptopenjdk.net/) 1.7 or higher. - **Note:** For analyzing Apex, JavaScript, Scala or VisualForce or running the [Designer](pmd_userdocs_extending_designer_reference.html) + **Note:** For analyzing Apex, HTML, JavaScript, Scala or VisualForce or running the [Designer](pmd_userdocs_extending_designer_reference.html) at least Java 8 is required. * A zip archiver, e.g.: @@ -23,7 +23,7 @@ sidebar: pmd_sidebar * For Windows: [Winzip](http://winzip.com) or the free [7-zip](http://www.7-zip.org/) * For Linux / Unix: [InfoZip](http://infozip.sourceforge.net/) -{% include note.html content="For executing the Designer (./run.sh designer) using [OpenJDK](http://jdk.java.net) or Java 11, you need additionally [OpenJFX](http://jdk.java.net). Download it, extract it and set the environment variable JAVAFX_HOME." %} +{% include note.html content="For executing the Designer (./run.sh designer) using [OpenJDK](http://jdk.java.net) or Java 11, you need additionally [OpenJFX](https://openjfx.io/). Download it, extract it and set the environment variable JAVAFX_HOME." %} ### Installation diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..ffc4e4ac9c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,10 +15,15 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy ### Fixed Issues +* doc + * [#4144](https://github.com/pmd/pmd/pull/4144) \[doc] Update docs to reflect supported languages +* java-documentation + * [#4141](https://github.com/pmd/pmd/issues/4141): \[java] UncommentedEmptyConstructor FP when constructor annotated with @Autowired ### API Changes ### External Contributions +* [#4142](https://github.com/pmd/pmd/pull/4142): \[java] fix #4141 Update UncommentedEmptyConstructor - ignore @Autowired annotations - [Lynn](https://github.com/LynnBroe) (@LynnBroe) {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 932e00b8b9..209d6a89cb 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,98 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases + +## 30-September-2022 - 6.50.0 + +The PMD team is pleased to announce PMD 6.50.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Lua now supports additionally Luau](#lua-now-supports-additionally-luau) + * [Modified rules](#modified-rules) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [CPD CLI](#cpd-cli) +* [Financial Contributions](#financial-contributions) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### Lua now supports additionally Luau + +This release of PMD adds support for [Luau](https://github.com/Roblox/luau), a gradually typed language derived +from Lua. This means, that the Lua language in PMD can now parse both Lua and Luau. + +#### Modified rules + +* The Java rule [`UnusedPrivateField`](https://pmd.github.io/pmd-6.50.0/pmd_rules_java_bestpractices.html#unusedprivatefield) now ignores private fields, if the fields are + annotated with any annotation or the enclosing class has any annotation. Annotations often enable a + framework (such as dependency injection, mocking or e.g. Lombok) which use the fields by reflection or other + means. This usage can't be detected by static code analysis. Previously these frameworks where explicitly allowed + by listing their annotations in the property "ignoredAnnotations", but that turned out to be prone of false + positive for any not explicitly considered framework. That's why the property "ignoredAnnotations" has been + deprecated for this rule. +* The Java rule [`CommentDefaultAccessModifier`](https://pmd.github.io/pmd-6.50.0/pmd_rules_java_codestyle.html#commentdefaultaccessmodifier) now by default ignores JUnit5 annotated + methods. This behavior can be customized using the property `ignoredAnnotations`. + +### Fixed Issues +* cli + * [#4118](https://github.com/pmd/pmd/issues/4118): \[cli] run.sh designer reports "integer expression expected" +* core + * [#4116](https://github.com/pmd/pmd/pull/4116): \[core] Missing --file arg in TreeExport CLI example +* doc + * [#4072](https://github.com/pmd/pmd/pull/4072): \[doc] Add architecture decision records + * [#4109](https://github.com/pmd/pmd/pull/4109): \[doc] Add page for 3rd party rulesets + * [#4124](https://github.com/pmd/pmd/pull/4124): \[doc] Fix typos in Java rule docs +* java + * [#3431](https://github.com/pmd/pmd/issues/3431): \[java] Add sample java project to regression-tester which uses new language constructs +* java-bestpractices + * [#4033](https://github.com/pmd/pmd/issues/4033): \[java] UnusedPrivateField - false positive with Lombok @ToString.Include + * [#4037](https://github.com/pmd/pmd/issues/4037): \[java] UnusedPrivateField - false positive with Spring @SpyBean +* java-codestyle + * [#3859](https://github.com/pmd/pmd/issues/3859): \[java] CommentDefaultAccessModifier is triggered in JUnit5 test class + * [#4085](https://github.com/pmd/pmd/issues/4085): \[java] UnnecessaryFullyQualifiedName false positive when nested and non-nested classes with the same name and in the same package are used together + * [#4133](https://github.com/pmd/pmd/issues/4133): \[java] UnnecessaryFullyQualifiedName - FP for inner class pkg.ClassA.Foo implementing pkg.Foo +* java-design + * [#4090](https://github.com/pmd/pmd/issues/4090): \[java] FinalFieldCouldBeStatic false positive with non-static synchronized block (regression in 6.48, worked with 6.47) +* java-errorprone + * [#1718](https://github.com/pmd/pmd/issues/1718): \[java] ConstructorCallsOverridableMethod false positive when calling super method + * [#2348](https://github.com/pmd/pmd/issues/2348): \[java] ConstructorCallsOverridableMethod occurs when unused overloaded method is defined + * [#4099](https://github.com/pmd/pmd/issues/4099): \[java] ConstructorCallsOverridableMethod should consider method calls with var access +* scala + * [#4138](https://github.com/pmd/pmd/pull/4138): \[scala] Upgrade scala-library to 2.12.7 / 2.13.9 and scalameta to 4.6.0 + +### API Changes + +#### CPD CLI + +* CPD now supports the `--ignore-literal-sequences` argument when analyzing Lua code. + +### Financial Contributions + +Many thanks to our sponsors: + +* [Oliver Siegmar](https://github.com/osiegmar) (@osiegmar) + +### External Contributions +* [#4066](https://github.com/pmd/pmd/pull/4066): \[lua] Add support for Luau syntax and skipping literal sequences in CPD - [Matt Hargett](https://github.com/matthargett) (@matthargett) +* [#4100](https://github.com/pmd/pmd/pull/4100): \[java] Update UnusedPrivateFieldRule - ignore any annotations - [Lynn](https://github.com/LynnBroe) (@LynnBroe) +* [#4116](https://github.com/pmd/pmd/pull/4116): \[core] Fix missing --file arg in TreeExport CLI example - [mohan-chinnappan-n](https://github.com/mohan-chinnappan-n) (@mohan-chinnappan-n) +* [#4124](https://github.com/pmd/pmd/pull/4124): \[doc] Fix typos in Java rule docs - [Piotrek Ε»ygieΕ‚o](https://github.com/pzygielo) (@pzygielo) +* [#4128](https://github.com/pmd/pmd/pull/4128): \[java] Fix False-positive UnnecessaryFullyQualifiedName when nested and non-nest… #4103 - [Oleg Andreych](https://github.com/OlegAndreych) (@OlegAndreych) +* [#4130](https://github.com/pmd/pmd/pull/4130): \[ci] GitHub Workflows security hardening - [Alex](https://github.com/sashashura) (@sashashura) +* [#4131](https://github.com/pmd/pmd/pull/4131): \[doc] TooFewBranchesForASwitchStatement - Use "if-else" instead of "if-then" - [Suvashri](https://github.com/Suvashri) (@Suvashri) +* [#4137](https://github.com/pmd/pmd/pull/4137): \[java] Fixes 3859: Exclude junit5 test methods from the commentDefaultAccessModifierRule - [Luis Alcantar](https://github.com/lfalcantar) (@lfalcantar) + +### Stats +* 100 commits +* 26 closed tickets & PRs +* Days since last release: 29 + ## 31-August-2022 - 6.49.0 The PMD team is pleased to announce PMD 6.49.0. diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index e39ef4b13d..3ecd79b58e 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 0302dd9be0..d80050e0ce 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 2cfe77ea15..d8c12a2db6 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index 70dcecaacf..7c3ab37f96 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -63,6 +63,7 @@ import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; */ public class PMD { + private static final Logger LOG = Logger.getLogger(PMD.class.getName()); /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java index cd7c7c7e99..5ad05cb9da 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java @@ -139,7 +139,7 @@ public class TreeExportCli { sb.append(System.lineSeparator()) .append(System.lineSeparator()); - sb.append("Example: ast-dump --format xml --language java MyFile.java") + sb.append("Example: ast-dump --format xml --language java --file MyFile.java") .append(System.lineSeparator()); System.err.print(sb); diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index dc6312c4d8..91c3734932 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 39cb5a5124..d0bac9376b 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-dart/pom.xml b/pmd-dart/pom.xml index 62ecfa06d7..5fffeab190 100644 --- a/pmd-dart/pom.xml +++ b/pmd-dart/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 5bf7cf2aa5..82070748b7 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-dist/src/main/resources/scripts/designer.bat b/pmd-dist/src/main/resources/scripts/designer.bat index bc3cbb9188..a53623c28c 100644 --- a/pmd-dist/src/main/resources/scripts/designer.bat +++ b/pmd-dist/src/main/resources/scripts/designer.bat @@ -4,35 +4,41 @@ set OPTS= set MAIN_CLASS=net.sourceforge.pmd.util.fxdesigner.DesignerStarter -:: sets the jver variable to the java version, eg 901 for 9.0.1+x or 180 for 1.8.0_171-b11 +:: sets the jver variable to the java version, eg 90 for 9.0.1+x or 80 for 1.8.0_171-b11 or 110 for 11.0.6.1 :: sets the jvendor variable to either java (oracle) or openjdk for /f tokens^=1^,3^,4^,5^ delims^=.-_+^"^ %%j in ('java -version 2^>^&1 ^| find "version"') do ( set jvendor=%%j if %%l EQU ea ( - set /A "jver=%%k00" + set /A "jver=%%k0" ) else ( - set /A jver=%%k%%l%%m + if %%k EQU 1 ( + :: for java version 1.7.x, 1.8.x, ignore the first 1. + set /A "jver=%%l%%m" + ) else ( + set /A "jver=%%k%%l" + ) ) ) + Set "jreopts=" :: oracle java 9 and 10 has javafx included as a module -if /I "%jvendor%" EQU "java" ( - if %jver% GEQ 900 ( - if %jver% LSS 1100 ( +if /I %jvendor% == java ( + if %jver% GEQ 90 ( + if %jver% LSS 110 ( :: enable reflection - Set jreopts=--add-opens javafx.controls/javafx.scene.control.skin=ALL-UNNAMED + set jreopts=--add-opens javafx.controls/javafx.scene.control.skin=ALL-UNNAMED ) ) ) set "_needjfxlib=0" -if /I "%jvendor%" EQU "openjdk" set _needjfxlib=1 -if /I "%jvendor%" EQU "java" ( - if %jver% GEQ 1100 set _needjfxlib=1 +if /I %jvendor% == openjdk set _needjfxlib=1 +if /I %jvendor% == java ( + if %jver% GEQ 110 set _needjfxlib=1 ) if %_needjfxlib% EQU 1 ( - if %jver% LSS 1000 ( + if %jver% LSS 100 ( echo For openjfx at least java 10 is required. pause exit diff --git a/pmd-dist/src/main/resources/scripts/run.sh b/pmd-dist/src/main/resources/scripts/run.sh index 1501db12bc..ece76fc949 100755 --- a/pmd-dist/src/main/resources/scripts/run.sh +++ b/pmd-dist/src/main/resources/scripts/run.sh @@ -2,7 +2,7 @@ usage() { echo "Usage:" - echo " $(basename $0) [-h|-v] ..." + echo " $(basename "$0") [-h|-v] ..." echo "" echo "application-name: valid options are: $(valid_app_options)" echo "-h print this help" @@ -60,9 +60,9 @@ java_heapsize_settings() { set_lib_dir() { - if [ -z ${LIB_DIR} ]; then + if [ -z "${LIB_DIR}" ]; then # Allow for symlinks to this script - if [ -L $0 ]; then + if [ -L "$0" ]; then local script_real_loc=$(readlink "$0") else local script_real_loc=$0 @@ -83,23 +83,25 @@ check_lib_dir() { } function script_exit() { - echo $1 >&2 + echo "$1" >&2 exit 1 } determine_java_version() { local full_ver=$(java -version 2>&1) - # java_ver is eg "18" for java 1.8, "90" for java 9.0, "100" for java 10.0.x - readonly java_ver=$(echo $full_ver | sed -n '{ + # java_ver is eg "80" for java 1.8, "90" for java 9.0, "100" for java 10.0.x + readonly java_ver=$(echo "$full_ver" | sed -n '{ # replace early access versions, e.g. 11-ea with 11.0.0 s/-ea/.0.0/ # replace versions such as 10 with 10.0.0 s/version "\([0-9]\{1,\}\)"/version "\1.0.0"/ + # replace old java versions 1.x.* (java 1.7, java 1.8) with x.* + s/version "1\.\(.*\)"/version "\1"/ # extract the major and minor parts of the version - s/^.* version "\(.*\)\.\(.*\)\..*".*$/\1\2/p + s/^.* version "\([0-9]\{1,\}\)\.\([0-9]\{1,\}\).*".*$/\1\2/p }') # java_vendor is either java (oracle) or openjdk - readonly java_vendor=$(echo $full_ver | sed -n -e 's/^\(.*\) version .*$/\1/p') + readonly java_vendor=$(echo "$full_ver" | sed -n -e 's/^\(.*\) version .*$/\1/p') } jre_specific_vm_options() { @@ -197,7 +199,7 @@ case "${APPNAME}" in readonly CLASSNAME="net.sourceforge.pmd.util.treeexport.TreeExportCli" ;; *) - echo "${APPNAME} is NOT a valid application name, valid options are:$(valid_app_options)" + echo "${APPNAME} is NOT a valid application name, valid options are: $(valid_app_options)" ;; esac diff --git a/pmd-dist/src/test/resources/scripts/designertest.bat b/pmd-dist/src/test/resources/scripts/designertest.bat new file mode 100644 index 0000000000..d43a5ff146 --- /dev/null +++ b/pmd-dist/src/test/resources/scripts/designertest.bat @@ -0,0 +1,103 @@ +@echo off + +:: BSD-style license; for more info see http://pmd.sourceforge.net/license.html + +:: +:: Simple manual test script +:: - code is copied from designer.bat to be tested here (so please check, it might be out of sync) +:: - mostly the function "determine_java_version" is tested here +:: - just run it with "designertest.bat" and look at the output +:: - test cases are at the end of this script +:: + +GOTO :main + +:determine_java_version +:: sets the jver variable to the java version, eg 90 for 9.0.1+x or 80 for 1.8.0_171-b11 or 110 for 11.0.6.1 +:: sets the jvendor variable to either java (oracle) or openjdk +for /f tokens^=1^,3^,4^,5^ delims^=.-_+^"^ %%j in (%full_version%) do ( + set jvendor=%%j + if %%l EQU ea ( + set /A "jver=%%k0" + ) else ( + if %%k EQU 1 ( + :: for java version 1.7.x, 1.8.x, ignore the first 1. + set /A "jver=%%l%%m" + ) else ( + set /A "jver=%%k%%l" + ) + ) +) + +set detection= +if %jver% GEQ 70 ( + if %jver% LSS 80 ( + set detection="detected java 7" + ) +) +if [%detection%] == [] ( + if %jver% GEQ 80 ( + if %jver% LSS 90 ( + set detection="detected java 8" + ) + ) +) +if [%detection%] == [] ( + if %jver% GEQ 90 ( + if %jver% LSS 110 ( + if %jvendor% == java ( + set detection="detected java 9 or 10 from oracle" + ) + ) + ) +) +if [%detection%] == [] ( + if %jvendor% == openjdk ( + set detection="detected java 11 from oracle or any openjdk" + ) +) +if [%detection%] == [] ( + if %jvendor% == java ( + if %jver% GEQ 110 ( + set detection="detected java 11 from oracle or any openjdk" + ) + ) +) + +EXIT /B + + + +:run_test +set full_version=%1 +set expected_vendor=%2 +set expected_version=%3 +set expected_detection=%4 + +CALL :determine_java_version + +echo full_version: %full_version% +if %jver% == %expected_version% ( echo jver: %jver% OK ) ELSE ( echo jver: %jver% EXPECTED: %expected_version%  ) +if %jvendor% == %expected_vendor% ( echo jvendor: %jvendor% OK ) ELSE ( echo jvendor: %jvendor% EXPECTED: %expected_vendor%  ) +if [%detection%] == [%expected_detection%] ( echo detection: %detection% OK ) ELSE ( echo detection: %detection% EXPECTED: %expected_detection%  ) +echo. + +EXIT /B + +:main + +CALL :run_test "java version ""1.7.0_80""" java 70 "detected java 7" +CALL :run_test "openjdk version ""1.7.0_352""" openjdk 70 "detected java 7" +CALL :run_test "java version ""1.8.0_271""" java 80 "detected java 8" +CALL :run_test "openjdk version ""1.8.0_345""" openjdk 80 "detected java 8" +CALL :run_test "java version ""9.0.4""" java 90 "detected java 9 or 10 from oracle" +CALL :run_test "openjdk version ""9.0.4""" openjdk 90 "detected java 11 from oracle or any openjdk" +CALL :run_test "java version ""10.0.2"" 2018-07-17" java 100 "detected java 9 or 10 from oracle" +CALL :run_test "openjdk version ""11.0.6"" 2022-08-12" openjdk 110 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""11.0.6.1"" 2022-08-12" openjdk 110 "detected java 11 from oracle or any openjdk" +CALL :run_test "java version ""11.0.13"" 2021-10-19 LTS" java 110 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""17.0.4"" 2022-08-12" openjdk 170 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""17.1.4"" 2022-08-12" openjdk 171 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""17.0.4.1"" 2022-08-12" openjdk 170 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""18.0.2.1"" 2022-08-18" openjdk 180 "detected java 11 from oracle or any openjdk" +CALL :run_test "openjdk version ""19-ea"" 2022-09-20" openjdk 190 "detected java 11 from oracle or any openjdk" diff --git a/pmd-dist/src/test/resources/scripts/runtest.sh b/pmd-dist/src/test/resources/scripts/runtest.sh new file mode 100755 index 0000000000..34185ddb1c --- /dev/null +++ b/pmd-dist/src/test/resources/scripts/runtest.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# BSD-style license; for more info see http://pmd.sourceforge.net/license.html + +# +# Simple manual test script +# - code is copied from run.sh to be tested here (so please check, it might be out of sync) +# - mostly the function "determine_java_version" is tested here +# - just run it with "./runtest.sh" and look at the output +# - test cases are at the end of this script +# + +export LANG=en_US.UTF-8 + +FULL_JAVA_VERSION="" + +get_full_java_version() { + #java -version 2>&1 + #echo "openjdk version \"11.0.6\" 2022-08-12" + echo "$FULL_JAVA_VERSION" +} + +determine_java_version() { + local full_ver=$(get_full_java_version) + # java_ver is eg "80" for java 1.8, "90" for java 9.0, "100" for java 10.0.x + java_ver=$(echo "$full_ver" | sed -n '{ + # replace early access versions, e.g. 11-ea with 11.0.0 + s/-ea/.0.0/ + # replace versions such as 10 with 10.0.0 + s/version "\([0-9]\{1,\}\)"/version "\1.0.0"/ + # replace old java versions 1.x.* (java 1.7, java 1.8) with x.* + s/version "1\.\(.*\)"/version "\1"/ + # extract the major and minor parts of the version + s/^.* version "\([0-9]\{1,\}\)\.\([0-9]\{1,\}\).*".*$/\1\2/p + }') + # java_vendor is either java (oracle) or openjdk + java_vendor=$(echo "$full_ver" | sed -n -e 's/^\(.*\) version .*$/\1/p') +} + +jre_specific_vm_options() { + options="" + if [ "$java_ver" -ge 70 ] && [ "$java_ver" -lt 80 ] + then + options="detected java 7" + elif [ "$java_ver" -ge 80 ] && [ "$java_ver" -lt 90 ] + then + options="detected java 8" + elif [ "$java_ver" -ge 90 ] && [ "$java_ver" -lt 110 ] && [ "$java_vendor" = "java" ] + then + options="detected java 9 or 10 from oracle" + elif [ "$java_vendor" = "openjdk" ] || ( [ "$java_vendor" = "java" ] && [ "$java_ver" -ge 110 ] ) + then + options="detected java 11 from oracle or any openjdk" + fi + echo $options +} + +run_test() { + FULL_JAVA_VERSION="$1" + EXPECTED_VENDOR="$2" + EXPECTED_VER="$3" + EXPECTED="$4" + echo "Testing: '${FULL_JAVA_VERSION}'" + determine_java_version + java_opts="$(jre_specific_vm_options)" + echo -n "java_ver: $java_ver " + if [ "$EXPECTED_VER" = "$java_ver" ]; then echo -e "\e[32mOK\e[0m"; else echo -e "\e[31mFAILED\e[0m"; fi + echo -n "java_vendor: $java_vendor " + if [ "$EXPECTED_VENDOR" = "$java_vendor" ]; then echo -e "\e[32mOK\e[0m"; else echo -e "\e[31mFAILED\e[0m"; fi + echo -n "java_opts: $java_opts " + if [ "$EXPECTED" = "$java_opts" ]; then echo -e "\e[32mOK\e[0m"; else echo -e "\e[31mFAILED\e[0m - expected: ${EXPECTED}"; fi + echo +} + +run_test "java version \"1.7.0_80\"" "java" "70" "detected java 7" +run_test "openjdk version \"1.7.0_352\"" "openjdk" "70" "detected java 7" +run_test "java version \"1.8.0_271\"" "java" "80" "detected java 8" +run_test "openjdk version \"1.8.0_345\"" "openjdk" "80" "detected java 8" +run_test "java version \"9.0.4\"" "java" "90" "detected java 9 or 10 from oracle" +run_test "openjdk version \"9.0.4\"" "openjdk" "90" "detected java 11 from oracle or any openjdk" +run_test "java version \"10.0.2\" 2018-07-17" "java" "100" "detected java 9 or 10 from oracle" +run_test "openjdk version \"11.0.6\" 2022-08-12" "openjdk" "110" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"11.0.6.1\" 2022-08-12" "openjdk" "110" "detected java 11 from oracle or any openjdk" +run_test "java version \"11.0.13\" 2021-10-19 LTS" "java" "110" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"17.0.4\" 2022-08-12" "openjdk" "170" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"17.1.4\" 2022-08-12" "openjdk" "171" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"17.0.4.1\" 2022-08-12" "openjdk" "170" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"18.0.2.1\" 2022-08-18" "openjdk" "180" "detected java 11 from oracle or any openjdk" +run_test "openjdk version \"19-ea\" 2022-09-20" "openjdk" "190" "detected java 11 from oracle or any openjdk" diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index c215c8b406..3dfcfc73d1 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index d6017d7530..5cba970f11 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-gherkin/pom.xml b/pmd-gherkin/pom.xml index c134fc08fa..415023d340 100644 --- a/pmd-gherkin/pom.xml +++ b/pmd-gherkin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index 387bbcf234..3138dc39ba 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index 3c72a3acfc..20166cfb4f 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-html/pom.xml b/pmd-html/pom.xml index bbd5c0a746..bf4ee5e693 100644 --- a/pmd-html/pom.xml +++ b/pmd-html/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml @@ -31,7 +31,7 @@ org.jsoup jsoup - 1.14.3 + 1.15.3 diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 1999933e18..f711a40ab8 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java index 06133f4bf4..009bc3f486 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java @@ -4,11 +4,14 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; +import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty; + import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.logging.Logger; +import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -22,7 +25,7 @@ import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.Annotatable; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractLombokAwareRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; @@ -30,8 +33,17 @@ import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { +public class UnusedPrivateFieldRule extends AbstractJavaRule { + private static final Logger LOG = Logger.getLogger(UnusedPrivateFieldRule.class.getName()); + + private static final PropertyDescriptor> IGNORED_ANNOTATIONS_DESCRIPTOR + = stringListProperty("ignoredAnnotations") + .desc("deprecated! Fully qualified names of the annotation types that should be ignored by this rule. " + + "This property has been deprecated since PMD 6.50.0 and will be completely ignored.") + .defaultValue(new ArrayList()) + .build(); + private static boolean warnedAboutDeprecatedIgnoredAnnotationsProperty = false; private static final PropertyDescriptor> IGNORED_FIELD_NAMES = PropertyFactory.stringListProperty("ignoredFieldNames") .defaultValues("serialVersionUID", "serialPersistentFields") @@ -39,31 +51,25 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { .build(); public UnusedPrivateFieldRule() { + definePropertyDescriptor(IGNORED_ANNOTATIONS_DESCRIPTOR); definePropertyDescriptor(IGNORED_FIELD_NAMES); } @Override - protected Collection defaultSuppressionAnnotations() { - Collection defaultValues = new ArrayList<>(super.defaultSuppressionAnnotations()); - defaultValues.add("java.lang.Deprecated"); - defaultValues.add("javafx.fxml.FXML"); - defaultValues.add("lombok.experimental.Delegate"); - defaultValues.add("lombok.EqualsAndHashCode"); - defaultValues.add("javax.persistence.Id"); - defaultValues.add("javax.persistence.EmbeddedId"); - defaultValues.add("javax.persistence.Version"); - defaultValues.add("jakarta.persistence.Id"); - defaultValues.add("jakarta.persistence.EmbeddedId"); - defaultValues.add("jakarta.persistence.Version"); - defaultValues.add("org.mockito.Mock"); - defaultValues.add("org.mockito.Spy"); - defaultValues.add("org.springframework.boot.test.mock.mockito.MockBean"); - return defaultValues; + public String dysfunctionReason() { + List> overriddenPropertyDescriptors = getOverriddenPropertyDescriptors(); + if (!warnedAboutDeprecatedIgnoredAnnotationsProperty && overriddenPropertyDescriptors.contains(IGNORED_ANNOTATIONS_DESCRIPTOR)) { + LOG.warning("The property '" + IGNORED_ANNOTATIONS_DESCRIPTOR.name() + "' for rule '" + + this.getName() + "' is deprecated. The value is being ignored and the property will " + + "be removed in PMD " + PMDVersion.getNextMajorRelease()); + warnedAboutDeprecatedIgnoredAnnotationsProperty = true; + } + return super.dysfunctionReason(); } @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (hasIgnoredAnnotation(node)) { + if (hasAnyAnnotation(node)) { return super.visit(node, data); } @@ -74,7 +80,7 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { AccessNode accessNodeParent = decl.getAccessNodeParent(); if (!accessNodeParent.isPrivate() || isOK(decl.getImage()) - || hasIgnoredAnnotation((Annotatable) accessNodeParent)) { + || hasAnyAnnotation((Annotatable) accessNodeParent)) { continue; } if (!actuallyUsed(entry.getValue())) { @@ -86,6 +92,10 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { return super.visit(node, data); } + private boolean hasAnyAnnotation(Annotatable node) { + return !node.getDeclaredAnnotations().isEmpty(); + } + private boolean usedInOuterEnum(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) { List outerEnums = node.getParentsOfType(ASTEnumDeclaration.class); for (ASTEnumDeclaration outerEnum : outerEnums) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/CommentDefaultAccessModifierRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/CommentDefaultAccessModifierRule.java index 2891b13843..f6f9fb0fbc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/CommentDefaultAccessModifierRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/CommentDefaultAccessModifierRule.java @@ -57,6 +57,15 @@ public class CommentDefaultAccessModifierRule extends AbstractIgnoredAnnotationR Collection ignoredStrings = new ArrayList<>(); ignoredStrings.add("com.google.common.annotations.VisibleForTesting"); ignoredStrings.add("android.support.annotation.VisibleForTesting"); + ignoredStrings.add("org.junit.jupiter.api.Test"); + ignoredStrings.add("org.junit.jupiter.api.ParameterizedTest"); + ignoredStrings.add("org.junit.jupiter.api.RepeatedTest"); + ignoredStrings.add("org.junit.jupiter.api.TestFactory"); + ignoredStrings.add("org.junit.jupiter.api.TestTemplate"); + ignoredStrings.add("org.junit.jupiter.api.BeforeEach"); + ignoredStrings.add("org.junit.jupiter.api.BeforeAll"); + ignoredStrings.add("org.junit.jupiter.api.AfterEach"); + ignoredStrings.add("org.junit.jupiter.api.AfterAll"); return ignoredStrings; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java index 32bf8fa21c..8091d944e3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java @@ -17,7 +17,9 @@ import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNameList; @@ -186,9 +188,12 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule { if (matches.isEmpty()) { if (isJavaLangImplicit(node)) { - addViolation(data, node, new Object[] { node.getImage(), "java.lang.*", "implicit "}); + asCtx(data).addViolation(node, + node.getImage(), "java.lang.*", "implicit "); } else if (isSamePackage(node, name)) { - addViolation(data, node, new Object[] { node.getImage(), currentPackage + ".*", "same package "}); + if (!hasSameSimpleNameInScope(node)) { + asCtx(data).addViolation(node, node.getImage(), currentPackage + ".*", "same package "); + } } } else { ASTImportDeclaration firstMatch = findFirstMatch(matches); @@ -199,11 +204,29 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule { String importStr = firstMatch.getImportedName() + (firstMatch.isImportOnDemand() ? ".*" : ""); String type = firstMatch.isStatic() ? "static " : ""; - addViolation(data, node, new Object[] { node.getImage(), importStr, type }); + asCtx(data).addViolation(node, node.getImage(), importStr, type); } } } + private boolean hasSameSimpleNameInScope(TypeNode node) { + final ASTCompilationUnit root = node.getRoot(); + final List declarationDescendants = root.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); + final Class nodeType = node.getType(); + + if (nodeType == null) { + return false; + } + + for (ASTClassOrInterfaceDeclaration declarationDescendant : declarationDescendants) { + if (nodeType.getSimpleName().equals(declarationDescendant.getSimpleName()) + && !nodeType.getName().equals(declarationDescendant.getQualifiedName().toString())) { + return true; + } + } + return false; + } + private ASTImportDeclaration findFirstMatch(List imports) { // first search only static imports ASTImportDeclaration result = null; @@ -404,7 +427,7 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule { // Is it a conflict with a class in the same file? final Set qualifiedTypes = node.getScope().getEnclosingScope(SourceFileScope.class) - .getQualifiedTypeNames().keySet(); + .getQualifiedTypeNames().keySet(); for (final String qualified : qualifiedTypes) { int fullLength = qualified.length(); if (qualified.endsWith(unqualifiedName) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java index 740842f9fc..531932a3a9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java @@ -7,17 +7,20 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.StringTokenizer; import java.util.TreeMap; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; @@ -32,12 +35,14 @@ import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; import net.sourceforge.pmd.lang.java.ast.ASTType; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; /** * Searches through all methods and constructors called from constructors. It @@ -178,11 +183,11 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul private List referenceNames; private List qualifierNames; private int argumentSize; - private List argumentTypes; + private List> argumentTypes; private boolean superCall; private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, - String name, int argumentSize, List argumentTypes, boolean superCall) { + String name, int argumentSize, List> argumentTypes, boolean superCall) { this.ape = ape; this.qualifierNames = qualifierNames; this.referenceNames = referenceNames; @@ -204,7 +209,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul return argumentSize; } - public List getArgumentTypes() { + public List> getArgumentTypes() { return argumentTypes; } @@ -220,6 +225,15 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul return ape; } + public boolean matches(ASTMethodDeclaration methodDeclaration) { + String methName = methodDeclaration.getName(); + int count = methodDeclaration.getArity(); + List> parameterTypes = getMethodDeclaratorParameterTypes(methodDeclaration); + return methName.equals(getName()) + && getArgumentCount() == count + && compareParameterAndArgumentTypes(parameterTypes, getArgumentTypes()); + } + public static MethodInvocation getMethod(ASTPrimaryExpression node) { MethodInvocation meth = null; int i = node.getNumChildren(); @@ -237,7 +251,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul String methodName = null; ASTArguments args = (ASTArguments) lastNode.getChild(0); int numOfArguments = args.size(); - List argumentTypes = ConstructorCallsOverridableMethodRule.getArgumentTypes(args); + List> argumentTypes = ConstructorCallsOverridableMethodRule.getArgumentTypes(args); boolean superFirst = false; int thisIndex = -1; @@ -274,21 +288,18 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul // check prefix type match ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child; if (getNameFromPrefix(child2) == null) { - if (child2.getImage() == null) { + if (child2.usesThisModifier()) { thisIndex = x; break; - } else { - // happens when super is used - // [super.method(): image = 'method'] + } else if (child2.usesSuperModifier()) { superFirst = true; thisIndex = x; - // the true super is at an unusable - // index because super.method() has only - // 2 nodes [method=0,()=1] - // as opposed to the 3 you might expect - // and which this.method() actually has. - // [this=0,method=1.()=2] break; + } else if (child2.getFirstChildOfType(ASTAllocationExpression.class) != null) { + // change of scope - the method call is neither on this or super, + // but on a different instance. + // ignore this method call + return null; } } } @@ -315,7 +326,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul String name = child.getImage(); // special case if (i == 2) { // last named node = method name methodName = name; - } else { + } else if (name != null) { // not the last named node so its only // var name varNames.add(name); @@ -384,8 +395,11 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul String toParse = getNameFromPrefix(child); // System.out.println("parsing for var names in : " // + toParse); - java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, "."); - while (st.hasMoreTokens()) { + StringTokenizer st = null; + if (toParse != null) { + st = new StringTokenizer(toParse, "."); + } + while (st != null && st.hasMoreTokens()) { String value = st.nextToken(); if (!st.hasMoreTokens()) { if (i == 2) { @@ -450,7 +464,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul private ASTExplicitConstructorInvocation eci; private String name; private int count = 0; - private List argumentTypes = new ArrayList<>(); + private List> argumentTypes = new ArrayList<>(); ConstructorInvocation(ASTExplicitConstructorInvocation eci) { this.eci = eci; @@ -471,30 +485,42 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul return count; } - public List getArgumentTypes() { + public List> getArgumentTypes() { return argumentTypes; } public String getName() { return name; } + + public boolean matches(ASTConstructorDeclaration constructorDeclaration) { + int matchConstArgCount = constructorDeclaration.getArity(); + List> parameterTypes = getMethodDeclaratorParameterTypes(constructorDeclaration); + return matchConstArgCount == getArgumentCount() && compareParameterAndArgumentTypes(parameterTypes, getArgumentTypes()); + } } private static final class MethodHolder { private ASTMethodDeclaration amd; private boolean dangerous; - private String called; + private Deque callStack = new LinkedList<>(); MethodHolder(ASTMethodDeclaration amd) { this.amd = amd; } - public void setCalledMethod(String name) { - this.called = name; + public void addToStack(String name) { + callStack.push(name); } - public String getCalled() { - return this.called; + public void addToStack(Deque otherStack) { + for (String name : otherStack) { + callStack.addLast(name); + } + } + + public Deque getCallStack() { + return callStack; } public ASTMethodDeclaration getASTMethodDeclaration() { @@ -675,13 +701,15 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul for (MethodInvocation meth : getCurrentEvalPackage().calledMethods) { // check against each dangerous method in class for (MethodHolder h : getCurrentEvalPackage().allMethodsOfClass.keySet()) { - if (h.isDangerous()) { - String methName = h.getASTMethodDeclaration().getName(); - int count = h.getASTMethodDeclaration().getArity(); - List parameterTypes = getMethodDeclaratorParameterTypes(h.getASTMethodDeclaration()); - if (methName.equals(meth.getName()) && meth.getArgumentCount() == count - && parameterTypes.equals(meth.getArgumentTypes())) { - addViolation(data, meth.getASTPrimaryExpression(), "method '" + h.getCalled() + "'"); + if (h.isDangerous() && meth.matches(h.getASTMethodDeclaration())) { + Deque completeCallStack = new LinkedList<>(h.getCallStack()); + completeCallStack.addFirst(meth.getName()); + String overridableMethod = completeCallStack.getLast(); + if (completeCallStack.size() > 1) { + asCtx(data).addViolation(meth.getASTPrimaryExpression(), "method '" + overridableMethod + "'", + " (call stack: " + completeCallStack + ")"); + } else { + asCtx(data).addViolation(meth.getASTPrimaryExpression(), "method '" + overridableMethod + "'", ""); } } } @@ -700,7 +728,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul for (ConstructorInvocation ci : getCurrentEvalPackage().calledConstructors) { if (ci.getArgumentCount() == paramCount) { // match name super / this !? - addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor"); + asCtx(data).addViolation(ci.getASTExplicitConstructorInvocation(), "constructor", ""); } } } @@ -739,19 +767,14 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul // System.out.println("Called meth is " + meth); for (MethodHolder h3 : classMethodMap.keySet()) { // need to skip self here h == h3 - if (h3.isDangerous()) { - String matchMethodName = h3.getASTMethodDeclaration().getName(); - int matchMethodParamCount = h3.getASTMethodDeclaration().getArity(); - List parameterTypes = getMethodDeclaratorParameterTypes(h3.getASTMethodDeclaration()); + if (h3.isDangerous() && meth.matches(h3.getASTMethodDeclaration())) { // System.out.println("matching " + matchMethodName + " // to " + meth.getName()); - if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount() - && parameterTypes.equals(meth.getArgumentTypes())) { - h.setDangerous(); - h.setCalledMethod(matchMethodName); - found = true; - break; - } + h.setDangerous(); + h.addToStack(h3.getCallStack()); + h.addToStack(meth.getName()); + found = true; + break; } } } @@ -783,24 +806,16 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul // but were never evaluated, // they need reevaluation MethodInvocation meth = calledMethsIter.next(); // CCE - String methName = meth.getName(); - int methArgCount = meth.getArgumentCount(); // check each of the already evaluated methods: need to // optimize this out for (MethodHolder h : evaluatedMethods) { - if (h.isDangerous()) { - String matchName = h.getASTMethodDeclaration().getName(); - int matchParamCount = h.getASTMethodDeclaration().getArity(); - List parameterTypes = getMethodDeclaratorParameterTypes(h.getASTMethodDeclaration()); - if (methName.equals(matchName) && methArgCount == matchParamCount - && parameterTypes.equals(meth.getArgumentTypes())) { - ch.setDangerous(true); - // System.out.println("evaluateDangerOfConstructors1 - // setting dangerous constructor with " + - // ch.getASTConstructorDeclaration().getParameterCount() - // + " params"); - break; - } + if (h.isDangerous() && meth.matches(h.getASTMethodDeclaration())) { + ch.setDangerous(true); + // System.out.println("evaluateDangerOfConstructors1 + // setting dangerous constructor with " + + // ch.getASTConstructorDeclaration().getParameterCount() + // + " params"); + break; } } } @@ -827,23 +842,18 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul } // if its not dangerous then evaluate if it should be // if it calls dangerous constructor mark it as dangerous - int cCount = calledC.getArgumentCount(); for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter .hasNext() && !ch.isDangerous();) { // forget skipping self because that introduces another // check for each, but only 1 hit ConstructorHolder h2 = innerConstIter.next(); - if (h2.isDangerous()) { - int matchConstArgCount = h2.getASTConstructorDeclaration().getArity(); - List parameterTypes = getMethodDeclaratorParameterTypes(h2.getASTConstructorDeclaration()); - if (matchConstArgCount == cCount && parameterTypes.equals(calledC.getArgumentTypes())) { - ch.setDangerous(true); - found = true; - // System.out.println("evaluateDangerOfConstructors2 - // setting dangerous constructor with " + - // ch.getASTConstructorDeclaration().getParameterCount() - // + " params"); - } + if (h2.isDangerous() && calledC.matches(h2.getASTConstructorDeclaration())) { + ch.setDangerous(true); + found = true; + // System.out.println("evaluateDangerOfConstructors2 + // setting dangerous constructor with " + + // ch.getASTConstructorDeclaration().getParameterCount() + // + " params"); } } } @@ -953,7 +963,6 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul if (!node.isAbstract() && !node.isPrivate() && !node.isStatic() && !node.isFinal()) { // Skip abstract methods, have a separate rule for that h.setDangerous(); // this method is overridable - h.setCalledMethod(node.getName()); } List l = new ArrayList<>(); addCalledMethodsOfNode(node, l, getCurrentEvalPackage().className); @@ -1038,53 +1047,47 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul return name; } - private static List getMethodDeclaratorParameterTypes(ASTMethodOrConstructorDeclaration methodOrConstructorDeclarator) { + private static List> getMethodDeclaratorParameterTypes(ASTMethodOrConstructorDeclaration methodOrConstructorDeclarator) { ASTFormalParameters parameters = methodOrConstructorDeclarator.getFirstDescendantOfType(ASTFormalParameters.class); - List parameterTypes = new ArrayList<>(); + List> parameterTypes = new ArrayList<>(); if (parameters != null) { for (ASTFormalParameter p : parameters) { ASTType type = p.getFirstChildOfType(ASTType.class); - if (type.getChild(0) instanceof ASTPrimitiveType) { - parameterTypes.add(type.getChild(0).getImage()); - } else if (type.getChild(0) instanceof ASTReferenceType) { - parameterTypes.add("ref"); + ASTVariableDeclaratorId varId = p.getFirstChildOfType(ASTVariableDeclaratorId.class); + + JavaTypeDefinition typeDefinition = type.getTypeDefinition(); + if (typeDefinition != null) { + typeDefinition = typeDefinition.withDimensions(varId.getArrayDepth()); + if (p.isVarargs()) { + typeDefinition = typeDefinition.withDimensions(1); + } + parameterTypes.add(typeDefinition.getType()); } else { - parameterTypes.add(""); + parameterTypes.add(null); // unknown type } } } return parameterTypes; } - private static List getArgumentTypes(ASTArguments args) { - List argumentTypes = new ArrayList<>(); + private static List> getArgumentTypes(ASTArguments args) { + List> argumentTypes = new ArrayList<>(); ASTArgumentList argumentList = args.getFirstChildOfType(ASTArgumentList.class); if (argumentList != null) { for (int a = 0; a < argumentList.getNumChildren(); a++) { - Node expression = argumentList.getChild(a); - ASTPrimaryPrefix arg = expression.getFirstDescendantOfType(ASTPrimaryPrefix.class); - String type = ""; - if (arg != null && arg.getNumChildren() > 0) { - if (arg.getChild(0) instanceof ASTLiteral) { - ASTLiteral lit = (ASTLiteral) arg.getChild(0); - if (lit.isCharLiteral()) { - type = "char"; - } else if (lit.isFloatLiteral()) { - type = "float"; - } else if (lit.isIntLiteral()) { - type = "int"; - } else if (lit.isStringLiteral()) { - type = "String"; - } else if (lit.getNumChildren() > 0 && lit.getChild(0) instanceof ASTBooleanLiteral) { - type = "boolean"; - } else if (lit.isDoubleLiteral()) { - type = "double"; - } else if (lit.isLongLiteral()) { - type = "long"; - } - } else if (arg.getChild(0) instanceof ASTName) { - // ASTName n = (ASTName)arg.getChild(0); - type = "ref"; + JavaNode expression = argumentList.getChild(a); + final TypeNode typeNode; + if (expression instanceof TypeNode) { + typeNode = (TypeNode) expression; + } else { + typeNode = expression.getFirstDescendantOfType(ASTPrimaryPrefix.class); + } + + Class type = null; + if (typeNode != null) { + JavaTypeDefinition typeDefinition = typeNode.getTypeDefinition(); + if (typeDefinition != null) { + type = typeDefinition.getType(); } } argumentTypes.add(type); @@ -1092,4 +1095,23 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul } return argumentTypes; } + + private static boolean compareParameterAndArgumentTypes(List> parameters, List> arguments) { + if (parameters.size() != arguments.size()) { + return false; + } + + for (int i = 0; i < parameters.size(); i++) { + Class param = parameters.get(i); + Class argument = arguments.get(i); + + if (param != null && argument != null + && param != argument + && !param.isAssignableFrom(argument)) { + return false; + } + } + + return true; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java index ed2fa306ae..17c8320689 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java @@ -234,7 +234,9 @@ public final class TypeInferenceResolver { for (Variable var : variablesToResolve) { List lowerBounds = getLowerBoundsOf(var, bounds); // TODO: should call incorporation - instantiations.put(var, lub(lowerBounds)); + if (!lowerBounds.isEmpty()) { + instantiations.put(var, lub(lowerBounds)); + } } uninstantiatedVariables.removeAll(variablesToResolve); diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index e865f684e4..9eec8d4fd8 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -19,7 +19,7 @@ Rules which enforce generally accepted best practices. The abstract class does not contain any abstract methods. An abstract class suggests an incomplete implementation, which is to be completed by subclasses implementing the abstract methods. If the class is intended to be used as a base class only (not to be instantiated -directly) a protected constructor can be provided prevent direct instantiation. +directly) a protected constructor can be provided to prevent direct instantiation. 3 @@ -1722,6 +1722,12 @@ public class Foo { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#unusedprivatefield"> Detects when a private field is declared and/or assigned a value, but not used. + +Since PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the +enclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking +or e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis. +Previously these frameworks where explicitly allowed by listing their annotations in the property +"ignoredAnnotations", but that turned out to be prone of false positive for any not explicitly considered framework. 3 diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index f9770a891b..ec8f37cc46 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -436,9 +436,10 @@ public class Γ‰lΓ©phant {} externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#commentdefaultaccessmodifier"> To avoid mistakes if we want that an Annotation, Class, Enum, Method, Constructor or Field have a default access modifier -we must add a comment at the beginning of it's declaration. -By default the comment must be `/* default */` or `/* package */`, if you want another, you have to provide a regular expression. -This rule ignores by default all cases that have a @VisibleForTesting annotation. Use the +we must add a comment at the beginning of its declaration. +By default, the comment must be `/* default */` or `/* package */`, if you want another, you have to provide a regular expression. + +This rule ignores by default all cases that have a `@VisibleForTesting` annotation or any JUnit5 annotation. Use the property "ignoredAnnotations" to customize the recognized annotations. 3 diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index edeecae3fd..0146f66ff0 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -17,7 +17,7 @@ Rules that help you discover design issues. message="No abstract method which means that the keyword is most likely used to prevent instantiation. Use a private or protected constructor instead." externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#abstractclasswithoutanymethod"> -If an abstract class does not provides any methods, it may be acting as a simple data container +If an abstract class does not provide any methods, it may be acting as a simple data container that is not meant to be instantiated. In this case, it is probably better to use a private or protected constructor in order to prevent instantiation than make the class misleadingly abstract. @@ -53,7 +53,7 @@ public abstract class Example { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#avoidcatchinggenericexception"> -Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block +Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block. 3 @@ -836,6 +836,8 @@ in each object at runtime. ] ] /VariableDeclaratorId + [not(@Image = //MethodDeclaration[@Static = false()]//SynchronizedStatement/Expression/PrimaryExpression/ + (PrimaryPrefix/Name|PrimarySuffix[preceding-sibling::PrimaryPrefix[@ThisModifier = true()]])/@Image)] ]]> diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml index 9081295102..8a331af2e6 100644 --- a/pmd-java/src/main/resources/category/java/documentation.xml +++ b/pmd-java/src/main/resources/category/java/documentation.xml @@ -102,7 +102,10 @@ and unintentional empty constructors. [@containsComment = false()] [not(BlockStatement)] [$ignoreExplicitConstructorInvocation = true() or not(ExplicitConstructorInvocation)] - [not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('javax.inject.Inject')])] + [not(../Annotation/MarkerAnnotation/Name[ + pmd-java:typeIs('javax.inject.Inject') + or pmd-java:typeIs('org.springframework.beans.factory.annotation.Autowired') + ])] ]]> diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 4472626154..77c809b50d 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1170,7 +1170,7 @@ boolean x = (y == Double.NaN); diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index d211ac31b4..84a7da762d 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -844,8 +844,8 @@ private String baz() { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#toofewbranchesforaswitchstatement"> Switch statements are intended to be used to support complex branching behaviour. Using a switch for only a few -cases is ill-advised, since switches are not as easy to understand as if-then statements. In these cases use the -if-then statement to increase code readability. +cases is ill-advised, since switches are not as easy to understand as if-else statements. In these cases use the +if-else statement to increase code readability. 3 diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/ClassA.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/ClassA.java new file mode 100644 index 0000000000..cd5f6b9e40 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/ClassA.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.codestyle.unnecessaryfullyqualifiedname; + +/** + * Test case for #4133 + */ +public class ClassA { + public static class Foo implements net.sourceforge.pmd.lang.java.rule.codestyle.unnecessaryfullyqualifiedname.Foo { + + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/Foo.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/Foo.java new file mode 100644 index 0000000000..b3072fac8c --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/unnecessaryfullyqualifiedname/Foo.java @@ -0,0 +1,8 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.codestyle.unnecessaryfullyqualifiedname; + +public interface Foo { +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/AbstractThing.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/AbstractThing.java new file mode 100644 index 0000000000..184216ed41 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/AbstractThing.java @@ -0,0 +1,19 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.constructorcallsoverridablemethod; + +public abstract class AbstractThing implements Thing { + protected AbstractThing(Thing original) { + setName(original.getName()); + } + + @Override + public void setName(String name) { } + + @Override + public String getName() { + return ""; + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/Thing.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/Thing.java new file mode 100644 index 0000000000..70f21878ee --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/constructorcallsoverridablemethod/Thing.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.errorprone.constructorcallsoverridablemethod; + +public interface Thing { + String getName(); + + void setName(String name); +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java index cef6b52fa1..3b8680ccca 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertSame; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; @@ -47,6 +48,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; @@ -1873,9 +1875,20 @@ public class ClassTypeResolverTest { @Test public void testMethodCallExpressionTypes() throws Exception { ASTCompilationUnit cu = java11.parseClass(MethodCallExpressionTypes.class); - ASTPrimaryExpression expr = cu.getFirstDescendantOfType(ASTPrimaryExpression.class); + List methods = cu.findDescendantsOfType(ASTMethodDeclaration.class); + + // objectsToString + ASTPrimaryExpression expr = methods.get(0).getFirstDescendantOfType(ASTPrimaryExpression.class); assertEquals(forClass(String.class), expr.getTypeDefinition()); assertEquals(forClass(Objects.class), expr.getFirstChildOfType(ASTPrimaryPrefix.class).getTypeDefinition()); + + // arraysAsList + expr = methods.get(1).getFirstDescendantOfType(ASTPrimaryExpression.class); + // note: the type parameter is not correct - it should be bound to String and not object + // but that's close enough - import is, that we correctly identified the method call + // and the result of the method call is a List. + assertEquals(forClass(List.class, forClass(Object.class)), expr.getTypeDefinition()); + assertEquals(forClass(Arrays.class), expr.getFirstChildOfType(ASTPrimaryPrefix.class).getTypeDefinition()); } private JavaTypeDefinition getChildTypeDef(Node node, int childIndex) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodCallExpressionTypes.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodCallExpressionTypes.java index a9fdc44da4..bfaf54bd30 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodCallExpressionTypes.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/MethodCallExpressionTypes.java @@ -4,10 +4,16 @@ package net.sourceforge.pmd.typeresolution.testdata; +import java.util.Arrays; +import java.util.Collection; import java.util.Objects; public class MethodCallExpressionTypes { - public void bar() { + public void objectsToString() { Objects.toString(null); } + + public void arraysAsList() { + Collection l = Arrays.asList("a"); + } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml index 39afc069c1..66ad2f040c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml @@ -120,13 +120,6 @@ public class Foo { - - Comprehensive, check for nothing - - 0 - - - Comprehensive, check for IPv4 IPv4 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml index 3574634255..b773a97ba6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml @@ -545,8 +545,7 @@ public class Foo { #907 UnusedPrivateField false-positive with @FXML - 3 - javafx.fxml.FXML - 1 + 0 #1952 [java] UnusedPrivateField not triggering if @Value annotation present - 1 - 6 + 0 #2673 UnusedPrivateField false positive with lombok annotation EqualsAndHashCode - lombok.Getter|lombok.Data - 1 + 0 + + + #4037 false positive with Spring @SpyBean + 0 + + + + + [java] UnusedPrivateField - false positive with Lombok @ToString.Include #4033 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml index 00d81cfa78..3b3c76f2d8 100755 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml @@ -381,6 +381,20 @@ public interface MyInterface { public enum MyEnum { FOO; class MyNestedClass {} +} + ]]> + + + + #3859 [java] CommentDefaultAccessModifier is triggered in JUnit5 method and it was conflicting with rule JUnit5TestShouldBePackagePrivate + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml index f95d7bc81e..8be00145a9 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryFullyQualifiedName.xml @@ -622,6 +622,40 @@ public class UnnecessaryFullyQualifiedName { ]]> + + False positive when same package inner class is referenced (not enum) #4085 + 0 + + + + + Should report fully-qualified name usage of a class in itself #4085 + 1 + 4 + + + #2098 false positive with annotated package 0 @@ -684,4 +718,16 @@ public class SomeClass { } ]]> + + + [java] UnnecessaryFullyQualifiedName - FP for inner class pkg.ClassA.Foo implementing pkg.Foo #4133 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/FinalFieldCouldBeStatic.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/FinalFieldCouldBeStatic.xml index e5f41598a0..4ed2aed5c1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/FinalFieldCouldBeStatic.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/FinalFieldCouldBeStatic.xml @@ -231,4 +231,34 @@ public class Foo { } ]]> + + + [java] FinalFieldCouldBeStatic false positive with non-static synchronized block (regression in 6.48, worked with 6.47) #4090 + 1 + 4 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LoosePackageCoupling.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LoosePackageCoupling.xml index 871ad0543e..7a6a9dba22 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LoosePackageCoupling.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LoosePackageCoupling.xml @@ -25,12 +25,6 @@ public class Foo { } ]]> - - default package: nothing configured, ok - 0 - - - default package: unused package, ok nothing.used @@ -69,12 +63,6 @@ public class Foo { - - some package: nothing configured, ok - 0 - - - some package: unused package, ok nothing.used @@ -115,6 +103,7 @@ public class Foo { bug fix: annotation before package + javax.xml.ws.wsaddressing 0 Test default report level - report 200 - 0 + 1 1 - The method 'bar()' has an NPath complexity of 200, current threshold is 0 + The method 'bar()' has an NPath complexity of 200, current threshold is 1 + + + #4141 UncommentedEmptyConstructor FP when annotated constructor with @Autowired + 0 + + + + + #4141 UncommentedEmptyConstructor FP when annotated constructor with @Autowired + 0 + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ConstructorCallsOverridableMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ConstructorCallsOverridableMethod.xml index f6fc70406f..34590797be 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ConstructorCallsOverridableMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ConstructorCallsOverridableMethod.xml @@ -320,4 +320,275 @@ import java.lang.annotation.*; } ]]> + + [java] ConstructorCallsOverridableMethod should consider method calls with var access #4099 + 9 + 7,14,21,28,35,42,49,56,63 + { + public Foo9(Set arg) { + bar(arg); // should report a warning at this line + } + public void bar(Collection s) {} // base type +} +]]> + + + False positive with public method call on new instance + 2 + 4,5 + + + + NPE when trying to find method name of method call + 0 + + + + + False negative with method call as argument + 1 + 5 + + + + + Clone and finalize overridden #1718 + 6 + 3,4,5,7,8,9 + + + + + False negative with var args and Arrays.asList + 1 + 6 + names) { } +} +]]> + + + + Misleading message for method call chain + 3 + 3,4,5 + + Overridable method 'overridableMethod' called during object construction (call stack: [intermediatePrivateMethod, otherMethod1, otherMethod2, overridableMethod]) + Overridable method 'overridableMethod' called during object construction (call stack: [shorterChain, overridableMethod]) + Overridable method 'otherOverridableMethod' called during object construction (call stack: [differentChain, otherOverridableMethod]) + + + + + + [java] ConstructorCallsOverridableMethod occurs when unused overloaded method is defined #2348 + 0 + + + + + [java] ConstructorCallsOverridableMethod occurs when unused overloaded method is defined #2348 - sample 2 + 0 + + diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index dfbfe23d76..e6d123c011 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index a88e165bd3..773e763b05 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 3ec09898c0..5ebcbaaaa2 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index 26d6620be5..9c91119822 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index 3bb187261e..52520ed9c9 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -12,7 +12,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-lua/pom.xml b/pmd-lua/pom.xml index b93a17f046..ecbe9b67c5 100644 --- a/pmd-lua/pom.xml +++ b/pmd-lua/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4 b/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4 index 59c0b86a60..fde74727ac 100644 --- a/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4 +++ b/pmd-lua/src/main/antlr4/net/sourceforge/pmd/lang/lua/antlr4/Lua.g4 @@ -62,6 +62,7 @@ Tested by Matt Hargett with: - Entire codebase and test suite for neovim v0.7.2: https://github.com/neovim/neovim/tree/v0.7.2 - Entire codebase for World of Warcraft Interface: https://github.com/tomrus88/BlizzardInterfaceCode - Benchmarks and conformance test suite for Luau 0.537: https://github.com/Roblox/luau/tree/0.537 + - Entire Lua codebase for nmap 7.92 : https://github.com/nmap/nmap */ grammar Lua; @@ -71,12 +72,13 @@ chunk ; block - : stat* laststat? + : (stat ';'?)* (laststat ';'?)? ; stat : ';' - | varlist '=' explist + | varlist ASSIGNMENT explist + | var compoundop exp | functioncall | label | 'break' @@ -85,11 +87,12 @@ stat | 'while' exp 'do' block 'end' | 'repeat' block 'until' exp | 'if' exp 'then' block ('elseif' exp 'then' block)* ('else' block)? 'end' - | 'for' NAME '=' exp ',' exp (',' exp)? 'do' block 'end' - | 'for' namelist 'in' explist 'do' block 'end' + | 'for' binding ASSIGNMENT exp ',' exp (',' exp)? 'do' block 'end' + | 'for' bindinglist 'in' explist 'do' block 'end' | 'function' funcname funcbody - | 'local' 'function' NAME funcbody - | 'local' attnamelist ('=' explist)? + | LOCAL 'function' NAME funcbody + | LOCAL bindinglist (ASSIGNMENT explist)? + | ('export')? 'type' NAME ('<' genericTypeParameterList '>')? '=' type ; attnamelist @@ -100,47 +103,50 @@ attrib : ('<' NAME '>')? ; -laststat - : 'return' explist? | 'break' | 'continue' ';'? - ; - label : '::' NAME '::' ; +laststat + // "continue" is a luau addition and actually not a reserved keyword + : 'return' explist? | 'break' | 'continue' + ; + funcname : NAME ('.' NAME)* (':' NAME)? ; -varlist - : var (',' var)* +funcbody + : ('<' genericTypeParameterList '>')? OPEN_PARENS parlist? CLOSE_PARENS (':' '...'? returnType ) block 'end' ; -namelist - : NAME (',' NAME)* +parlist + : bindinglist (',' '...')? + | '...' ; explist : (exp ',')* exp ; -exp - : 'nil' | 'false' | 'true' - | number - | string - | '...' - | functiondef - | prefixexp - | tableconstructor - | exp operatorPower exp - | operatorUnary exp - | exp operatorMulDivMod exp - | exp operatorAddSub exp - | exp operatorStrcat exp - | exp operatorComparison exp - | exp operatorAnd exp - | exp operatorOr exp - | exp operatorBitwise exp +namelist + : NAME (',' NAME)* + ; + +binding + : NAME (':' type ('?')?)? + ; + +bindinglist + : binding (',' bindinglist)? + ; + +var + : (NAME | OPEN_PARENS exp CLOSE_PARENS varSuffix) varSuffix* + ; + +varlist + : var (',' var)* ; prefixexp @@ -151,16 +157,35 @@ functioncall : varOrExp nameAndArgs+ ; -varOrExp - : var | '(' exp ')' +exp + : (asexp | operatorUnary exp) ( binop exp )* ; -var - : (NAME | '(' exp ')' varSuffix) varSuffix* +ifelseexp + : 'if' exp 'then' exp ('elseif' exp 'then' exp)* 'else' exp + ; + +asexp + : simpleexp ('::' type)? + ; + +simpleexp + : NIL | BOOLEAN + | number + | string + | '...' + | 'function' funcbody + | prefixexp + | ifelseexp + | tableconstructor; + +varOrExp + : var + | OPEN_PARENS exp CLOSE_PARENS ; varSuffix - : nameAndArgs* ('[' exp ']' | '.' NAME) + : nameAndArgs* (OPEN_BRACKET exp CLOSE_BRACKET | '.' NAME) ; nameAndArgs @@ -168,23 +193,17 @@ nameAndArgs ; args - : '(' explist? ')' | tableconstructor | string + : OPEN_PARENS explist? CLOSE_PARENS + | tableconstructor + | string ; functiondef : 'function' funcbody ; -funcbody - : '(' parlist? ')' block 'end' - ; - -parlist - : namelist (',' '...')? | '...' - ; - tableconstructor - : '{' fieldlist? '}' + : OPEN_BRACE fieldlist? CLOSE_BRACE ; fieldlist @@ -192,13 +211,35 @@ fieldlist ; field - : '[' exp ']' '=' exp | NAME '=' exp | exp + : OPEN_BRACKET exp CLOSE_BRACKET ASSIGNMENT exp + | NAME ASSIGNMENT exp + | exp ; fieldsep - : ',' | ';' + : ',' + | ';' ; +compoundop + : '+=' + | '-=' + + | '*=' + | '/=' + | '%=' + | '^=' + | '..='; + +binop: operatorAddSub + | operatorMulDivMod + | operatorPower + | operatorStrcat + | operatorComparison + | operatorAnd + | operatorOr + | operatorBitwise; + operatorOr : 'or'; @@ -206,56 +247,183 @@ operatorAnd : 'and'; operatorComparison - : '<' | '>' | '<=' | '>=' | '~=' | '=='; + : '<' + | '>' + | '<=' + | '>=' + | '~=' + | '==' + ; + +ASSIGNMENT + : '=' + ; operatorStrcat : '..'; operatorAddSub - : '+' | '-'; + : '+' + | '-' + ; operatorMulDivMod - : '*' | '/' | '%' | '//'; + : '*' + | '/' + | '%' + | '//' + ; operatorBitwise - : '&' | '|' | '~' | '<<' | '>>'; + : '&' + | '|' + | '~' + | '<<' + | '>>' + ; operatorUnary - : 'not' | '#' | '-' | '~'; + : 'not' + | '#' + | '-' + | '~' + ; operatorPower : '^'; number - : INT | HEX | FLOAT | HEX_FLOAT + : INT + | HEX + | FLOAT + | HEX_FLOAT ; string - : NORMALSTRING | CHARSTRING | LONGSTRING + : NORMAL_STRING + | LONG_STRING + | INTERPOLATED_STRING + ; + +simpleType + : NIL + | singletonType + | NAME ('.' NAME)? ('<' typeParams '>')? + | 'typeof' OPEN_PARENS exp CLOSE_PARENS + | tableType + | functionType + ; + +singletonType + : NORMAL_STRING + | BOOLEAN + ; + +type + : simpleType ('?')? + | type ('|' type) + | type ('&' type) + ; + +genericTypePackParameter + : NAME '...' ('=' (typePack | variadicTypePack | genericTypePack))? + ; + +genericTypeParameterList + : NAME ('=' type)? (',' genericTypeParameterList)? + | genericTypePackParameter (',' genericTypePackParameter)* + ; + +typeList + : type (',' typeList)? | variadicTypePack + ; + +typeParams + : (type | typePack | variadicTypePack | genericTypePack) (',' typeParams)? + ; + +typePack + : OPEN_PARENS (typeList)? CLOSE_PARENS + ; + +genericTypePack + : NAME '...' + ; + +variadicTypePack + : '...' type + ; + +returnType + : type + | typePack + ; + +tableIndexer + : OPEN_BRACKET type CLOSE_BRACKET ':' type + ; + +tableProp + : NAME ':' type + ; + +tablePropOrIndexer + : tableProp + | tableIndexer + ; + +propList + : tablePropOrIndexer (fieldsep tablePropOrIndexer)* fieldsep? + ; + +tableType + : OPEN_BRACE propList CLOSE_BRACE + ; + +functionType + : ('<' genericTypeParameterList '>')? OPEN_PARENS (typeList)? CLOSE_PARENS '->' returnType ; // LEXER +LOCAL + : 'local' + ; + +REQUIRE + : 'require' + ; + +NIL + : 'nil' + ; + +BOOLEAN + : 'true' + | 'false' + ; + NAME : [a-zA-Z_][a-zA-Z_0-9]* ; -NORMALSTRING - : '"' ( EscapeSequence | ~('\\'|'"') )* '"' +NORMAL_STRING + : '"' (~["\\\r\n\u0085\u2028\u2029] | EscapeSequence | '\\\n')* '"' + | '\'' (~['\\\r\n\u0085\u2028\u2029] | EscapeSequence | '\\\n')* '\'' ; -CHARSTRING - : '\'' ( EscapeSequence | ~('\''|'\\') )* '\'' +INTERPOLATED_STRING + : '`' (~[`\\\r\n\u0085\u2028\u2029] | EscapeSequence | '\\\n')* '`' ; -LONGSTRING - : '[' NESTED_STR ']' +LONG_STRING + : OPEN_BRACKET NESTED_STR CLOSE_BRACKET ; fragment NESTED_STR : '=' NESTED_STR '=' - | '[' .*? ']' + | OPEN_BRACKET .*? CLOSE_BRACKET ; INT @@ -278,6 +446,40 @@ HEX_FLOAT | '0' [xX] HexDigit+ HexExponentPart ; +OPEN_BRACE + : '{' + ; + +CLOSE_BRACE + : '}' + ; + +OPEN_BRACKET + : '[' + ; +CLOSE_BRACKET + : ']' + ; + +OPEN_PARENS: + '(' + ; + +CLOSE_PARENS + : ')' + ; + +NL + : '\r\n' | '\r' | '\n' + | '\u0085' // ' + | '\u2028' //'' + | '\u2029' //'' + ; + +COMMA + : ',' + ; + fragment ExponentPart : [eE] [+-]? Digit+ @@ -290,8 +492,8 @@ HexExponentPart fragment EscapeSequence - : '\\' [abfnrtvz"'|$#\\] // World of Warcraft Lua additionally escapes |$# - | '\\' '\r'? '\n' + : '\\' [abfnrtvz"'`|$#\\] // World of Warcraft Lua additionally escapes |$# + | NL | DecimalEscape | HexEscape | UtfEscape @@ -324,6 +526,11 @@ HexDigit : [0-9a-fA-F] ; +fragment +StartingSingleCommentLineInputCharacter + : ~[[\r\n\u0085\u2028\u2029] + ; + fragment SingleLineInputCharacter : ~[\r\n\u0085\u2028\u2029] @@ -334,11 +541,11 @@ COMMENT ; LINE_COMMENT - : '--' SingleLineInputCharacter* -> channel(HIDDEN) + : '--' (NL | StartingSingleCommentLineInputCharacter SingleLineInputCharacter*) -> channel(HIDDEN) ; WS - : [ \t\u000C\r\n]+ -> skip + : [ \n\r\t\u000B\u000C\u0000]+ -> channel(HIDDEN) ; SHEBANG diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java index e2a87ec878..2e485e13b8 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java @@ -4,15 +4,28 @@ package net.sourceforge.pmd.cpd; +import java.util.Properties; + /** * Language implementation for Lua */ public class LuaLanguage extends AbstractLanguage { + public LuaLanguage() { + this(System.getProperties()); + } + /** * Creates a new Lua Language instance. */ - public LuaLanguage() { + public LuaLanguage(Properties properties) { super("Lua", "lua", new LuaTokenizer(), ".lua"); + setProperties(properties); + } + + @Override + public final void setProperties(Properties properties) { + LuaTokenizer tokenizer = (LuaTokenizer) getTokenizer(); + tokenizer.setProperties(properties); } } diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java index 23c292dbe7..328c5e8ab0 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java @@ -4,8 +4,11 @@ package net.sourceforge.pmd.cpd; +import java.util.Properties; + import org.antlr.v4.runtime.CharStream; +import net.sourceforge.pmd.cpd.token.AntlrToken; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.lang.antlr.AntlrTokenManager; import net.sourceforge.pmd.lang.lua.antlr4.LuaLexer; @@ -15,6 +18,22 @@ import net.sourceforge.pmd.lang.lua.antlr4.LuaLexer; */ public class LuaTokenizer extends AntlrTokenizer { + private boolean ignoreLiteralSequences = false; + + /** + * Sets the possible options for the Lua tokenizer. + * + * @param properties the properties + * @see #OPTION_IGNORE_LITERAL_SEQUENCES + */ + public void setProperties(Properties properties) { + ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES); + } + + private boolean getBooleanProperty(final Properties properties, final String property) { + return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString())); + } + @Override protected AntlrTokenManager getLexerForSource(SourceCode sourceCode) { CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode); @@ -23,6 +42,151 @@ public class LuaTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new AntlrTokenFilter(tokenManager); + return new LuaTokenFilter(tokenManager, ignoreLiteralSequences); + } + + /** + * The {@link LuaTokenFilter} extends the {@link AntlrTokenFilter} to discard + * Lua-specific tokens. + *

+ * By default, it discards semicolons, require statements, and + * enables annotation-based CPD suppression. + *

+ */ + private static class LuaTokenFilter extends AntlrTokenFilter { + + private final boolean ignoreLiteralSequences; + private boolean discardingRequires = false; + private boolean discardingNL = false; + private AntlrToken discardingLiteralsUntil = null; + private boolean discardCurrent = false; + + + LuaTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreLiteralSequences) { + super(tokenManager); + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + + @Override + protected void analyzeToken(final AntlrToken currentToken) { + skipNewLines(currentToken); + } + + @Override + protected void analyzeTokens(final AntlrToken currentToken, final Iterable remainingTokens) { + discardCurrent = false; + skipRequires(currentToken); + skipLiteralSequences(currentToken, remainingTokens); + } + + private void skipRequires(final AntlrToken currentToken) { + final int type = currentToken.getKind(); + if (type == LuaLexer.REQUIRE) { + discardingRequires = true; + } else if (type == LuaLexer.CLOSE_PARENS && discardingRequires) { + discardingRequires = false; + discardCurrent = true; + } + } + + private void skipNewLines(final AntlrToken currentToken) { + discardingNL = currentToken.getKind() == LuaLexer.NL; + } + + private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { + if (ignoreLiteralSequences) { + final int type = currentToken.getKind(); + if (isDiscardingLiterals()) { + if (currentToken == discardingLiteralsUntil) { // NOPMD - intentional check for reference equality + discardingLiteralsUntil = null; + discardCurrent = true; + } + } else if (type == LuaLexer.OPEN_BRACE + || type == LuaLexer.OPEN_BRACKET + || type == LuaLexer.OPEN_PARENS) { + final AntlrToken finalToken = findEndOfSequenceOfLiterals(remainingTokens); + discardingLiteralsUntil = finalToken; + } + } + } + + private AntlrToken findEndOfSequenceOfLiterals(final Iterable remainingTokens) { + boolean seenLiteral = false; + int braceCount = 0; + int bracketCount = 0; + int parenCount = 0; + for (final AntlrToken token : remainingTokens) { + switch (token.getKind()) { + case LuaLexer.INT: + case LuaLexer.NORMAL_STRING: + case LuaLexer.INTERPOLATED_STRING: + case LuaLexer.LONG_STRING: + case LuaLexer.HEX_FLOAT: + case LuaLexer.HEX: + case LuaLexer.FLOAT: + case LuaLexer.NIL: + case LuaLexer.BOOLEAN: + seenLiteral = true; + break; // can be skipped; continue to the next token + case LuaLexer.COMMA: + break; // can be skipped; continue to the next token + case LuaLexer.NL: + // this helps skip large multi-line data table sequences in Lua + break; // can be skipped; continue to the next token + case LuaLexer.ASSIGNMENT: + // this helps skip large data table sequences in Lua: { ["bob"] = "uncle", ["alice"] = "enby" } + break; // can be skipped; continue to the next token + case LuaLexer.OPEN_BRACE: + braceCount++; + break; // curly braces are allowed, as long as they're balanced + case LuaLexer.CLOSE_BRACE: + braceCount--; + if (braceCount < 0) { + // end of the list in the braces; skip all contents + return seenLiteral ? token : null; + } else { + // curly braces are not yet balanced; continue to the next token + break; + } + case LuaLexer.OPEN_BRACKET: + bracketCount++; + break; // brackets are allowed, as long as they're balanced + case LuaLexer.CLOSE_BRACKET: + bracketCount--; + if (bracketCount < 0) { + // end of the list in the brackets; skip all contents + return seenLiteral ? token : null; + } else { + // brackets are not yet balanced; continue to the next token + break; + } + case LuaLexer.OPEN_PARENS: + parenCount++; + break; // parens are allowed, as long as they're balanced + case LuaLexer.CLOSE_PARENS: + parenCount--; + if (parenCount < 0) { + // end of the list in the parens; skip all contents + return seenLiteral ? token : null; + } else { + // parens are not yet balanced; continue to the next token + break; + } + default: + // some other token than the expected ones; this is not a sequence of literals + return null; + } + } + return null; + } + + public boolean isDiscardingLiterals() { + return discardingLiteralsUntil != null; + } + + @Override + protected boolean isLanguageSpecificDiscarding() { + return discardingRequires || discardingNL || isDiscardingLiterals() || discardCurrent; + } } } diff --git a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java index 76d18a3ffa..9c8705d4b4 100644 --- a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java +++ b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java @@ -39,4 +39,14 @@ public class LuaTokenizerTest extends CpdTextComparisonTest { public void testTabWidth() { doTest("tabWidth"); } + + @Test + public void testLuauTypes() { + doTest("luauTypes"); + } + + @Test + public void testComment() { + doTest("comment"); + } } diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.lua b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.lua new file mode 100644 index 0000000000..d79dc5db81 --- /dev/null +++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.lua @@ -0,0 +1,13 @@ + +-- inline comment ("long comment") +print(1 --[[, 2]]) + +-- line comment ("short comment") +print(1) -- comment + +-- inline comment with multiple lines ("long comment") +print(1 --[[comment line 1 +comment line 2]]) + +-- line comment without any content +print(1) -- diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.txt b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.txt new file mode 100644 index 0000000000..f6f1fd25fd --- /dev/null +++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/comment.txt @@ -0,0 +1,23 @@ + [Image] or [Truncated image[ Bcol Ecol +L3 + [print] 1 5 + [(] 6 6 + [1] 7 7 + [)] 18 18 +L6 + [print] 1 5 + [(] 6 6 + [1] 7 7 + [)] 8 8 +L9 + [print] 1 5 + [(] 6 6 + [1] 7 7 +L10 + [)] 17 17 +L13 + [print] 1 5 + [(] 6 6 + [1] 7 7 + [)] 8 8 +EOF diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.lua b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.lua new file mode 100644 index 0000000000..da4e9ddf80 --- /dev/null +++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.lua @@ -0,0 +1,34 @@ +--!strict +type Array = { T } +local x = 31337 +local _negativeLiteral = -3 +local _negativeVariable = -x +local _notLiteral = not true +local _notVariable = not x +local _length = #{x} +export type Function = (...any) -> T... +local _PlatformService = nil +local game = require(script.Parent.game).default :: any +pcall(function() _PlatformService = game:GetService('PlatformService') end) + +return function (req, ...: boolean): ({[string|number]: T}, string, Function<...any>) + local body = string.format("%s %s\n", req.method, req.path) + local res = { + code = 200, + { "Content-Type", "text/plain" }, + { + "Content-Length", + #body, + ["Auth.Confirm"] = [[θ‡³οΌš%s。]], + + } :: Array, + } :: { [any]: number | Array } + if (req :: any).keepAlive then + local socketType: "Connection" | "Pingback" | "" = "" :: "" + socketType = "Connection" :: "Connection" + res[#res + 1] = { socketType :: string, `\`${req.keepAlive}\`` } + res[#res - 2] = { ... } + end + + return (res :: any) :: { T }, (if req then body else "") :: string, function(...): ...any return ... end +end \ No newline at end of file diff --git a/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.txt b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.txt new file mode 100644 index 0000000000..f1a3934c53 --- /dev/null +++ b/pmd-lua/src/test/resources/net/sourceforge/pmd/lang/lua/cpd/testdata/luauTypes.txt @@ -0,0 +1,302 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [type] 1 4 + [Array] 6 10 + [<] 11 11 + [T] 12 12 + [=] 14 14 + [any] 16 18 + [>] 19 19 + [=] 21 21 + [{] 23 23 + [T] 25 25 + [}] 27 27 +L3 + [local] 1 5 + [x] 7 7 + [=] 9 9 + [31337] 11 15 +L4 + [local] 1 5 + [_negativeLiteral] 7 22 + [=] 24 24 + [-] 26 26 + [3] 27 27 +L5 + [local] 1 5 + [_negativeVariable] 7 23 + [=] 25 25 + [-] 27 27 + [x] 28 28 +L6 + [local] 1 5 + [_notLiteral] 7 17 + [=] 19 19 + [not] 21 23 + [true] 25 28 +L7 + [local] 1 5 + [_notVariable] 7 18 + [=] 20 20 + [not] 22 24 + [x] 26 26 +L8 + [local] 1 5 + [_length] 7 13 + [=] 15 15 + [#] 17 17 + [{] 18 18 + [x] 19 19 + [}] 20 20 +L9 + [export] 1 6 + [type] 8 11 + [Function] 13 20 + [<] 21 21 + [T] 22 22 + [...] 23 25 + [=] 27 27 + [...] 29 31 + [any] 32 34 + [>] 35 35 + [=] 37 37 + [(] 39 39 + [...] 40 42 + [any] 43 45 + [)] 46 46 + [->] 48 49 + [T] 51 51 + [...] 52 54 +L10 + [local] 1 5 + [_PlatformService] 7 22 + [=] 24 24 + [nil] 26 28 +L11 + [local] 1 5 + [game] 7 10 + [=] 12 12 + [.] 41 41 + [default] 42 48 + [::] 50 51 + [any] 53 55 +L12 + [pcall] 1 5 + [(] 6 6 + [function] 7 14 + [(] 15 15 + [)] 16 16 + [_PlatformService] 18 33 + [=] 35 35 + [game] 37 40 + [:] 41 41 + [GetService] 42 51 + [(] 52 52 + ['PlatformService'] 53 69 + [)] 70 70 + [end] 72 74 + [)] 75 75 +L14 + [return] 1 6 + [function] 8 15 + [<] 17 17 + [T] 18 18 + [>] 19 19 + [(] 20 20 + [req] 21 23 + [,] 24 24 + [...] 26 28 + [:] 29 29 + [boolean] 31 37 + [)] 38 38 + [:] 39 39 + [(] 41 41 + [{] 42 42 + [\[] 43 43 + [string] 44 49 + [|] 50 50 + [number] 51 56 + [\]] 57 57 + [:] 58 58 + [T] 60 60 + [}] 61 61 + [,] 62 62 + [string] 64 69 + [,] 70 70 + [Function] 72 79 + [<] 80 80 + [...] 81 83 + [any] 84 86 + [>] 87 87 + [)] 88 88 +L15 + [local] 3 7 + [body] 9 12 + [=] 14 14 + [string] 16 21 + [.] 22 22 + [format] 23 28 + [(] 29 29 + ["%s %s\\n"] 30 38 + [,] 39 39 + [req] 41 43 + [.] 44 44 + [method] 45 50 + [,] 51 51 + [req] 53 55 + [.] 56 56 + [path] 57 60 + [)] 61 61 +L16 + [local] 3 7 + [res] 9 11 + [=] 13 13 + [{] 15 15 +L17 + [code] 5 8 + [=] 10 10 + [200] 12 14 + [,] 15 15 +L18 + [{] 5 5 + ["Content-Type"] 7 20 + [,] 21 21 + ["text/plain"] 23 34 + [}] 36 36 + [,] 37 37 +L19 + [{] 5 5 +L20 + ["Content-Length"] 7 22 + [,] 23 23 +L21 + [#] 7 7 + [body] 8 11 + [,] 12 12 +L22 + [\[] 7 7 + ["Auth.Confirm"] 8 21 + [\]] 22 22 + [=] 24 24 + [\[\[θ‡³οΌš%s。\]\]] 26 34 + [,] 35 35 +L24 + [}] 5 5 + [::] 7 8 + [Array] 10 14 + [<] 15 15 + [any] 16 18 + [>] 19 19 + [,] 20 20 +L25 + [}] 3 3 + [::] 5 6 + [{] 8 8 + [\[] 10 10 + [any] 11 13 + [\]] 14 14 + [:] 15 15 + [number] 17 22 + [|] 24 24 + [Array] 26 30 + [<] 31 31 + [string] 32 37 + [|] 39 39 + [boolean] 41 47 + [>] 48 48 + [}] 50 50 +L26 + [if] 3 4 + [(] 6 6 + [req] 7 9 + [::] 11 12 + [any] 14 16 + [)] 17 17 + [.] 18 18 + [keepAlive] 19 27 + [then] 29 32 +L27 + [local] 5 9 + [socketType] 11 20 + [:] 21 21 + ["Connection"] 23 34 + [|] 36 36 + ["Pingback"] 38 47 + [|] 49 49 + [""] 51 52 + [=] 54 54 + [""] 56 57 + [::] 59 60 + [""] 62 63 +L28 + [socketType] 5 14 + [=] 16 16 + ["Connection"] 18 29 + [::] 31 32 + ["Connection"] 34 45 +L29 + [res] 5 7 + [\[] 8 8 + [#] 9 9 + [res] 10 12 + [+] 14 14 + [1] 16 16 + [\]] 17 17 + [=] 19 19 + [{] 21 21 + [socketType] 23 32 + [::] 34 35 + [string] 37 42 + [,] 43 43 + [`\\`${req.keepAlive}\\``] 45 66 + [}] 68 68 +L30 + [res] 5 7 + [\[] 8 8 + [#] 9 9 + [res] 10 12 + [-] 14 14 + [2] 16 16 + [\]] 17 17 + [=] 19 19 + [{] 21 21 + [...] 23 25 + [}] 27 27 +L31 + [end] 3 5 +L33 + [return] 3 8 + [(] 10 10 + [res] 11 13 + [::] 15 16 + [any] 18 20 + [)] 21 21 + [::] 23 24 + [{] 26 26 + [T] 28 28 + [}] 30 30 + [,] 31 31 + [(] 33 33 + [if] 34 35 + [req] 37 39 + [then] 41 44 + [body] 46 49 + [else] 51 54 + [""] 56 57 + [)] 58 58 + [::] 60 61 + [string] 63 68 + [,] 69 69 + [function] 71 78 + [(] 79 79 + [...] 80 82 + [)] 83 83 + [:] 84 84 + [...] 86 88 + [any] 89 91 + [return] 93 98 + [...] 100 102 + [end] 104 106 +L34 + [end] 1 3 +EOF diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index ba5076d6e9..132b9c6a41 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-modelica/pom.xml b/pmd-modelica/pom.xml index 4d09398f22..5c3be1df60 100644 --- a/pmd-modelica/pom.xml +++ b/pmd-modelica/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 6dd1bbe61f..2783d6817a 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 5342902fd5..5ae65eb5dc 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 983c228b54..f4800eac8c 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index 31e821cd95..707289b8df 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 8e7a6451d9..b8b3759af6 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml index f1c8297b34..f30afe4fa6 100644 --- a/pmd-ruby/pom.xml +++ b/pmd-ruby/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-scala-modules/pmd-scala-common/pom.xml b/pmd-scala-modules/pmd-scala-common/pom.xml index 0997d90d5d..3c1e1c3aa7 100644 --- a/pmd-scala-modules/pmd-scala-common/pom.xml +++ b/pmd-scala-modules/pmd-scala-common/pom.xml @@ -8,12 +8,12 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../../pom.xml - 4.2.0 + 4.6.0 diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/ast/testdata/package.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/ast/testdata/package.txt index 0a5eedd4b5..fc088d5b3e 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/ast/testdata/package.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/ast/testdata/package.txt @@ -11,10 +11,11 @@ +- DefnType | +- TypeName | +- TypeSelect - | +- TermSelect - | | +- TermName - | | +- TermName - | +- TypeName + | | +- TermSelect + | | | +- TermName + | | | +- TermName + | | +- TypeName + | +- TypeBounds +- DefnVal | +- PatVar | | +- TermName @@ -26,10 +27,11 @@ +- DefnType | +- TypeName | +- TypeSelect - | +- TermSelect - | | +- TermName - | | +- TermName - | +- TypeName + | | +- TermSelect + | | | +- TermName + | | | +- TermName + | | +- TypeName + | +- TypeBounds +- DefnVal | +- PatVar | | +- TermName @@ -51,8 +53,9 @@ | | +- TypeName | | +- TypeBounds | +- TypeApply - | +- TypeName - | +- TypeName + | | +- TypeName + | | +- TypeName + | +- TypeBounds +- DefnVal | +- ModAnnot | | +- Init @@ -79,12 +82,13 @@ | +- TypeName | +- TypeBounds +- TypeApply - +- TypeSelect - | +- TermSelect - | | +- TermSelect - | | | +- TermName - | | | +- TermName - | | +- TermName - | +- TypeName - +- TypeName - +- TypeName + | +- TypeSelect + | | +- TermSelect + | | | +- TermSelect + | | | | +- TermName + | | | | +- TermName + | | | +- TermName + | | +- TypeName + | +- TypeName + | +- TypeName + +- TypeBounds diff --git a/pmd-scala-modules/pmd-scala_2.12/pom.xml b/pmd-scala-modules/pmd-scala_2.12/pom.xml index 9d8b80cbbd..dfa1368708 100644 --- a/pmd-scala-modules/pmd-scala_2.12/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.12/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pmd-scala-common/pom.xml @@ -28,7 +28,7 @@ org.scala-lang scala-library - ${scalaVersion}.10 + ${scalaVersion}.17 org.scalameta diff --git a/pmd-scala-modules/pmd-scala_2.13/pom.xml b/pmd-scala-modules/pmd-scala_2.13/pom.xml index ae57caed29..da71270591 100644 --- a/pmd-scala-modules/pmd-scala_2.13/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.13/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pmd-scala-common/pom.xml @@ -28,7 +28,7 @@ org.scala-lang scala-library - ${scalaVersion}.3 + ${scalaVersion}.9 org.scalameta diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml index 5b199fa762..caaa746ba9 100644 --- a/pmd-scala/pom.xml +++ b/pmd-scala/pom.xml @@ -9,7 +9,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml index a363e59183..7632b8a40d 100644 --- a/pmd-swift/pom.xml +++ b/pmd-swift/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-test-schema/pom.xml b/pmd-test-schema/pom.xml index 37e4c2de71..02e6b1e303 100644 --- a/pmd-test-schema/pom.xml +++ b/pmd-test-schema/pom.xml @@ -11,7 +11,7 @@ pmd net.sourceforge.pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml index 6ddeaaff3a..e964fda20e 100644 --- a/pmd-test/pom.xml +++ b/pmd-test/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 7768660bd7..9bd6b0a541 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.xml.sax.InputSource; import net.sourceforge.pmd.PMDConfiguration; @@ -104,6 +105,11 @@ public abstract class RuleTst { } } + String dysfunctionReason = rule.dysfunctionReason(); + if (StringUtils.isNotBlank(dysfunctionReason)) { + throw new RuntimeException("Rule is not configured correctly: " + dysfunctionReason); + } + report = processUsingStringReader(test, rule); res = report.size(); } catch (Exception e) { diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 61c823bda1..27ab62e969 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index 399246c0d5..f2e18a2f8f 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index ecb77817dc..4e51df9ec1 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT ../pom.xml @@ -30,6 +30,7 @@ ${*} + \ diff --git a/pmd-xml/src/main/resources/category/pom/errorprone.xml b/pmd-xml/src/main/resources/category/pom/errorprone.xml index cfedde95c5..a882baa52f 100644 --- a/pmd-xml/src/main/resources/category/pom/errorprone.xml +++ b/pmd-xml/src/main/resources/category/pom/errorprone.xml @@ -67,7 +67,7 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear, externalInfoUrl="${pmd.website.baseurl}/pmd_rules_pom_errorprone.html#projectversionasdependencyversion"> Using that expression in dependency declarations seems like a shortcut, but it can go wrong. -By far the most common problem is the use of ${project.version} in a BOM or parent POM. +By far the most common problem is the use of \${project.version} in a BOM or parent POM. 3 @@ -92,7 +92,7 @@ By far the most common problem is the use of ${project.version} in a BOM or ... ... - ${project.version} + \${project.version} ]]> diff --git a/pom.xml b/pom.xml index 9036d17f18..a6d461d9bb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,17 +3,17 @@ 4.0.0 net.sourceforge.pmd pmd - 6.50.0-SNAPSHOT + 6.51.0-SNAPSHOT pom PMD PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, - Modelica, PLSQL, Apache Velocity, XML, XSL, Scala. + Modelica, PLSQL, Apache Velocity, HTML, XML, XSL, Scala. Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code in - C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, + C/C++, C#, Dart, Fortran, Gherkin, Go, Groovy, HTML, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift and Visualforce. @@ -76,7 +76,7 @@ - 2022-08-31T17:19:27Z + 2022-09-30T13:39:13Z 7 @@ -92,9 +92,9 @@ 5.0 3.0.0-M5 - 9.3 - 3.1.2 - 3.18.0 + 10.3.3 + 3.2.0 + 3.19.0 1.10.12 3.2.0 4.7.2 @@ -407,22 +407,22 @@ net.sourceforge.pmd pmd-core - 6.49.0 + 6.50.0 net.sourceforge.pmd pmd-java - 6.49.0 + 6.50.0 net.sourceforge.pmd pmd-jsp - 6.49.0 + 6.50.0 net.sourceforge.pmd pmd-javascript - 6.49.0 + 6.50.0 @@ -770,7 +770,7 @@ org.yaml snakeyaml - 1.30 + 1.33 @@ -942,11 +942,13 @@ com.google.protobuf protobuf-java - 3.16.1 + 3.16.3