Merge branch 'master' into pr-5125

This commit is contained in:
Andreas Dangel 2024-08-29 10:10:35 +02:00
commit 60a29f08a5
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
95 changed files with 1165 additions and 473 deletions

View File

@ -7699,6 +7699,42 @@
"contributions": [ "contributions": [
"bug" "bug"
] ]
},
{
"login": "rbri",
"name": "RBRi",
"avatar_url": "https://avatars.githubusercontent.com/u/2544132?v=4",
"profile": "https://github.com/rbri",
"contributions": [
"bug"
]
},
{
"login": "jbisotti",
"name": "Jamie Bisotti",
"avatar_url": "https://avatars.githubusercontent.com/u/899712?v=4",
"profile": "https://github.com/jbisotti",
"contributions": [
"bug"
]
},
{
"login": "soloturn",
"name": "soloturn",
"avatar_url": "https://avatars.githubusercontent.com/u/825568?v=4",
"profile": "https://github.com/soloturn",
"contributions": [
"bug"
]
},
{
"login": "schosin",
"name": "schosin",
"avatar_url": "https://avatars.githubusercontent.com/u/1669777?v=4",
"profile": "https://github.com/schosin",
"contributions": [
"bug"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@ -1,13 +1,23 @@
version: 2 version: 2
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
updates: updates:
- package-ecosystem: "maven" - package-ecosystem: "maven"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
target-branch: "master" - package-ecosystem: "bundler"
open-pull-requests-limit: 0 directories:
- "/"
- "/docs"
schedule:
interval: "weekly"
groups:
all-gems:
patterns: [ "*" ]
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
target-branch: "master" groups:
all-actions:
patterns: [ "*" ]

View File

@ -1,9 +1,8 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.6) addressable (2.8.7)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
bigdecimal (3.1.8) bigdecimal (3.1.8)
claide (1.1.0) claide (1.1.0)
claide-plugins (0.9.2) claide-plugins (0.9.2)
@ -11,10 +10,10 @@ GEM
nap nap
open4 (~> 1.3) open4 (~> 1.3)
colored2 (3.1.2) colored2 (3.1.2)
concurrent-ruby (1.2.3) concurrent-ruby (1.3.4)
cork (0.3.0) cork (0.3.0)
colored2 (~> 3.1) colored2 (~> 3.1)
danger (9.4.3) danger (9.5.0)
claide (~> 1.0) claide (~> 1.0)
claide-plugins (>= 0.9.2) claide-plugins (>= 0.9.2)
colored2 (~> 3.1) colored2 (~> 3.1)
@ -24,19 +23,19 @@ GEM
git (~> 1.13) git (~> 1.13)
kramdown (~> 2.3) kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.0) kramdown-parser-gfm (~> 1.0)
no_proxy_fix
octokit (>= 4.0) octokit (>= 4.0)
terminal-table (>= 1, < 4) terminal-table (>= 1, < 4)
differ (0.1.2) differ (0.1.2)
et-orbi (1.2.11) et-orbi (1.2.11)
tzinfo tzinfo
faraday (2.9.0) faraday (2.11.0)
faraday-net_http (>= 2.0, < 3.2) faraday-net_http (>= 2.0, < 3.4)
logger
faraday-http-cache (2.5.1) faraday-http-cache (2.5.1)
faraday (>= 0.8) faraday (>= 0.8)
faraday-net_http (3.1.0) faraday-net_http (3.3.0)
net-http net-http
fugit (1.11.0) fugit (1.11.1)
et-orbi (~> 1, >= 1.2.11) et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4) raabro (~> 1.4)
git (1.19.1) git (1.19.1)
@ -46,16 +45,15 @@ GEM
rexml rexml
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
liquid (5.5.0) liquid (5.5.1)
logger (1.6.0)
logger-colors (1.0.0) logger-colors (1.0.0)
nap (1.1.0) nap (1.1.0)
net-http (0.4.1) net-http (0.4.1)
uri uri
no_proxy_fix (0.1.2) nokogiri (1.16.7-x86_64-linux)
nokogiri (1.16.5-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
octokit (8.1.0) octokit (9.1.0)
base64
faraday (>= 1, < 3) faraday (>= 1, < 3)
sawyer (~> 0.9) sawyer (~> 0.9)
open4 (1.3.4) open4 (1.3.4)
@ -66,13 +64,13 @@ GEM
nokogiri (~> 1.13) nokogiri (~> 1.13)
rufus-scheduler (~> 3.8) rufus-scheduler (~> 3.8)
slop (~> 4.9) slop (~> 4.9)
public_suffix (5.0.5) public_suffix (6.0.1)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.0) racc (1.8.1)
rchardet (1.8.0) rchardet (1.8.0)
rexml (3.2.8) rexml (3.3.6)
strscan (>= 3.0.9) strscan
rouge (4.2.1) rouge (4.3.0)
rufus-scheduler (3.9.1) rufus-scheduler (3.9.1)
fugit (~> 1.1, >= 1.1.6) fugit (~> 1.1, >= 1.1.6)
safe_yaml (1.0.5) safe_yaml (1.0.5)
@ -86,7 +84,7 @@ GEM
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0) unicode-display_width (2.5.0)
uri (0.13.0) uri (0.13.1)
PLATFORMS PLATFORMS
x86_64-linux x86_64-linux

View File

@ -126,21 +126,43 @@ echo "Press enter to continue..."
read -r read -r
# calculating stats for release notes # determine current milestone
MILESTONE_JSON=$(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$RELEASE_VERSION\")")
MILESTONE=$(echo "$MILESTONE_JSON" | jq .number)
# determine dependency updates
DEPENDENCIES_JSON=$(curl -s "https://api.github.com/repos/pmd/pmd/issues?labels=dependencies&state=closed&direction=asc&per_page=50&page=1&milestone=${MILESTONE}")
DEPENDENCIES_COUNT=$(echo "$DEPENDENCIES_JSON" | jq length)
DEPENDENCIES=""
if [ $DEPENDENCIES_COUNT -gt 0 ]; then
DEPENDENCIES=$(
echo "### 📦 Dependency updates"
echo "$DEPENDENCIES_JSON" | jq --raw-output '.[] | "* [#\(.number)](https://github.com/pmd/pmd/issues/\(.number)): \(.title)"'
)
else
DEPENDENCIES=$(
echo "### 📦 Dependency updates"
echo "No dependency updates"
)
fi
# calculating stats for release notes (excluding dependency updates)
STATS_CLOSED_ISSUES=$(echo "$MILESTONE_JSON" | jq .closed_issues)
STATS=$( STATS=$(
echo "### 📈 Stats" echo "### 📈 Stats"
echo "* $(git log pmd_releases/"${LAST_VERSION}"..HEAD --oneline --no-merges |wc -l) commits" echo "* $(git log pmd_releases/"${LAST_VERSION}"..HEAD --oneline --no-merges |wc -l) commits"
echo "* $(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$RELEASE_VERSION\") | .closed_issues") closed tickets & PRs" echo "* $(($STATS_CLOSED_ISSUES - $DEPENDENCIES_COUNT)) closed tickets & PRs"
echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/"${LAST_VERSION}") ) / 86400))" echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/"${LAST_VERSION}") ) / 86400))"
) )
TEMP_RELEASE_NOTES=$(cat docs/pages/release_notes.md) TEMP_RELEASE_NOTES=$(cat docs/pages/release_notes.md)
TEMP_RELEASE_NOTES=${TEMP_RELEASE_NOTES/\{\% endtocmaker \%\}/${STATS//\&/\\\&}$'\n'$'\n'\{\% endtocmaker \%\}} TEMP_RELEASE_NOTES=${TEMP_RELEASE_NOTES/\{\% endtocmaker \%\}/${DEPENDENCIES//\&/\\\&}$'\n'$'\n'${STATS//\&/\\\&}$'\n'$'\n'\{\% endtocmaker \%\}}
echo "${TEMP_RELEASE_NOTES}" > docs/pages/release_notes.md echo "${TEMP_RELEASE_NOTES}" > docs/pages/release_notes.md
echo echo
echo "Updated stats in release notes:" echo "Updated dependencies and stats in release notes:"
echo "$DEPENDENCIES"
echo "$STATS" echo "$STATS"
echo echo
echo "Please verify docs/pages/release_notes.md" echo "Please verify docs/pages/release_notes.md"

View File

@ -1,18 +1,19 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (7.1.3.3) activesupport (7.2.1)
base64 base64
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5) connection_pool (>= 2.2.5)
drb drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1) minitest (>= 5.1)
mutex_m securerandom (>= 0.3)
tzinfo (~> 2.0) tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.6) addressable (2.8.7)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0) base64 (0.2.0)
bigdecimal (3.1.8) bigdecimal (3.1.8)
coffee-script (2.4.1) coffee-script (2.4.1)
@ -21,10 +22,10 @@ GEM
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
colorator (1.1.0) colorator (1.1.0)
commonmarker (0.23.10) commonmarker (0.23.10)
concurrent-ruby (1.2.3) concurrent-ruby (1.3.4)
connection_pool (2.4.1) connection_pool (2.4.1)
csv (3.3.0) csv (3.3.0)
dnsruby (1.72.1) dnsruby (1.72.2)
simpleidn (~> 0.2.1) simpleidn (~> 0.2.1)
drb (2.2.1) drb (2.2.1)
em-websocket (0.5.3) em-websocket (0.5.3)
@ -34,19 +35,20 @@ GEM
ffi (>= 1.15.0) ffi (>= 1.15.0)
eventmachine (1.2.7) eventmachine (1.2.7)
execjs (2.9.1) execjs (2.9.1)
faraday (2.9.0) faraday (2.11.0)
faraday-net_http (>= 2.0, < 3.2) faraday-net_http (>= 2.0, < 3.4)
faraday-net_http (3.1.0) logger
faraday-net_http (3.3.0)
net-http net-http
ffi (1.16.3) ffi (1.17.0-x86_64-linux-gnu)
forwardable-extended (2.6.0) forwardable-extended (2.6.0)
gemoji (4.1.0) gemoji (4.1.0)
github-pages (231) github-pages (232)
github-pages-health-check (= 1.18.2) github-pages-health-check (= 1.18.2)
jekyll (= 3.9.5) jekyll (= 3.10.0)
jekyll-avatar (= 0.8.0) jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2) jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.4.0) jekyll-commonmark-ghpages (= 0.5.1)
jekyll-default-layout (= 0.1.5) jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0) jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0) jekyll-gist (= 1.5.0)
@ -83,9 +85,10 @@ GEM
liquid (= 4.0.4) liquid (= 4.0.4)
mercenary (~> 0.3) mercenary (~> 0.3)
minima (= 2.5.1) minima (= 2.5.1)
nokogiri (>= 1.13.6, < 2.0) nokogiri (>= 1.16.2, < 2.0)
rouge (= 3.30.0) rouge (= 3.30.0)
terminal-table (~> 1.4) terminal-table (~> 1.4)
webrick (~> 1.8)
github-pages-health-check (1.18.2) github-pages-health-check (1.18.2)
addressable (~> 2.3) addressable (~> 2.3)
dnsruby (~> 1.60) dnsruby (~> 1.60)
@ -98,9 +101,10 @@ GEM
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.14.5) i18n (1.14.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (3.9.5) jekyll (3.10.0)
addressable (~> 2.4) addressable (~> 2.4)
colorator (~> 1.0) colorator (~> 1.0)
csv (~> 3.0)
em-websocket (~> 0.5) em-websocket (~> 0.5)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0) jekyll-sass-converter (~> 1.0)
@ -111,6 +115,7 @@ GEM
pathutil (~> 0.9) pathutil (~> 0.9)
rouge (>= 1.7, < 4) rouge (>= 1.7, < 4)
safe_yaml (~> 1.0) safe_yaml (~> 1.0)
webrick (>= 1.0)
jekyll-avatar (0.8.0) jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0) jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.2.2) jekyll-coffeescript (1.2.2)
@ -118,9 +123,9 @@ GEM
coffee-script-source (~> 1.12) coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0) jekyll-commonmark (1.4.0)
commonmarker (~> 0.22) commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.4.0) jekyll-commonmark-ghpages (0.5.1)
commonmarker (~> 0.23.7) commonmarker (>= 0.23.7, < 1.1.0)
jekyll (~> 3.9.0) jekyll (>= 3.9, < 4.0)
jekyll-commonmark (~> 1.4.0) jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0) rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5) jekyll-default-layout (0.1.5)
@ -214,29 +219,29 @@ GEM
listen (3.9.0) listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.0)
mercenary (0.3.6) mercenary (0.3.6)
minima (2.5.1) minima (2.5.1)
jekyll (>= 3.5, < 5.0) jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9) jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1) jekyll-seo-tag (~> 2.1)
minitest (5.23.1) minitest (5.25.1)
mutex_m (0.2.0)
net-http (0.4.1) net-http (0.4.1)
uri uri
nokogiri (1.16.5-x86_64-linux) nokogiri (1.16.7-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
octokit (4.25.1) octokit (4.25.1)
faraday (>= 1, < 3) faraday (>= 1, < 3)
sawyer (~> 0.9) sawyer (~> 0.9)
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (5.0.5) public_suffix (5.1.1)
racc (1.8.0) racc (1.8.1)
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.11.1) rb-inotify (0.11.1)
ffi (~> 1.0) ffi (~> 1.0)
rexml (3.2.8) rexml (3.3.6)
strscan (>= 3.0.9) strscan
rouge (3.30.0) rouge (3.30.0)
rubyzip (2.3.2) rubyzip (2.3.2)
safe_yaml (1.0.5) safe_yaml (1.0.5)
@ -248,6 +253,7 @@ GEM
sawyer (0.9.2) sawyer (0.9.2)
addressable (>= 2.3.5) addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3) faraday (>= 0.17.3, < 3)
securerandom (0.3.1)
simpleidn (0.2.3) simpleidn (0.2.3)
strscan (3.1.0) strscan (3.1.0)
terminal-table (1.8.0) terminal-table (1.8.0)
@ -257,7 +263,7 @@ GEM
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
uri (0.13.0) uri (0.13.1)
webrick (1.8.1) webrick (1.8.1)
PLATFORMS PLATFORMS

View File

@ -1,9 +1,9 @@
repository: pmd/pmd repository: pmd/pmd
pmd: pmd:
version: 7.4.0-SNAPSHOT version: 7.5.0-SNAPSHOT
previous_version: 7.3.0 previous_version: 7.4.0
date: 2024-07-26 date: 2024-08-30
# release types: major, minor, bugfix # release types: major, minor, bugfix
release_type: minor release_type: minor

View File

@ -2,7 +2,7 @@
title: Release process title: Release process
permalink: pmd_projectdocs_committers_releasing.html permalink: pmd_projectdocs_committers_releasing.html
author: Romain Pelisse <rpelisse@users.sourceforge.net>, Andreas Dangel <andreas.dangel@pmd-code.org> author: Romain Pelisse <rpelisse@users.sourceforge.net>, Andreas Dangel <andreas.dangel@pmd-code.org>
last_updated: April 2024 last_updated: July 2024 (7.5.0)
--- ---
This page describes the current status of the release process. This page describes the current status of the release process.
@ -67,8 +67,8 @@ news posts can be changed afterward (although that's an entirely manual process)
You can find the release notes here: `docs/pages/release_notes.md`. You can find the release notes here: `docs/pages/release_notes.md`.
The date (`date +%d-%B-%Y`) and the version (remove the SNAPSHOT) must be updated in `docs/_config.yml`, e.g. The date (`date +%Y-%m-%d`) and the version (remove the SNAPSHOT) must be updated in `docs/_config.yml`, e.g.
in order to release version "6.34.0", the configuration should look like this: in order to release version "7.2.0", the configuration should look like this:
```yaml ```yaml
pmd: pmd:
@ -78,7 +78,7 @@ pmd:
release_type: minor release_type: minor
``` ```
The release type could be one of "bugfix" (e.g. 7.1.x), "minor" (7.x.0), or "major" (x.0.0). The release type could be one of "bugfix" (e.g. 7.2.x), "minor" (7.x.0), or "major" (x.0.0).
The release notes usually mention any new rules that have been added since the last release. The release notes usually mention any new rules that have been added since the last release.
@ -95,25 +95,50 @@ not pmd-cli and pmd-dist.
In case, there is no need for a new pmd-designer version, we could stick to the latest already available version. In case, there is no need for a new pmd-designer version, we could stick to the latest already available version.
Then we can skip the release of pmd-designer and immediately start the second phase of the release. Then we can skip the release of pmd-designer and immediately start the second phase of the release.
Starting with PMD 7.5.0 we use Dependabot to update dependencies. Dependabot will create pull requests
labeled with `dependencies`. When we merge such a pull request, we should assign it to the correct
milestone. It is important, that the due date of the milestone is set correctly, otherwise the query won't find
the milestone number.
Then we can query which PRs have been merged and generate a section for the release notes:
```shell
NEW_VERSION=7.2.0
MILESTONE_JSON=$(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$NEW_VERSION\")")
MILESTONE=$(echo "$MILESTONE_JSON" | jq .number)
# determine dependency updates
DEPENDENCIES_JSON=$(curl -s "https://api.github.com/repos/pmd/pmd/issues?labels=dependencies&state=closed&direction=asc&per_page=50&page=1&milestone=${MILESTONE}")
DEPENDENCIES_COUNT=$(echo "$DEPENDENCIES_JSON" | jq length)
if [ $DEPENDENCIES_COUNT -gt 0 ]; then
echo "### 📦 Dependency updates"
echo "$DEPENDENCIES_JSON" | jq --raw-output '.[] | "* [#\(.number)](https://github.com/pmd/pmd/issues/\(.number)): \(.title)"'
else
echo "### 📦 Dependency updates"
echo "No dependency updates"
fi
```
This section needs to be added to the release notes at the end.
Starting with PMD 6.23.0 we'll provide small statistics for every release. This needs to be added Starting with PMD 6.23.0 we'll provide small statistics for every release. This needs to be added
to the release notes as the last section. To count the closed issues and pull requests, the milestone to the release notes as the last section (after "Dependency updates"). To count the closed issues and pull requests, the milestone
on GitHub with the title of the new release is searched. It is important, that the due date of the milestone on GitHub with the title of the new release is searched. It is important, that the due date of the milestone
is correctly set, as the returned milestones in the API call are sorted by due date. is correctly set, as the returned milestones in the API call are sorted by due date.
Make sure, there is such a milestone on <https://github.com/pmd/pmd/milestones>. The following snippet will Make sure, there is such a milestone on <https://github.com/pmd/pmd/milestones>. The following snippet will
create the numbers, that can be attached to the release notes as a last section: create the numbers, that can be attached to the release notes as a last section. Note: It uses part of the
above code snippet (e.g. NEW_VERSION, MILESTONE, DEPENDENCIES_COUNT):
```shell ```shell
LAST_VERSION=7.1.0 LAST_VERSION=7.1.0
NEW_VERSION=7.2.0
NEW_VERSION_COMMITISH=HEAD NEW_VERSION_COMMITISH=HEAD
STATS_CLOSED_ISSUES=$(echo "$MILESTONE_JSON" | jq .closed_issues)
echo "### Stats" echo "### Stats"
echo "* $(git log pmd_releases/${LAST_VERSION}..${NEW_VERSION_COMMITISH} --oneline --no-merges |wc -l) commits" echo "* $(git log pmd_releases/${LAST_VERSION}..${NEW_VERSION_COMMITISH} --oneline --no-merges |wc -l) commits"
echo "* $(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$NEW_VERSION\") | .closed_issues") closed tickets & PRs" echo "* $(($STATS_CLOSED_ISSUES - $DEPENDENCIES_COUNT)) closed tickets & PRs"
echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/${LAST_VERSION}) ) / 86400))" echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/${LAST_VERSION}) ) / 86400))"
``` ```
Note: this part is also integrated into `do-release.sh`. Note: both shell snippets are also integrated into `do-release.sh`.
Check in all (version) changes to branch master or any other branch, from which the release takes place: Check in all (version) changes to branch master or any other branch, from which the release takes place:

File diff suppressed because it is too large Load Diff

View File

@ -14,59 +14,33 @@ This is a {{ site.pmd.release_type }} release.
### 🚀 New and noteworthy ### 🚀 New and noteworthy
### 🌟 New and changed rules
#### New Rules
* The new Apex rule {%rule apex/performance/AvoidNonRestrictiveQueries %} finds SOQL and SOSL queries without a where
or limit statement. This can quickly cause governor limit exceptions.
#### Changed rules
* {%rule apex/codestyle/ClassNamingConventions %}: Two new properties to configure different patterns
for inner classes and interfaces: `innerClassPattern` and `innerInterfacePattern`.
#### Renamed rules
* {%rule ecmascript/errorprone/InaccurateNumericLiteral %} has been renamed from `InnaccurateNumericLiteral`.
The old rule name still works but is deprecated.
### 🐛 Fixed Issues ### 🐛 Fixed Issues
* apex
* [#5094](https://github.com/pmd/pmd/issues/5094): \[apex] "No adapter exists for type" error message printed to stdout instead of stderr
* apex-bestpractices
* [#5095](https://github.com/pmd/pmd/issues/5095): \[apex] ApexUnitTestShouldNotUseSeeAllDataTrue false negative due to casing (regression in PMD 7)
* apex-codestyle
* [#4800](https://github.com/pmd/pmd/issues/4800): \[apex] ClassNamingConvention: Support naming convention for *inner* classes
* apex-performance * apex-performance
* [#635](https://github.com/pmd/pmd/issues/635): \[apex] New Rule: Avoid soql/sosl queries without a where clause or limit statement * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop: false negative for triggers
* java
* [#5167](https://github.com/pmd/pmd/issues/5167): \[java] java.lang.IllegalArgumentException: \<?\> cannot be a wildcard bound
* java-bestpractices * java-bestpractices
* [#5117](https://github.com/pmd/pmd/issues/5117): \[java] UnusedPrivateMethod for methods annotated with jakarta.annotation.PostConstruct or PreDestroy * [#3602](https://github.com/pmd/pmd/issues/3602): \[java] GuardLogStatement: False positive when compile-time constant is created from external constants
* java-errorprone * [#4731](https://github.com/pmd/pmd/issues/4731): \[java] GuardLogStatement: Documentation is unclear why getters are flagged
* [#1488](https://github.com/pmd/pmd/issues/1488): \[java] MissingStaticMethodInNonInstantiatableClass: False positive with Lombok Builder on Constructor * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] UnusedPrivateMethod: False positive with method calls inside lambda
* javascript-errorprone * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class
* [#2367](https://github.com/pmd/pmd/issues/2367): \[javascript] InnaccurateNumericLiteral is misspelled * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this"
* [#4716](https://github.com/pmd/pmd/issues/4716): \[javascript] InaccurateNumericLiteral with number 259200000 * [#5153](https://github.com/pmd/pmd/issues/5153): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is an array element
* plsql * plsql-bestpractices
* [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair: XPathException for more complex exception handler
* [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO
* [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions
* cli
* [#5120](https://github.com/pmd/pmd/issues/5120): \[cli] Can't start designer under Windows
### 🚨 API Changes ### 🚨 API Changes
* pmd-jsp
* javascript * {%jdoc jsp::lang.jsp.ast.JspParserImpl %} is deprecated now. It should have been package-private
* The old rule name `InnaccurateNumericLiteral` has been deprecated. Use the new name because this is an implementation class that should not be used directly.
{%rule ecmascript/errorprone/InaccurateNumericLiteral %} instead. * pmd-velocity
* {%jdoc velocity::lang.velocity.ast.VtlParserImpl %} is deprecated now. It should have been package-private
because this is an implementation class that should not be used directly.
* pmd-visualforce
* {%jdoc visualforce::lang.visualforce.ast.VfParserImpl %} is deprecated now. It should have been package-private
because this is an implementation class that should not be used directly.
### ✨ External Contributions ### ✨ External Contributions
* [#5048](https://github.com/pmd/pmd/pull/5048): \[apex] Added Inner Classes to Apex Class Naming Conventions Rule - [Justin Stroud](https://github.com/justinstroudbah) (@justinstroudbah / @sgnl-labs)
* [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5107](https://github.com/pmd/pmd/pull/5107): \[doc] Update maven.md - Typo fixed for maven target - [karthikaiyasamy](https://github.com/karthikaiyasamy) (@karthikaiyasamy)
* [#5109](https://github.com/pmd/pmd/pull/5109): \[java] Exclude constructor with lombok.Builder for MissingStaticMethodInNonInstantiatableClass - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85)
* [#5118](https://github.com/pmd/pmd/pull/5118): \[java] FP for UnusedPrivateMethod with Jakarta @<!-- -->PostConstruct/PreDestroy annotations - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85)
* [#5121](https://github.com/pmd/pmd/pull/5121): \[plsql] Fixed issue with missing optional table alias in MERGE usage - [Arjen Duursma](https://github.com/duursma) (@duursma)
{% endtocmaker %} {% endtocmaker %}

View File

@ -5,6 +5,86 @@ permalink: pmd_release_notes_old.html
Previous versions of PMD can be downloaded here: [Releases - pmd/pmd (GitHub)](https://github.com/pmd/pmd/releases) Previous versions of PMD can be downloaded here: [Releases - pmd/pmd (GitHub)](https://github.com/pmd/pmd/releases)
## 26-July-2024 - 7.4.0
The PMD team is pleased to announce PMD 7.4.0.
This is a minor release.
### Table Of Contents
* [🌟 New and changed rules](#new-and-changed-rules)
* [New Rules](#new-rules)
* [Changed rules](#changed-rules)
* [Renamed rules](#renamed-rules)
* [🐛 Fixed Issues](#fixed-issues)
* [🚨 API Changes](#api-changes)
* [✨ External Contributions](#external-contributions)
* [📈 Stats](#stats)
### 🌟 New and changed rules
#### New Rules
* The new Apex rule [`AvoidNonRestrictiveQueries`](https://docs.pmd-code.org/pmd-doc-7.4.0/pmd_rules_apex_performance.html#avoidnonrestrictivequeries) finds SOQL and SOSL queries without a where
or limit statement. This can quickly cause governor limit exceptions.
#### Changed rules
* [`ClassNamingConventions`](https://docs.pmd-code.org/pmd-doc-7.4.0/pmd_rules_apex_codestyle.html#classnamingconventions) (Apex Codestyle): Two new properties to configure different patterns
for inner classes and interfaces: `innerClassPattern` and `innerInterfacePattern`.
#### Renamed rules
* [`InaccurateNumericLiteral`](https://docs.pmd-code.org/pmd-doc-7.4.0/pmd_rules_ecmascript_errorprone.html#inaccuratenumericliteral) (JavaScript Error Prone) has been renamed from `InnaccurateNumericLiteral`.
The old rule name still works but is deprecated.
### 🐛 Fixed Issues
* apex
* [#5094](https://github.com/pmd/pmd/issues/5094): \[apex] "No adapter exists for type" error message printed to stdout instead of stderr
* apex-bestpractices
* [#5095](https://github.com/pmd/pmd/issues/5095): \[apex] ApexUnitTestShouldNotUseSeeAllDataTrue false negative due to casing (regression in PMD 7)
* apex-codestyle
* [#4800](https://github.com/pmd/pmd/issues/4800): \[apex] ClassNamingConvention: Support naming convention for *inner* classes
* apex-performance
* [#635](https://github.com/pmd/pmd/issues/635): \[apex] New Rule: Avoid soql/sosl queries without a where clause or limit statement
* java-bestpractices
* [#5106](https://github.com/pmd/pmd/issues/5106): \[java] AccessorClassGeneration: Node was null for default constructor
* [#5110](https://github.com/pmd/pmd/issues/5110): \[java] UnusedPrivateMethod for method referenced by lombok.Builder.ObtainVia
* [#5117](https://github.com/pmd/pmd/issues/5117): \[java] UnusedPrivateMethod for methods annotated with jakarta.annotation.PostConstruct or PreDestroy
* java-errorprone
* [#1488](https://github.com/pmd/pmd/issues/1488): \[java] MissingStaticMethodInNonInstantiatableClass: False positive with Lombok Builder on Constructor
* javascript-errorprone
* [#2367](https://github.com/pmd/pmd/issues/2367): \[javascript] InnaccurateNumericLiteral is misspelled
* [#4716](https://github.com/pmd/pmd/issues/4716): \[javascript] InaccurateNumericLiteral with number 259200000
* plsql
* [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage
* [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO
* [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions
* [#5133](https://github.com/pmd/pmd/issues/5133): \[plsql] AssertionError: Root of the tree should implement RootNode for a PL/SQL type declaration
* cli
* [#5120](https://github.com/pmd/pmd/issues/5120): \[cli] Can't start designer under Windows
* core
* [#5091](https://github.com/pmd/pmd/issues/5091): \[core] PMD CPD v7.3.0 gives deprecation warning for skipLexicalErrors even when not used
### 🚨 API Changes
* javascript
* The old rule name `InnaccurateNumericLiteral` has been deprecated. Use the new name
[`InaccurateNumericLiteral`](https://docs.pmd-code.org/pmd-doc-7.4.0/pmd_rules_ecmascript_errorprone.html#inaccuratenumericliteral) instead.
### ✨ External Contributions
* [#5048](https://github.com/pmd/pmd/pull/5048): \[apex] Added Inner Classes to Apex Class Naming Conventions Rule - [Justin Stroud](https://github.com/justinstroudbah) (@justinstroudbah / @sgnl-labs)
* [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions - [Arjen Duursma](https://github.com/duursma) (@duursma)
* [#5107](https://github.com/pmd/pmd/pull/5107): \[doc] Update maven.md - Typo fixed for maven target - [karthikaiyasamy](https://github.com/karthikaiyasamy) (@karthikaiyasamy)
* [#5109](https://github.com/pmd/pmd/pull/5109): \[java] Exclude constructor with lombok.Builder for MissingStaticMethodInNonInstantiatableClass - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85)
* [#5111](https://github.com/pmd/pmd/pull/5111): \[java] Fix UnusedPrivateMethod for @<!-- -->lombok.Builder.ObtainVia - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85)
* [#5118](https://github.com/pmd/pmd/pull/5118): \[java] FP for UnusedPrivateMethod with Jakarta @<!-- -->PostConstruct/PreDestroy annotations - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85)
* [#5121](https://github.com/pmd/pmd/pull/5121): \[plsql] Fixed issue with missing optional table alias in MERGE usage - [Arjen Duursma](https://github.com/duursma) (@duursma)
### 📈 Stats
* 81 commits
* 32 closed tickets & PRs
* Days since last release: 27
## 28-June-2024 - 7.3.0 ## 28-June-2024 - 7.3.0

View File

@ -7,7 +7,7 @@
<parent> <parent>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -111,9 +111,6 @@ public class CPDTask extends Task {
+ "Use failOnError=\"false\" to not fail the build.", Project.MSG_WARN); + "Use failOnError=\"false\" to not fail the build.", Project.MSG_WARN);
} }
// implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case.
config.setSkipLexicalErrors(true);
config.setIgnoreAnnotations(ignoreAnnotations); config.setIgnoreAnnotations(ignoreAnnotations);
config.setIgnoreLiterals(ignoreLiterals); config.setIgnoreLiterals(ignoreLiterals);
config.setIgnoreIdentifiers(ignoreIdentifiers); config.setIgnoreIdentifiers(ignoreIdentifiers);

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -10,6 +10,7 @@ import java.util.stream.Collectors;
import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextDocument;
import net.sourceforge.pmd.lang.document.TextPos2d; import net.sourceforge.pmd.lang.document.TextPos2d;
import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.document.TextRegion;
import net.sourceforge.pmd.lang.rule.xpath.NoAttribute;
import com.google.summit.ast.SourceLocation; import com.google.summit.ast.SourceLocation;
import com.google.summit.ast.declaration.MethodDeclaration; import com.google.summit.ast.declaration.MethodDeclaration;
@ -27,6 +28,12 @@ public final class ASTMethod extends AbstractApexNode implements ApexQualifiable
*/ */
private static final String STATIC_INIT_ID = "<clinit>"; private static final String STATIC_INIT_ID = "<clinit>";
/**
* Internal name used by the synthetic trigger method.
* @see #isTriggerBlock()
*/
private static final String TRIGGER_INVOKE_ID = "<invoke>";
// Store the details instead of wrapping a com.google.summit.ast.Node. // Store the details instead of wrapping a com.google.summit.ast.Node.
// This is to allow synthetic ASTMethod nodes. // This is to allow synthetic ASTMethod nodes.
// An example is the trigger `invoke` method. // An example is the trigger `invoke` method.
@ -150,4 +157,14 @@ public final class ASTMethod extends AbstractApexNode implements ApexQualifiable
public int getArity() { public int getArity() {
return parameterTypes.size(); return parameterTypes.size();
} }
/**
* Checks whether this method is the synthetic trigger method.
* @return true if this method is the synthetic trigger method
* @since 7.5.0
*/
@NoAttribute
public boolean isTriggerBlock() {
return TRIGGER_INVOKE_ID.equals(internalName);
}
} }

View File

@ -229,7 +229,7 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua
// 2. Add the expected ASTModifier child node // 2. Add the expected ASTModifier child node
buildModifiers(emptyList()).also { it.setParent(invokeMethod) } buildModifiers(emptyList()).also { it.setParent(invokeMethod) }
// 3. Elide the body CompoundStatement->ASTBlockStatement // 3. Elide the body CompoundStatement->ASTBlockStatement
node.body.forEach { buildChildren(it, parent = invokeMethod as AbstractApexNode) } node.body.forEach { buildAndSetParent(it, parent = invokeMethod as AbstractApexNode) }
} else { } else {
buildChildren(node, parent = this, exclude = { it in node.modifiers }) buildChildren(node, parent = this, exclude = { it in node.modifiers })
} }
@ -737,18 +737,18 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua
findDescendants(root, nodeType = ASTProperty::class).forEach { node -> generateFields(node) } findDescendants(root, nodeType = ASTProperty::class).forEach { node -> generateFields(node) }
// Sort resulting nodes // Sort resulting nodes
findDescendants(root, nodeType = ASTUserClass::class).forEach { node -> findDescendants(root, nodeType = BaseApexClass::class).forEach { node ->
sortUserClassChildren(node) sortUserClassChildren(node)
} }
} }
/** /**
* Sort children of [ASTUserClass] in historical order. * Sort children of [BaseApexClass] (ASTUserClass, ASTUserTrigger, ...) in historical order.
* *
* This sorts [ASTField] nodes immediately after [ASTModifierNode] nodes at * This sorts [ASTField] nodes immediately after [ASTModifierNode] nodes at
* the start of the ordered children. * the start of the ordered children.
*/ */
private fun sortUserClassChildren(node: ASTUserClass) { private fun sortUserClassChildren(node: BaseApexClass<*>) {
val children = ArrayList(node.children().toList()) val children = ArrayList(node.children().toList())
children.sortBy{ when (it) { children.sortBy{ when (it) {
@ -772,7 +772,13 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua
/** Generates [ASTField] nodes for the [ASTFieldDeclarationStatements]. */ /** Generates [ASTField] nodes for the [ASTFieldDeclarationStatements]. */
private fun generateFields(node: ASTFieldDeclarationStatements) { private fun generateFields(node: ASTFieldDeclarationStatements) {
val parent = node.parent as BaseApexClass<*> val parent = if (node.parent is BaseApexClass<*>) {
node.parent as BaseApexClass<*>
} else if (node.parent is ASTMethod && (node.parent as ASTMethod).isTriggerBlock) {
node.parent.parent as BaseApexClass<*>
} else {
throw IllegalStateException("Unexpected apex tree - field declaration $node cannot appear hear")
}
node.node.declarations node.node.declarations
.map { decl -> .map { decl ->

View File

@ -65,4 +65,9 @@ class ApexTreeDumpTest extends BaseTreeDumpTest {
void switchStatements() { void switchStatements() {
doTest("SwitchStatements"); doTest("SwitchStatements");
} }
@Test
void trigger() {
doTest("AccountTrigger");
}
} }

View File

@ -0,0 +1,9 @@
// see https://github.com/pmd/pmd/issues/5139
trigger AccountTrigger on Account (before insert, before update) {
integer i = 0;
for (i = 0; i <15; i++) {
SObjectType token = Schema.getGlobalDescribe().get('Account');
}
integer anotherField = 2;
System.debug('test');
}

View File

@ -0,0 +1,50 @@
+- ApexFile[@DefiningType = "AccountTrigger", @RealLoc = true]
+- UserTrigger[@DefiningType = "AccountTrigger", @Image = "AccountTrigger", @Nested = false, @RealLoc = true, @SimpleName = "AccountTrigger", @TargetName = "Account", @Usages = (TriggerUsage.BEFORE_INSERT, TriggerUsage.BEFORE_UPDATE)]
+- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Field[@DefiningType = "AccountTrigger", @Image = "i", @Name = "i", @RealLoc = true, @Type = "Integer", @Value = "0"]
| +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Field[@DefiningType = "AccountTrigger", @Image = "anotherField", @Name = "anotherField", @RealLoc = true, @Type = "Integer", @Value = "2"]
| +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Method[@Arity = 0, @CanonicalName = "invoke", @Constructor = false, @DefiningType = "AccountTrigger", @Image = "invoke", @RealLoc = false, @ReturnType = "void", @StaticInitializer = false]
+- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- FieldDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true, @TypeArguments = (), @TypeName = "Integer"]
| +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- FieldDeclaration[@DefiningType = "AccountTrigger", @Image = "i", @Name = "i", @RealLoc = true]
| +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "0", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false]
| +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- ForLoopStatement[@DefiningType = "AccountTrigger", @RealLoc = true]
| +- StandardCondition[@DefiningType = "AccountTrigger", @RealLoc = true]
| | +- BooleanExpression[@DefiningType = "AccountTrigger", @Op = BooleanOperator.LESS_THAN, @RealLoc = true]
| | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true]
| | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "15", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false]
| +- Expression[@DefiningType = "AccountTrigger", @RealLoc = true]
| | +- AssignmentExpression[@DefiningType = "AccountTrigger", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true]
| | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "0", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false]
| +- BlockStatement[@CurlyBrace = true, @DefiningType = "AccountTrigger", @RealLoc = true]
| | +- VariableDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true]
| | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| | +- VariableDeclaration[@DefiningType = "AccountTrigger", @Image = "token", @RealLoc = true, @Type = "SObjectType"]
| | +- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "get", @InputParametersSize = 1, @MethodName = "get", @RealLoc = true]
| | | +- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "", @RealLoc = false, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false]
| | | | +- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "Schema.getGlobalDescribe", @InputParametersSize = 0, @MethodName = "getGlobalDescribe", @RealLoc = true]
| | | | +- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "Schema", @RealLoc = true, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false]
| | | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "Account", @Integer = false, @LiteralType = LiteralType.STRING, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = true]
| | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "token", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- PostfixExpression[@DefiningType = "AccountTrigger", @Op = PostfixOperator.INCREMENT, @RealLoc = true]
| +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- FieldDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true, @TypeArguments = (), @TypeName = "Integer"]
| +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- FieldDeclaration[@DefiningType = "AccountTrigger", @Image = "anotherField", @Name = "anotherField", @RealLoc = true]
| +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "2", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false]
| +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "anotherField", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- ExpressionStatement[@DefiningType = "AccountTrigger", @RealLoc = true]
+- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "System.debug", @InputParametersSize = 1, @MethodName = "debug", @RealLoc = true]
+- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "System", @RealLoc = true, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false]
+- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "test", @Integer = false, @LiteralType = LiteralType.STRING, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = true]

View File

@ -263,7 +263,7 @@ trigger CaseAssignLevel on CaseAssignLevel__c (after delete, after insert, after
<rule-property name="methodReportLevel">1</rule-property> <rule-property name="methodReportLevel">1</rule-property>
<expected-problems>1</expected-problems> <expected-problems>1</expected-problems>
<expected-messages> <expected-messages>
<message>The trigger 'CaseAssignLevel' has a cyclomatic complexity of 9.</message> <message>The trigger 'CaseAssignLevel' has a cyclomatic complexity of 12.</message>
</expected-messages> </expected-messages>
<code-ref id="trigger"/> <code-ref id="trigger"/>
</test-code> </test-code>

View File

@ -135,4 +135,18 @@ public class Foo {
]]></code> ]]></code>
</test-code> </test-code>
<!-- End Schema method invocations --> <!-- End Schema method invocations -->
<test-code>
<description>#5139 [apex] OperationWithHighCostInLoop not firing in triggers</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>4</expected-linenumbers>
<code><![CDATA[
trigger AccountTrigger on Account (before insert, before update) {
integer i = 0;
for (i = 0; i <15; i++) {
SObjectType token = Schema.getGlobalDescribe().get('Account');
}
}
]]></code>
</test-code>
</test-data> </test-data>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -124,7 +124,6 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand<CPDConfiguration>
configuration.setRendererName(rendererName); configuration.setRendererName(rendererName);
configuration.setSkipBlocksPattern(skipBlocksPattern); configuration.setSkipBlocksPattern(skipBlocksPattern);
configuration.setSkipDuplicates(skipDuplicates); configuration.setSkipDuplicates(skipDuplicates);
configuration.setSkipLexicalErrors(skipLexicalErrors);
configuration.setSourceEncoding(encoding.getEncoding()); configuration.setSourceEncoding(encoding.getEncoding());
configuration.setInputUri(uri); configuration.setInputUri(uri);
@ -133,9 +132,6 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand<CPDConfiguration>
configuration.setFailOnError(false); configuration.setFailOnError(false);
} }
// implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case.
configuration.setSkipLexicalErrors(true);
return configuration; return configuration;
} }

View File

@ -5,7 +5,7 @@
package net.sourceforge.pmd.cli; package net.sourceforge.pmd.cli;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.emptyString;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -146,7 +146,7 @@ abstract class BaseCliTest {
} }
public void checkNoErrorOutput() { public void checkNoErrorOutput() {
checkStdErr(equalTo("")); checkStdErr(emptyString());
} }
public void checkStdOut(Matcher<? super String> matcher) { public void checkStdOut(Matcher<? super String> matcher) {

View File

@ -239,10 +239,24 @@ class CpdCliTest extends BaseCliTest {
"--skip-lexical-errors") "--skip-lexical-errors")
.verify(r -> { .verify(r -> {
r.checkStdErr(containsPattern("Skipping file: Lexical error in file .*?BadFile\\.java")); r.checkStdErr(containsPattern("Skipping file: Lexical error in file .*?BadFile\\.java"));
r.checkStdErr(containsString("--skip-lexical-errors is deprecated. Use --no-fail-on-error instead."));
r.checkStdOut(containsString("Found a 5 line (13 tokens) duplication")); r.checkStdOut(containsString("Found a 5 line (13 tokens) duplication"));
}); });
} }
/**
* @see <a href="https://github.com/pmd/pmd/issues/5091">[core] PMD CPD v7.3.0 gives deprecation warning for skipLexicalErrors even when not used #5091</a>
* @throws Exception
*/
@Test
void noWarningsWithoutSkipLexicalErrors() throws Exception {
runCliSuccessfully("--minimum-tokens", "340", "--language", "java", "--dir", SRC_DIR, "--format", "text")
.verify(r -> {
r.checkNoErrorOutput();
r.checkStdOut(emptyString());
});
}
@Test @Test
void testExitCodeWithLexicalErrors() throws Exception { void testExitCodeWithLexicalErrors() throws Exception {
runCli(RECOVERED_ERRORS_OR_VIOLATIONS, runCli(RECOVERED_ERRORS_OR_VIOLATIONS,

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -67,7 +67,8 @@ public class CPDConfiguration extends AbstractConfiguration {
private boolean ignoreIdentifierAndLiteralSequences = false; private boolean ignoreIdentifierAndLiteralSequences = false;
@Deprecated @Deprecated
private boolean skipLexicalErrors = false; // Note: The default value was false until up to 7.3.0 and is true since 7.4.0
private boolean skipLexicalErrors = true;
private boolean noSkipBlocks = false; private boolean noSkipBlocks = false;

View File

@ -153,10 +153,6 @@ public final class CpdAnalysis implements AutoCloseable {
@SuppressWarnings("PMD.CloseResource") @SuppressWarnings("PMD.CloseResource")
public void performAnalysis(Consumer<CPDReport> consumer) { public void performAnalysis(Consumer<CPDReport> consumer) {
if (configuration.isSkipLexicalErrors()) {
LOGGER.warn("The option skipLexicalErrors is deprecated. Use failOnError instead.");
}
try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) {
Map<Language, CpdLexer> tokenizers = Map<Language, CpdLexer> tokenizers =
sourceManager.getTextFiles().stream() sourceManager.getTextFiles().stream()

View File

@ -223,7 +223,6 @@ class CpdAnalysisTest {
PmdReporter reporter = mock(PmdReporter.class); PmdReporter reporter = mock(PmdReporter.class);
config.setReporter(reporter); config.setReporter(reporter);
config.setSkipLexicalErrors(true); // must be true, otherwise CPD is aborted with first processing error
try (CpdAnalysis cpd = CpdAnalysis.create(config)) { try (CpdAnalysis cpd = CpdAnalysis.create(config)) {
assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION));
assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION));
@ -252,7 +251,6 @@ class CpdAnalysisTest {
PmdReporter reporter = mock(PmdReporter.class); PmdReporter reporter = mock(PmdReporter.class);
config.setReporter(reporter); config.setReporter(reporter);
config.setSkipLexicalErrors(true);
try (CpdAnalysis cpd = CpdAnalysis.create(config)) { try (CpdAnalysis cpd = CpdAnalysis.create(config)) {
assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION));
assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION));

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId> <artifactId>pmd</artifactId>
<version>7.4.0-SNAPSHOT</version> <version>7.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

Some files were not shown because too many files have changed in this diff Show More