Merge branch 'pmd/7.0.x' into pmd7-junit5-part2

This commit is contained in:
Andreas Dangel 2022-10-03 09:08:59 +02:00
commit a4422cf2e6
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
60 changed files with 2359 additions and 441 deletions

View File

@ -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,133 @@
"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"
]
},
{
"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,

View File

@ -42,17 +42,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

View File

@ -169,4 +169,12 @@ EOF
<tag>v2.3.0</tag>
<src-subpath>samples</src-subpath>
</project>
<project>
<name>java-regression-tests</name>
<type>git</type>
<connection>https://github.com/pmd/java-regression-tests</connection>
<tag>main</tag>
<auxclasspath-command>realpath java-regression-tests-*.jar</auxclasspath-command>
</project>
</projectlist>

View File

@ -15,9 +15,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:

View File

@ -10,6 +10,9 @@ on:
- '**'
workflow_dispatch:
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
runs-on: ubuntu-latest

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -2,7 +2,7 @@ repository: pmd/pmd
pmd:
version: 7.0.0-SNAPSHOT
previous_version: 6.49.0
previous_version: 6.50.0
date: ??-?????-2022
release_type: major

View File

@ -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:
@ -502,6 +505,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:

View File

@ -246,6 +246,12 @@ the breaking API changes will be performed in 7.0.0.
an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0,
we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %}
#### 6.50.0
##### CPD CLI
* CPD now supports the `--ignore-literal-sequences` argument when analyzing Lua code.
#### 6.49.0
##### Deprecated API

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
---
title: Architecture Decisions
sidebar: pmd_sidebar
permalink: pmd_projectdocs_decisions.html
last_updated: July 2022
---
<ul>
{% for page in site.pages %}
{% if page.adr == true and page.adr_status != "" %}
<li><a href="{{ page.url }}">{{ page.title }}</a> ({{ page.adr_status }})</li>
{% endif %}
{% endfor %}
</ul>

View File

@ -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. <https://github.com/joelparkerhenderson/architecture-decision-record>
gives a nice summary. The page <https://adr.github.io/> 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.

View File

@ -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.

View File

@ -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
---
<!-- https://github.com/joelparkerhenderson/architecture-decision-record/blob/main/templates/decision-record-template-by-michael-nygard/index.md -->
# 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.

View File

@ -9,26 +9,43 @@ author: Tom Copeland <tom@infoether.org>
### 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).

View File

@ -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.
* <https://github.com/jborgers/PMD-jPinpoint-rules>
* **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.
* <https://github.com/dgroup/arch4u-pmd>
* Sample ruleset from **maxdocs**, a multi markup wiki engine.
* <https://github.com/bohni/maxdocs/blob/master/src/main/config/pmd/pmd-ruleset.xml>
* Sample ruleset from **geotools**, an open source Java library that provides tools for geospatial data.
* <https://github.com/geotools/geotools/blob/main/build/qa/pmd-ruleset.xml>
* <https://github.com/geotools/geotools/blob/main/build/qa/pmd-junit-ruleset.xml>
## For Apex
* **unhappy-soup**, a repository with problematic Salesforce code to showcase PMD, the SFDX Scanner CLI
* <https://github.com/rsoesemann/unhappy-soup/blob/master/ruleset.xml>

View File

@ -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"

View File

@ -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.

View File

@ -142,7 +142,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);

View File

@ -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

View File

@ -2,7 +2,7 @@
usage() {
echo "Usage:"
echo " $(basename $0) <application-name> [-h|-v] ..."
echo " $(basename "$0") <application-name> [-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
@ -104,23 +104,25 @@ check_conf_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() {
@ -212,7 +214,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

View File

@ -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"

View File

@ -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"

View File

@ -31,7 +31,7 @@
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
<version>1.15.3</version>
</dependency>
<dependency>

View File

@ -4,51 +4,21 @@
package net.sourceforge.pmd.lang.java.rule.bestpractices;
import static net.sourceforge.pmd.util.CollectionUtil.setOf;
import java.util.List;
import java.util.Set;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility;
import net.sourceforge.pmd.lang.java.ast.Annotatable;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.internal.JavaPropertyUtil;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
public class UnusedPrivateFieldRule extends AbstractJavaRulechainRule {
private static final Set<String> INVALIDATING_CLASS_ANNOT = setOf(
"lombok.Builder",
"lombok.EqualsAndHashCode",
"lombok.Getter",
"lombok.Setter",
"lombok.Data",
"lombok.Value"
);
private static final PropertyDescriptor<List<String>> IGNORED_FIELD_ANNOTATIONS =
JavaPropertyUtil.ignoredAnnotationsDescriptor(
"lombok.Setter",
"lombok.Getter",
"java.lang.Deprecated",
"lombok.experimental.Delegate",
"javafx.fxml.FXML",
"javax.persistence.Id",
"javax.persistence.EmbeddedId",
"javax.persistence.Version",
"jakarta.persistence.Id",
"jakarta.persistence.EmbeddedId",
"jakarta.persistence.Version",
"org.mockito.Mock",
"org.mockito.Spy",
"org.springframework.boot.test.mock.mockito.MockBean"
);
private static final PropertyDescriptor<List<String>> IGNORED_FIELD_NAMES =
PropertyFactory.stringListProperty("ignoredFieldNames")
.defaultValues("serialVersionUID", "serialPersistentFields")
@ -57,7 +27,6 @@ public class UnusedPrivateFieldRule extends AbstractJavaRulechainRule {
public UnusedPrivateFieldRule() {
super(ASTAnyTypeDeclaration.class);
definePropertyDescriptor(IGNORED_FIELD_ANNOTATIONS);
definePropertyDescriptor(IGNORED_FIELD_NAMES);
}
@ -65,7 +34,7 @@ public class UnusedPrivateFieldRule extends AbstractJavaRulechainRule {
public Object visitJavaNode(JavaNode node, Object data) {
if (node instanceof ASTAnyTypeDeclaration) {
ASTAnyTypeDeclaration type = (ASTAnyTypeDeclaration) node;
if (JavaAstUtils.hasAnyAnnotation(type, INVALIDATING_CLASS_ANNOT)) {
if (hasAnyAnnotation(type)) {
return null;
}
@ -85,10 +54,14 @@ public class UnusedPrivateFieldRule extends AbstractJavaRulechainRule {
private boolean isIgnored(ASTFieldDeclaration field) {
return field.getVisibility() != Visibility.V_PRIVATE
|| isOK(field)
|| JavaAstUtils.hasAnyAnnotation(field, getProperty(IGNORED_FIELD_ANNOTATIONS));
|| hasAnyAnnotation(field);
}
private boolean isOK(ASTFieldDeclaration field) {
return field.getVarIds().any(it -> getProperty(IGNORED_FIELD_NAMES).contains(it.getName()));
}
private static boolean hasAnyAnnotation(Annotatable node) {
return !node.getDeclaredAnnotations().isEmpty();
}
}

View File

@ -40,7 +40,16 @@ public class CommentDefaultAccessModifierRule extends AbstractJavaRulechainRule
private static final PropertyDescriptor<List<String>> IGNORED_ANNOTS =
JavaPropertyUtil.ignoredAnnotationsDescriptor(
"com.google.common.annotations.VisibleForTesting",
"android.support.annotation.VisibleForTesting"
"android.support.annotation.VisibleForTesting",
"org.junit.jupiter.api.Test",
"org.junit.jupiter.api.ParameterizedTest",
"org.junit.jupiter.api.RepeatedTest",
"org.junit.jupiter.api.TestFactory",
"org.junit.jupiter.api.TestTemplate",
"org.junit.jupiter.api.BeforeEach",
"org.junit.jupiter.api.BeforeAll",
"org.junit.jupiter.api.AfterEach",
"org.junit.jupiter.api.AfterAll"
);
private static final PropertyDescriptor<Pattern> REGEX_DESCRIPTOR =

View File

@ -5,9 +5,13 @@
package net.sourceforge.pmd.lang.java.rule.errorprone;
import java.lang.reflect.Modifier;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.pcollections.PVector;
import org.pcollections.TreePVector;
@ -40,13 +44,15 @@ import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult;
public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRulechainRule {
private static final String MESSAGE = "Overridable method called during object construction: {0} ";
private static final String MESSAGE_TRANSITIVE = "This method may call an overridable method during object construction: {0}";
private static final String MESSAGE_TRANSITIVE = "This method may call an overridable method during object construction: {0} (call stack: [{1}])";
// Maps methods to the method that makes them unsafe
// Safe methods are mapped to null
// The value is used for better messages
private final Map<JMethodSymbol, JMethodSymbol> safeMethods = new HashMap<>();
// Maps methods to the method call stack that makes them unsafe
// Safe methods are mapped to an empty stack
// The method call stack (value of the map) is used for better messages
private final Map<JMethodSymbol, Deque<JMethodSymbol>> safeMethods = new HashMap<>();
private static final Deque<JMethodSymbol> EMPTY_STACK = new LinkedList<>();
public ConstructorCallsOverridableMethodRule() {
super(ASTConstructorDeclaration.class);
@ -64,47 +70,53 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul
return null; // then cannot be overridden
}
for (ASTMethodCall call : node.getBody().descendants(ASTMethodCall.class)) {
JMethodSymbol unsafetyReason = getUnsafetyReason(call, TreePVector.empty());
if (unsafetyReason != null) {
Deque<JMethodSymbol> unsafetyReason = getUnsafetyReason(call, TreePVector.empty());
if (!unsafetyReason.isEmpty()) {
JMethodSig overload = call.getOverloadSelectionInfo().getMethodType();
JMethodSig unsafeMethod = call.getTypeSystem().sigOf(unsafetyReason);
JMethodSig unsafeMethod = call.getTypeSystem().sigOf(unsafetyReason.getLast());
String message = unsafeMethod.equals(overload) ? MESSAGE : MESSAGE_TRANSITIVE;
addViolationWithMessage(data, call, message, new Object[] { PrettyPrintingUtil.prettyPrintOverload(unsafetyReason) });
String lastMethod = PrettyPrintingUtil.prettyPrintOverload(unsafetyReason.getLast());
String stack = unsafetyReason.stream().map(PrettyPrintingUtil::prettyPrintOverload).collect(Collectors.joining(", "));
asCtx(data).addViolationWithMessage(call, message, lastMethod, stack);
}
}
return null;
}
private JMethodSymbol getUnsafetyReason(ASTMethodCall call, PVector<ASTMethodDeclaration> recursionGuard) {
@NonNull
private Deque<JMethodSymbol> getUnsafetyReason(ASTMethodCall call, PVector<ASTMethodDeclaration> recursionGuard) {
if (!isCallOnThisInstance(call)) {
return null;
return EMPTY_STACK;
}
OverloadSelectionResult overload = call.getOverloadSelectionInfo();
if (overload.isFailed()) {
return null;
return EMPTY_STACK;
}
JMethodSymbol method = (JMethodSymbol) overload.getMethodType().getSymbol();
if (isOverridable(method)) {
return method; // the method itself
Deque<JMethodSymbol> stack = new LinkedList<>();
stack.addFirst(method); // the method itself
return stack;
} else {
return getUnsafetyReason(method, recursionGuard);
}
}
private JMethodSymbol getUnsafetyReason(JMethodSymbol method, PVector<ASTMethodDeclaration> recursionGuard) {
@NonNull
private Deque<JMethodSymbol> getUnsafetyReason(JMethodSymbol method, PVector<ASTMethodDeclaration> recursionGuard) {
if (method.isStatic()) {
return null; // no access to this instance anyway
return EMPTY_STACK; // no access to this instance anyway
}
// we need to prove that all calls on this instance are safe
ASTMethodDeclaration declaration = method.tryGetNode();
if (declaration == null) {
return null; // no idea
return EMPTY_STACK; // no idea
} else if (recursionGuard.contains(declaration)) {
// being visited, assume body is safe
return null;
return EMPTY_STACK;
}
// note we can't use computeIfAbsent because of comodification
@ -117,18 +129,19 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul
for (ASTMethodCall call : NodeStream.of(declaration.getBody())
.descendants(ASTMethodCall.class)
.filter(ConstructorCallsOverridableMethodRule::isCallOnThisInstance)) {
JMethodSymbol unsafetyReason = getUnsafetyReason(call, deeperRecursion);
if (unsafetyReason != null) {
Deque<JMethodSymbol> unsafetyReason = getUnsafetyReason(call, deeperRecursion);
if (!unsafetyReason.isEmpty()) {
// this method call is unsafe for some reason,
// body is unsafe for the same reason
safeMethods.put(method, unsafetyReason);
return unsafetyReason;
safeMethods.putIfAbsent(method, new LinkedList<>(unsafetyReason));
safeMethods.get(method).addFirst(method);
return safeMethods.get(method);
}
}
// body is safe
safeMethods.put(method, null);
return null;
safeMethods.remove(method);
return EMPTY_STACK;
}
private static boolean isCallOnThisInstance(ASTMethodCall call) {

View File

@ -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.
</description>
<priority>3</priority>
<example>
@ -1606,6 +1606,12 @@ public class Foo {
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#unusedprivatefield">
<description>
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.
</description>
<priority>3</priority>
<example>

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