Merge branch 'master' into experimental-apex-parser

This commit is contained in:
Andreas Dangel
2024-01-05 15:43:48 +01:00
78 changed files with 2695 additions and 458 deletions

View File

@ -1667,7 +1667,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/8858497?v=4",
"profile": "https://github.com/bergander",
"contributions": [
"bug"
"bug",
"code"
]
},
{
@ -7234,6 +7235,15 @@
]
},
{
"login": "Scrates1",
"name": "Scrates1",
"avatar_url": "https://avatars.githubusercontent.com/u/49557842?v=4",
"profile": "https://github.com/Scrates1",
"contributions": [
"bug"
]
},
{
"login": "eant60",
"name": "eant60",
"avatar_url": "https://avatars.githubusercontent.com/u/41472980?v=4",
@ -7269,6 +7279,15 @@
"bug"
]
},
{
"login": "nakul777",
"name": "Nakul Sharma",
"avatar_url": "https://avatars.githubusercontent.com/u/1551545?v=4",
"profile": "https://github.com/nakul777",
"contributions": [
"bug"
]
},
{
"login": "shai-bennathan",
"name": "Shai Bennathan",
@ -7296,6 +7315,42 @@
"contributions": [
"doc"
]
},
{
"login": "219sansim",
"name": "219sansim",
"avatar_url": "https://avatars.githubusercontent.com/u/108684604?v=4",
"profile": "https://github.com/219sansim",
"contributions": [
"code"
]
},
{
"login": "soyodream",
"name": "soyodream",
"avatar_url": "https://avatars.githubusercontent.com/u/151845313?v=4",
"profile": "https://github.com/soyodream",
"contributions": [
"bug"
]
},
{
"login": "Debamoy",
"name": "Debamoy Datta",
"avatar_url": "https://avatars.githubusercontent.com/u/44639649?v=4",
"profile": "https://github.com/Debamoy",
"contributions": [
"code"
]
},
{
"login": "marcindabrowski",
"name": "Marcin Dąbrowski",
"avatar_url": "https://avatars.githubusercontent.com/u/3007876?v=4",
"profile": "https://github.com/marcindabrowski",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -169,7 +169,7 @@ function build() {
#
function pmd_ci_build_setup_bundler() {
pmd_ci_log_info "Installing bundler..."
gem install bundler
gem install bundler -v 2.4.22
}
#

View File

@ -55,7 +55,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Daether.connector.http.connectionMaxTtl=180 -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/22/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/master/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -24,7 +24,7 @@ jobs:
shell: bash
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/22/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/master/scripts" >> $GITHUB_ENV
- name: Sync
run: .ci/git-repo-sync.sh
shell: bash

View File

@ -36,7 +36,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Daether.connector.http.connectionMaxTtl=180 -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/22/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/master/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
base64 (0.2.0)
claide (1.1.0)
@ -13,7 +13,7 @@ GEM
concurrent-ruby (1.2.2)
cork (0.3.0)
colored2 (~> 3.1)
danger (9.4.0)
danger (9.4.2)
claide (~> 1.0)
claide-plugins (>= 0.9.2)
colored2 (~> 3.1)
@ -24,12 +24,12 @@ GEM
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.0)
no_proxy_fix
octokit (>= 6.0, < 8.0)
octokit (>= 4.0)
terminal-table (>= 1, < 4)
differ (0.1.2)
et-orbi (1.2.7)
tzinfo
faraday (2.7.11)
faraday (2.7.12)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
@ -51,10 +51,10 @@ GEM
mini_portile2 (2.8.5)
nap (1.1.0)
no_proxy_fix (0.1.2)
nokogiri (1.15.4)
nokogiri (1.15.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
octokit (7.2.0)
octokit (8.0.0)
faraday (>= 1, < 3)
sawyer (~> 0.9)
open4 (1.3.4)
@ -65,7 +65,7 @@ GEM
nokogiri (~> 1.13)
rufus-scheduler (~> 3.8)
slop (~> 4.9)
public_suffix (5.0.3)
public_suffix (5.0.4)
raabro (1.4.0)
racc (1.7.3)
rchardet (1.8.0)
@ -96,4 +96,4 @@ DEPENDENCIES
safe_yaml
BUNDLED WITH
2.4.5
2.4.22

View File

@ -1,14 +1,20 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.0.8)
activesupport (7.1.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
addressable (2.8.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
base64 (0.1.1)
base64 (0.2.0)
bigdecimal (3.1.5)
coffee-script (2.4.1)
coffee-script-source
execjs
@ -16,8 +22,11 @@ GEM
colorator (1.1.0)
commonmarker (0.23.10)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
dnsruby (1.70.0)
simpleidn (~> 0.2.1)
drb (2.2.0)
ruby2_keywords
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
@ -25,12 +34,12 @@ GEM
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.9.1)
faraday (2.7.11)
faraday (2.7.12)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.16.2)
ffi (1.16.3)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (228)
@ -207,13 +216,14 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.8.4)
mini_portile2 (2.8.5)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.20.0)
nokogiri (1.15.4)
mutex_m (0.2.0)
nokogiri (1.15.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
octokit (4.25.1)
@ -222,7 +232,7 @@ GEM
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.7)
racc (1.7.1)
racc (1.7.3)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
@ -243,13 +253,13 @@ GEM
unf (~> 0.1.4)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
typhoeus (1.4.0)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unf_ext (0.0.9.1)
unicode-display_width (1.8.0)
webrick (1.8.1)
@ -262,4 +272,4 @@ DEPENDENCIES
webrick
BUNDLED WITH
2.4.5
2.4.22

View File

@ -11,3 +11,11 @@ summary: "Groovy-specific features and guidance"
> familiar and easy to learn syntax.
{% include language_info.html name='Groovy' id='groovy' implementation='groovy::lang.groovy.GroovyLanguageModule' supports_cpd=true since='5.5.2' %}
## Support in PMD
Groovy support was added with PMD 5.5.2. With PMD 7.0.0, support for Groovy 3 and 4 was added.
Since PMD 7.0.0, the Groovy module supports [suppression](pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs.
### Limitations
- Support for Groovy only extends to CPD to detect code duplication.

View File

@ -63,7 +63,7 @@ The semantic analysis roughly works like so:
3. The last pass resolves the types of expressions, which performs overload resolution on method calls, and type inference.
TODO describe
* why we need auxclasspath
* why we need auxclasspath, and how to put the java classes onto the auxclasspath (jre/lib/rt.jar or lib/jrt-fs.jar).
* how disambiguation can fail
## Type and symbol APIs

View File

@ -10,3 +10,5 @@ summary: "Swift-specific features and guidance"
> powerful for experts. It is fast, modern, safe, and a joy to write.
{% include language_info.html name='Swift' id='swift' implementation='swift::lang.swift.SwiftLanguageModule' supports_pmd=true supports_cpd=true since='5.3.7' %}
The grammar of the languages is documented in [The Swift Language Reference](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/aboutthelanguagereference/).

File diff suppressed because it is too large Load Diff

View File

@ -57,6 +57,20 @@ the duplication. Here's a quick summary:
Novice as much as advanced readers may want to [read on on Refactoring Guru](https://refactoring.guru/smells/duplicate-code)
for more in-depth strategies, use cases and explanations.
### Finding more duplicates
For some languages, additional options are supported. E.g. Java supports `--ignore-identifiers`. This has the
effect, that all identifiers are replaced with the same placeholder value before the comparing. This helps to
identify structurally identical code that only differs in naming (different class names, different method names,
different parameter names).
There are other similar options: `--ignore-annotations`, `--ignore-literals`, `--ignore-literal-sequences`,
`--ignore-sequences`, `--ignore-usings`.
Note that these options are *disabled* by default (e.g. identifiers are *not* replaced with the same placeholder
value). By default, CPD finds identical duplicates. Using these options, the found duplicates are not anymore
exactly identical.
## CLI Usage
### CLI options reference
@ -109,19 +123,17 @@ for more in-depth strategies, use cases and explanations.
%}
{% include custom/cli_option_row.html options="--skip-duplicate-files"
description="Ignore multiple copies of files of the same name and length in comparison."
default="false"
%}
{% include custom/cli_option_row.html options="--exclude"
option_arg="path"
description="Files to be excluded from the analysis"
%}
{% include custom/cli_option_row.html options="--non-recursive"
description="Don't scan subdirectories"
default="false"
description="Don't scan subdirectories. By default, subdirectories are considered."
%}
{% include custom/cli_option_row.html options="--skip-lexical-errors"
description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD"
default="false"
description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD.
By default, CPD analysis is stopped on the first error."
%}
{% include custom/cli_option_row.html options="--format,-f"
option_arg="format"
@ -144,38 +156,37 @@ for more in-depth strategies, use cases and explanations.
Disable this feature with `--no-fail-on-violation` to exit with 0 instead and just output the report."
%}
{% include custom/cli_option_row.html options="--ignore-literals"
description="Ignore number values and string contents when comparing text"
default="false"
description="Ignore literal values such as numbers and strings when comparing text.
By default, literals are not ignored."
languages="Java"
%}
{% include custom/cli_option_row.html options="--ignore-literal-sequences"
description="Ignore sequences of literals such as list initializers.
By default, such sequences of literals are not ignored."
languages="C#, C++, Lua"
%}
{% include custom/cli_option_row.html options="--ignore-identifiers"
description="Ignore constant and variable names when comparing text"
default="false"
description="Ignore names of classes, methods, variables, constants, etc. when comparing text.
By default, identifier names are not ignored."
languages="Java"
%}
{% include custom/cli_option_row.html options="--ignore-annotations"
description="Ignore language annotations (Java) or attributes (C#) when comparing text"
default="false"
description="Ignore language annotations (Java) or attributes (C#) when comparing text.
By default, annotations are not ignored."
languages="C#, Java"
%}
{% 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++, Lua"
%}
{% include custom/cli_option_row.html options="--ignore-sequences"
description="Ignore sequences of identifier and literals"
default="false"
description="Ignore sequences of identifier and literals.
By default, such sequences are not ignored."
languages="C++"
%}
{% include custom/cli_option_row.html options="--ignore-usings"
description="Ignore `using` directives in C# when comparing text"
default="false"
description="Ignore `using` directives in C# when comparing text.
By default, using directives are not ignored."
languages="C#"
%}
{% include custom/cli_option_row.html options="--no-skip-blocks"
description="Do not skip code blocks matched by `--skip-blocks-pattern`"
default="false"
languages="C++"
%}
{% include custom/cli_option_row.html options="--skip-blocks-pattern"
@ -220,6 +231,13 @@ You may wish to check sources that are stored in different directories:
<em>There is no limit to the number of `--dir`, you may add.</em>
You may wish to ignore identifiers so that more duplications are found, that only differ in naming:
{% include cli_example.html
id="ignore_identifiers"
linux="pmd cpd --minimum-tokens 100 --dir src/main/java --ignore-identifiers"
windows="pmd.bat cpd --minimum-tokens 100 --dir src\main\java --ignore-identifiers" %}
And if you're checking a C source tree with duplicate files in different architecture directories
you can skip those using `--skip-duplicate-files`:
@ -459,7 +477,7 @@ Here's a screenshot of CPD after running on the JDK 8 java.lang package:
## Suppression
Arbitrary blocks of code can be ignored through comments on **Java**, **C/C++**, **Dart**, **Go**, **Javascript**,
Arbitrary blocks of code can be ignored through comments on **Java**, **C/C++**, **Dart**, **Go**, **Groovy**, **Javascript**,
**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python**, **Scala**, **Swift** and **C#** by including the keywords `CPD-OFF` and `CPD-ON`.
```java

View File

@ -73,6 +73,16 @@ As PMD 7 revamped the Java module, if you have custom rules, you need to migrate
See the use case [I'm using custom rules]({{ baseurl }}pmd_userdocs_migrating_to_pmd7.html#im-using-custom-rules)
in the Migration Guide.
##### Swift Support
* limited support for Swift 5.9 (Macro Expansions)
##### Groovy Support (CPD)
* We now support parsing all Groovy features from Groovy 3 and 4.
* We now support [suppression](pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs.
* See [PR #4726](https://github.com/pmd/pmd/pull/4726) for details.
#### Rule Changes
**New Rules**
@ -82,31 +92,53 @@ in the Migration Guide.
* {% rule java/codestyle/UseExplicitTypes %} reports usages of `var` keyword, which was introduced with Java 10.
* {% rule xml/bestpractices/MissingEncoding %} finds XML files without explicit encoding.
**Changed Rules**
* {% rule java/codestyle/EmptyControlStatement %}: The rule has a new property to allow empty blocks when
they contain a comment (`allowCommentedBlocks`).
#### Fixed issues
* cli
* [#4594](https://github.com/pmd/pmd/pull/4594): \[cli] Change completion generation to runtime
* [#4685](https://github.com/pmd/pmd/pull/4685): \[cli] Clarify CPD documentation, fix positional parameter handling
* [#4723](https://github.com/pmd/pmd/issues/4723): \[cli] Launch fails for "bash pmd"
* core
* [#1027](https://github.com/pmd/pmd/issues/1027): \[core] Apply the new PropertyDescriptor&lt;Pattern&gt; type where applicable
* [#4674](https://github.com/pmd/pmd/issues/4674): \[core] WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass
* [#4694](https://github.com/pmd/pmd/pull/4694): \[core] Fix line/col numbers in TokenMgrError
* [#4717](https://github.com/pmd/pmd/issues/4717): \[core] XSLTRenderer doesn't close report file
* [#4750](https://github.com/pmd/pmd/pull/4750): \[core] Fix flaky SummaryHTMLRenderer
* doc
* [#3175](https://github.com/pmd/pmd/issues/3175): \[doc] Document language module features
* [#4659](https://github.com/pmd/pmd/pull/4659): \[doc] Improve ant documentation
* [#4669](https://github.com/pmd/pmd/pull/4669): \[doc] Add bld PMD Extension to Tools / Integrations
* [#4676](https://github.com/pmd/pmd/issues/4676): \[doc] Clarify how CPD `--ignore-literals` and `--ignore-identifiers` work
* miscellaneous
* [#4699](https://github.com/pmd/pmd/pull/4699): Make PMD buildable with java 21
* [#4586](https://github.com/pmd/pmd/pull/4586): Use explicit encoding in ruleset xml files
* [#4642](https://github.com/pmd/pmd/issues/4642): Update regression tests with Java 21 language features
* [#4741](https://github.com/pmd/pmd/pull/4741): Add pmd-compat6 module for maven-pmd-plugin
* [#4749](https://github.com/pmd/pmd/pull/4749): Fixes NoSuchMethodError on processing errors in pmd-compat6
* apex-performance
* [#4675](https://github.com/pmd/pmd/issues/4675): \[apex] New Rule: OperationWithHighCostInLoop
* groovy
* [#4726](https://github.com/pmd/pmd/pull/4726): \[groovy] Support Groovy to 3 and 4 and CPD suppressions
* java
* [#4628](https://github.com/pmd/pmd/pull/4628): \[java] Support loading classes from java runtime images
* [#4753](https://github.com/pmd/pmd/issues/4753): \[java] PMD crashes while using generics and wildcards
* java-codestyle
* [#2847](https://github.com/pmd/pmd/issues/2847): \[java] New Rule: Use Explicit Types
* [#4578](https://github.com/pmd/pmd/issues/4578): \[java] CommentDefaultAccessModifier comment needs to be before annotation if present
* [#4645](https://github.com/pmd/pmd/issues/4645): \[java] CommentDefaultAccessModifier - False Positive with JUnit5's ParameterizedTest
* [#4754](https://github.com/pmd/pmd/pull/4754): \[java] EmptyControlStatementRule: Add allowCommentedBlocks property
* java-errorprone
* [#1831](https://github.com/pmd/pmd/issues/1831): \[java] DetachedTestCase reports abstract methods
* [#4719](https://github.com/pmd/pmd/pull/4719): \[java] UnnecessaryCaseChange: example doc toUpperCase() should compare to a capitalized string
* javascript
* [#4673](https://github.com/pmd/pmd/pull/4673): \[javascript] CPD: Added support for decorator notation
* swift
* [#4697](https://github.com/pmd/pmd/issues/4697): \[swift] Support Swift 5.9 features (mainly macros expansion expressions)
* xml-bestpractices
* [#4592](https://github.com/pmd/pmd/pull/4592): \[xml] Add MissingEncoding rule
@ -130,8 +162,14 @@ The following previously deprecated classes have been removed:
* [#4640](https://github.com/pmd/pmd/pull/4640): \[cli] Launch script fails if run via "bash pmd" - [Shai Bennathan](https://github.com/shai-bennathan) (@shai-bennathan)
* [#4673](https://github.com/pmd/pmd/pull/4673): \[javascript] CPD: Added support for decorator notation - [Wener](https://github.com/wener-tiobe) (@wener-tiobe)
* [#4677](https://github.com/pmd/pmd/pull/4677): \[apex] Add new rule: OperationWithHighCostInLoop - [Thomas Prouvot](https://github.com/tprouvot) (@tprouvot)
* [#4698](https://github.com/pmd/pmd/pull/4698): \[swift] Add macro expansion support for swift 5.9 - [Richard B.](https://github.com/kenji21) (@kenji21)
* [#4706](https://github.com/pmd/pmd/pull/4706): \[java] DetachedTestCase should not report on abstract methods - [Debamoy Datta](https://github.com/Debamoy) (@Debamoy)
* [#4719](https://github.com/pmd/pmd/pull/4719): \[java] UnnecessaryCaseChange: example doc toUpperCase() should compare to a capitalized string - [ciufudean](https://github.com/ciufudean) (@ciufudean)
* [#4738](https://github.com/pmd/pmd/pull/4738): \[doc] Added reference to the PMD extension for bld - [Erik C. Thauvin](https://github.com/ethauvin) (@ethauvin)
* [#4749](https://github.com/pmd/pmd/pull/4749): Fixes NoSuchMethodError on processing errors in pmd-compat6 - [Andreas Bergander](https://github.com/bergander) (@bergander)
* [#4750](https://github.com/pmd/pmd/pull/4750): \[core] Fix flaky SummaryHTMLRenderer - [219sansim](https://github.com/219sansim) (@219sansim)
* [#4754](https://github.com/pmd/pmd/pull/4754): \[java] EmptyControlStatementRule: Add allowCommentedBlocks property - [Andreas Bergander](https://github.com/bergander) (@bergander)
* [#4759](https://github.com/pmd/pmd/pull/4759): \[java] fix: remove delimiter attribute from ruleset category/java/errorprone.xml - [Marcin Dąbrowski](https://github.com/marcindabrowski) (@marcindabrowski)
### 🚀 Major Features and Enhancements
@ -202,6 +240,7 @@ For more information on the languages, see the [Detailed Release Notes for PMD 7
#### New: Swift support
* use PMD to analyze Swift code with PMD rules
* limited support for Swift 5.9 (Macro Expansions)
* initially 4 built-in rules
Contributors: [Lucas Soncini](https://github.com/lsoncini) (@lsoncini),
@ -282,6 +321,12 @@ Note: Support for Java 19 preview language features have been removed. The versi
With the new version of Apex Jorje, the new language constructs like User Mode Database Operations
can be parsed now. PMD should now be able to parse Apex code up to version 59.0 (Winter '23).
#### Changed: Groovy Support (CPD)
* We now support parsing all Groovy features from Groovy 3 and 4.
* We now support [suppression](pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs.
* See [PR #4726](https://github.com/pmd/pmd/pull/4726) for details.
#### Changed: Rule properties
* The old deprecated classes like `IntProperty` and `StringProperty` have been removed. Please use
@ -390,6 +435,8 @@ can be parsed now. PMD should now be able to parse Apex code up to version 59.0
not necessary are allowed, if they separate expressions of different precedence.
The other property `ignoreBalancing` (default: true) is similar, in that it allows parentheses that help
reading and understanding the expressions.
* {% rule java/codestyle/EmptyControlStatement %}: The rule has a new property to allow empty blocks when
they contain a comment (`allowCommentedBlocks`).
**Java Design**
@ -476,9 +523,11 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.
* [#4460](https://github.com/pmd/pmd/pull/4460): Fix assembly-plugin warnings
* [#4582](https://github.com/pmd/pmd/issues/4582): \[dist] Download link broken
* [#4586](https://github.com/pmd/pmd/pull/4586): Use explicit encoding in ruleset xml files
* [#4642](https://github.com/pmd/pmd/issues/4642): Update regression tests with Java 21 language features
* [#4691](https://github.com/pmd/pmd/issues/4691): \[CVEs] Critical and High CEVs reported on PMD and PMD dependencies
* [#4699](https://github.com/pmd/pmd/pull/4699): Make PMD buildable with java 21
* [#4741](https://github.com/pmd/pmd/pull/4741): Add pmd-compat6 module for maven-pmd-plugin
* [#4749](https://github.com/pmd/pmd/pull/4749): Fixes NoSuchMethodError on processing errors in pmd-compat6
* ant
* [#4080](https://github.com/pmd/pmd/issues/4080): \[ant] Split off Ant integration into a new submodule
* core
@ -524,6 +573,10 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.
* [#4454](https://github.com/pmd/pmd/issues/4454): \[core] "Unknown option: '-min'" but is referenced in documentation
* [#4611](https://github.com/pmd/pmd/pull/4611): \[core] Fix loading language properties from env vars
* [#4621](https://github.com/pmd/pmd/issues/4621): \[core] Make `ClasspathClassLoader::getResource` child first
* [#4674](https://github.com/pmd/pmd/issues/4674): \[core] WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass
* [#4694](https://github.com/pmd/pmd/pull/4694): \[core] Fix line/col numbers in TokenMgrError
* [#4717](https://github.com/pmd/pmd/issues/4717): \[core] XSLTRenderer doesn't close report file
* [#4750](https://github.com/pmd/pmd/pull/4750): \[core] Fix flaky SummaryHTMLRenderer
* cli
* [#2234](https://github.com/pmd/pmd/issues/2234): \[core] Consolidate PMD CLI into a single command
* [#3828](https://github.com/pmd/pmd/issues/3828): \[core] Progress reporting
@ -532,6 +585,7 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.
* [#4482](https://github.com/pmd/pmd/issues/4482): \[cli] pmd.bat can only be executed once
* [#4484](https://github.com/pmd/pmd/issues/4484): \[cli] ast-dump with no properties produce an NPE
* [#4594](https://github.com/pmd/pmd/pull/4594): \[cli] Change completion generation to runtime
* [#4685](https://github.com/pmd/pmd/pull/4685): \[cli] Clarify CPD documentation, fix positional parameter handling
* [#4723](https://github.com/pmd/pmd/issues/4723): \[cli] Launch fails for "bash pmd"
* doc
* [#2501](https://github.com/pmd/pmd/issues/2501): \[doc] Verify ANTLR Documentation
@ -540,6 +594,7 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.
* [#4303](https://github.com/pmd/pmd/issues/4303): \[doc] Document new property framework
* [#4438](https://github.com/pmd/pmd/issues/4438): \[doc] Documentation links in VS Code are outdated
* [#4521](https://github.com/pmd/pmd/issues/4521): \[doc] Website is not mobile friendly
* [#4676](https://github.com/pmd/pmd/issues/4676): \[doc] Clarify how CPD `--ignore-literals` and `--ignore-identifiers` work
* [#4659](https://github.com/pmd/pmd/pull/4659): \[doc] Improve ant documentation
* [#4669](https://github.com/pmd/pmd/pull/4669): \[doc] Add bld PMD Extension to Tools / Integrations
* testing
@ -564,6 +619,8 @@ Language specific fixes:
* [#4675](https://github.com/pmd/pmd/issues/4675): \[apex] New Rule: OperationWithHighCostInLoop
* apex-security
* [#4646](https://github.com/pmd/pmd/issues/4646): \[apex] ApexSOQLInjection does not recognise SObjectType or SObjectField as safe variable types
* groovy
* [#4726](https://github.com/pmd/pmd/pull/4726): \[groovy] Support Groovy to 3 and 4 and CPD suppressions
* java
* [#520](https://github.com/pmd/pmd/issues/520): \[java] Allow `@SuppressWarnings` with constants instead of literals
* [#864](https://github.com/pmd/pmd/issues/864): \[java] Similar/duplicated implementations for determining FQCN
@ -593,6 +650,8 @@ Language specific fixes:
* [#4401](https://github.com/pmd/pmd/issues/4401): \[java] PMD 7 fails to build under Java 19
* [#4405](https://github.com/pmd/pmd/issues/4405): \[java] Processing error with ArrayIndexOutOfBoundsException
* [#4583](https://github.com/pmd/pmd/issues/4583): \[java] Support JDK 21 (LTS)
* [#4628](https://github.com/pmd/pmd/pull/4628): \[java] Support loading classes from java runtime images
* [#4753](https://github.com/pmd/pmd/issues/4753): \[java] PMD crashes while using generics and wildcards
* java-bestpractices
* [#342](https://github.com/pmd/pmd/issues/342): \[java] AccessorMethodGeneration: Name clash with another public field not properly handled
* [#755](https://github.com/pmd/pmd/issues/755): \[java] AccessorClassGeneration false positive for private constructors
@ -664,6 +723,7 @@ Language specific fixes:
* [#4557](https://github.com/pmd/pmd/issues/4557): \[java] UnnecessaryImport FP with static imports of overloaded methods
* [#4578](https://github.com/pmd/pmd/issues/4578): \[java] CommentDefaultAccessModifier comment needs to be before annotation if present
* [#4645](https://github.com/pmd/pmd/issues/4645): \[java] CommentDefaultAccessModifier - False Positive with JUnit5's ParameterizedTest
* [#4754](https://github.com/pmd/pmd/pull/4754): \[java] EmptyControlStatementRule: Add allowCommentedBlocks property
* java-design
* [#1014](https://github.com/pmd/pmd/issues/1014): \[java] LawOfDemeter: False positive with lambda expression
* [#1605](https://github.com/pmd/pmd/issues/1605): \[java] LawOfDemeter: False positive for standard UTF-8 charset name
@ -692,6 +752,7 @@ Language specific fixes:
* [#659](https://github.com/pmd/pmd/issues/659): \[java] MissingBreakInSwitch - last default case does not contain a break
* [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces
* [#1669](https://github.com/pmd/pmd/issues/1669): \[java] NullAssignment - FP with ternay and null as constructor argument
* [#1831](https://github.com/pmd/pmd/issues/1831): \[java] DetachedTestCase reports abstract methods
* [#1899](https://github.com/pmd/pmd/issues/1899): \[java] Recognize @<!-- -->SuppressWanings("fallthrough") for MissingBreakInSwitch
* [#2320](https://github.com/pmd/pmd/issues/2320): \[java] NullAssignment - FP with ternary and null as method argument
* [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case `new BigDecimal(Expression)`
@ -738,6 +799,7 @@ Language specific fixes:
* swift
* [#1877](https://github.com/pmd/pmd/pull/1877): \[swift] Feature/swift rules
* [#1882](https://github.com/pmd/pmd/pull/1882): \[swift] UnavailableFunction Swift rule
* [#4697](https://github.com/pmd/pmd/issues/4697): \[swift] Support Swift 5.9 features (mainly macros expansion expressions)
* xml
* [#1800](https://github.com/pmd/pmd/pull/1800): \[xml] Unimplement org.w3c.dom.Node from the XmlNodeWrapper
* xml-bestpractices
@ -786,8 +848,14 @@ Language specific fixes:
* [#4665](https://github.com/pmd/pmd/pull/4665): \[java] Doc: Fix references AutoClosable -> AutoCloseable - [Andrey Bozhko](https://github.com/AndreyBozhko) (@AndreyBozhko)
* [#4673](https://github.com/pmd/pmd/pull/4673): \[javascript] CPD: Added support for decorator notation - [Wener](https://github.com/wener-tiobe) (@wener-tiobe)
* [#4677](https://github.com/pmd/pmd/pull/4677): \[apex] Add new rule: OperationWithHighCostInLoop - [Thomas Prouvot](https://github.com/tprouvot) (@tprouvot)
* [#4698](https://github.com/pmd/pmd/pull/4698): \[swift] Add macro expansion support for swift 5.9 - [Richard B.](https://github.com/kenji21) (@kenji21)
* [#4706](https://github.com/pmd/pmd/pull/4706): \[java] DetachedTestCase should not report on abstract methods - [Debamoy Datta](https://github.com/Debamoy) (@Debamoy)
* [#4719](https://github.com/pmd/pmd/pull/4719): \[java] UnnecessaryCaseChange: example doc toUpperCase() should compare to a capitalized string - [ciufudean](https://github.com/ciufudean) (@ciufudean)
* [#4738](https://github.com/pmd/pmd/pull/4738): \[doc] Added reference to the PMD extension for bld - [Erik C. Thauvin](https://github.com/ethauvin) (@ethauvin)
* [#4749](https://github.com/pmd/pmd/pull/4749): Fixes NoSuchMethodError on processing errors in pmd-compat6 - [Andreas Bergander](https://github.com/bergander) (@bergander)
* [#4750](https://github.com/pmd/pmd/pull/4750): \[core] Fix flaky SummaryHTMLRenderer - [219sansim](https://github.com/219sansim) (@219sansim)
* [#4754](https://github.com/pmd/pmd/pull/4754): \[java] EmptyControlStatementRule: Add allowCommentedBlocks property - [Andreas Bergander](https://github.com/bergander) (@bergander)
* [#4759](https://github.com/pmd/pmd/pull/4759): \[java] fix: remove delimiter attribute from ruleset category/java/errorprone.xml - [Marcin Dąbrowski](https://github.com/marcindabrowski) (@marcindabrowski)
### 📈 Stats
* 5007 commits

View File

@ -154,8 +154,12 @@ See [the example report](report-examples/cpdhtml-v2.html).
### New: Swift support
Given the full Antlr support, PMD now fully supports Swift. We are pleased to announce we are shipping a number of
rules starting with PMD 7.
Given the full Antlr support, PMD now fully supports Swift for creating rules. Previously only CPD was supported.
Note: There is only limited support for newer Swift language features in the parser, e.g. Swift 5.9 (Macro Expansions)
are supported, but other features are not.
We are pleased to announce we are shipping a number of rules starting with PMD 7.
* {% rule "swift/errorprone/ForceCast" %} (`swift-errorprone`) flags all force casts, making sure you are
defensively considering all types. Having the application crash shouldn't be an option.
@ -275,6 +279,12 @@ Related issue: [[core] Explicitly name all language versions (#4120)](https://gi
With the new version of Apex Jorje, the new language constructs like User Mode Database Operations
can be parsed now. PMD should now be able to parse Apex code up to version 59.0 (Winter '23).
### Changed: Groovy Support (CPD)
* We now support parsing all Groovy features from Groovy 3 and 4.
* We now support [suppression](pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs.
* See [PR #4726](https://github.com/pmd/pmd/pull/4726) for details.
## 🌟 New and changed rules
### New Rules
@ -430,6 +440,7 @@ The following previously deprecated rules have been finally removed:
* {% deleted_rule apex/codestyle/VariableNamingConventions %} ➡️ use {% rule apex/codestyle/FieldNamingConventions %},
{% rule apex/codestyle/FormalParameterNamingConventions %}, {% rule apex/codestyle/LocalVariableNamingConventions %},
or {% rule apex/codestyle/PropertyNamingConventions %}
* {% deleted_rule apex/security/ApexCSRF %} ➡️ use {% rule apex/errorprone/ApexCSRF %}
**Java**

View File

@ -120,8 +120,6 @@ public class Foo {
</example>
</rule>
<rule name="ApexCSRF" ref="category/apex/errorprone.xml/ApexCSRF" deprecated="true"/>
<rule name="ApexDangerousMethods"
language="apex"
since="5.5.3"

View File

@ -10,7 +10,7 @@ These rules deal with different security problems that can occur within Apex.
<rule ref="category/apex/security.xml/ApexBadCrypto" deprecated="true" />
<rule ref="category/apex/security.xml/ApexCRUDViolation" deprecated="true" />
<rule ref="category/apex/security.xml/ApexCSRF" deprecated="true" />
<rule ref="category/apex/errorprone.xml/ApexCSRF" deprecated="true" />
<rule ref="category/apex/security.xml/ApexDangerousMethods" deprecated="true" />
<rule ref="category/apex/security.xml/ApexInsecureEndpoint" deprecated="true" />
<rule ref="category/apex/security.xml/ApexOpenRedirect" deprecated="true" />

View File

@ -13,6 +13,11 @@ public final class PmdCli {
private PmdCli() { }
public static void main(String[] args) {
// See https://github.com/remkop/picocli/blob/main/RELEASE-NOTES.md#-picocli-470
// and https://picocli.info/#_closures_in_annotations
// we don't use this feature. Disabling it avoids leaving the groovy jar open
// caused by Class.forName("groovy.lang.Closure")
System.setProperty("picocli.disable.closures", "true");
final CommandLine cli = new CommandLine(new PmdRootCommand())
.setCaseInsensitiveEnumValuesAllowed(true);

View File

@ -7,8 +7,9 @@ package net.sourceforge.pmd.cli.commands.internal;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.sourceforge.pmd.AbstractConfiguration;
import net.sourceforge.pmd.cli.commands.mixins.internal.EncodingMixin;
@ -25,14 +26,10 @@ public abstract class AbstractAnalysisPmdSubcommand<C extends AbstractConfigurat
@Mixin
protected EncodingMixin encoding;
@Option(names = { "--dir", "-d" },
description = "Path to a source file, or directory containing source files to analyze. "
+ "Zip and Jar files are also supported, if they are specified directly "
+ "(archive files found while exploring a directory are not recursively expanded). "
+ "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. "
+ "One of --dir, --file-list or --uri must be provided.",
arity = "1..*", split = ",")
protected List<Path> inputPaths;
// see the setters #setInputPaths and setPositionalInputPaths for @Option and @Parameters annotations
// Note: can't use annotations on the fields here, as otherwise the complete list would be replaced
// rather than accumulated.
protected Set<Path> inputPaths;
@Option(names = "--file-list",
description =
@ -50,16 +47,6 @@ public abstract class AbstractAnalysisPmdSubcommand<C extends AbstractConfigurat
+ "Disable this option with '--no-fail-on-violation' to exit with 0 instead and just write the report.",
defaultValue = "true", negatable = true)
protected boolean failOnViolation;
@Parameters(arity = "*", description = "Path to a source file, or directory containing source files to analyze. "
+ "Equivalent to using --dir.")
public void setInputPaths(final List<Path> inputPaths) {
if (this.inputPaths == null) {
this.inputPaths = new ArrayList<>();
}
this.inputPaths.addAll(inputPaths);
}
protected List<Path> relativizeRootPaths;
@ -69,7 +56,7 @@ public abstract class AbstractAnalysisPmdSubcommand<C extends AbstractConfigurat
+ "The option can be repeated, in which case the shortest relative path will be used. "
+ "If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered as absolute.",
arity = "1..*", split = ",")
public void setRelativizePathsWith(List<Path> rootPaths) {
protected void setRelativizePathsWith(List<Path> rootPaths) {
this.relativizeRootPaths = rootPaths;
for (Path path : this.relativizeRootPaths) {
@ -80,6 +67,27 @@ public abstract class AbstractAnalysisPmdSubcommand<C extends AbstractConfigurat
}
}
@Option(names = { "--dir", "-d" },
description = "Path to a source file, or directory containing source files to analyze. "
+ "Zip and Jar files are also supported, if they are specified directly "
+ "(archive files found while exploring a directory are not recursively expanded). "
+ "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. "
+ "One of --dir, --file-list or --uri must be provided.",
arity = "1..*", split = ",")
protected void setInputPaths(final List<Path> inputPaths) {
if (this.inputPaths == null) {
this.inputPaths = new LinkedHashSet<>(); // linked hashSet in order to maintain order
}
this.inputPaths.addAll(inputPaths);
}
@Parameters(arity = "*", description = "Path to a source file, or directory containing source files to analyze. "
+ "Equivalent to using --dir.")
protected void setPositionalInputPaths(final List<Path> inputPaths) {
this.setInputPaths(inputPaths);
}
@Override
protected final void validate() throws ParameterException {
super.validate();

View File

@ -105,7 +105,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand<CPDConfiguration>
configuration.setFailOnViolation(failOnViolation);
configuration.setInputFilePath(fileListPath);
if (inputPaths != null) {
configuration.setInputPathList(inputPaths);
configuration.setInputPathList(new ArrayList<>(inputPaths));
}
configuration.setIgnoreAnnotations(ignoreAnnotations);
configuration.setIgnoreIdentifiers(ignoreIdentifiers);

View File

@ -260,7 +260,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand<PMDConfiguration>
protected PMDConfiguration toConfiguration() {
final PMDConfiguration configuration = new PMDConfiguration();
if (inputPaths != null) {
configuration.setInputPathList(inputPaths);
configuration.setInputPathList(new ArrayList<>(inputPaths));
}
configuration.setInputFilePath(fileListPath);
configuration.setIgnoreFilePath(ignoreListPath);
@ -323,7 +323,8 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand<PMDConfiguration>
return CliExitCode.ERROR;
}
LOG.debug("Current classpath:\n{}", System.getProperty("java.class.path"));
LOG.debug("Runtime classpath:\n{}", System.getProperty("java.class.path"));
LOG.debug("Aux classpath: {}", configuration.getClassLoader());
if (showProgressBar) {
if (reportFile == null) {

View File

@ -87,6 +87,12 @@ class CpdCliTest extends BaseCliTest {
result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE"));
}
@Test
void debugLoggingShouldMentionLanguage() throws Exception {
final CliExecutionResult result = runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--debug");
result.checkStdErr(containsString("Created new FileCollector with LanguageVersionDiscoverer(LanguageRegistry(java))"));
}
@Test
void defaultLogging() throws Exception {
CliExecutionResult result = runCliSuccessfully("--minimum-tokens", "340", "--dir", SRC_DIR);
@ -112,6 +118,20 @@ class CpdCliTest extends BaseCliTest {
result.checkStdErr(containsString("Usage: pmd cpd"));
}
@Test
void testWrongCliOptionResultsInErrorLoggingAfterDir() throws Exception {
// --ignore-identifiers doesn't take an argument anymore - it is interpreted as a file for inputPaths
final CliExecutionResult result = runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "false");
result.checkStdErr(containsString("No such file false"));
}
@Test
void testWrongCliOptionResultsInErrorLoggingBeforeDir() throws Exception {
// --ignore-identifiers doesn't take an argument anymore - it is interpreted as a file for inputPaths
final CliExecutionResult result = runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--ignore-identifiers", "false", "--dir", SRC_DIR);
result.checkStdErr(containsString("No such file false"));
}
@Test
void testFindJavaDuplication() throws Exception {
runCli(VIOLATIONS_FOUND, "--minimum-tokens", "7", "--dir", SRC_DIR)
@ -125,7 +145,7 @@ class CpdCliTest extends BaseCliTest {
*/
@Test
void testIgnoreIdentifiers() throws Exception {
runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers")
runCli(VIOLATIONS_FOUND, "--minimum-tokens", "34", "--dir", SRC_DIR, "--ignore-identifiers", "false", "--debug")
.verify(result -> result.checkStdOut(containsString(
"Found a 14 line (89 tokens) duplication"
)));

View File

@ -0,0 +1,16 @@
<ruleset xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" name="Exception throwing ruleset">
<description/>
<rule name="ExceptionThrowingRule"
language="java"
class="net.sourceforge.pmd.lang.rule.XPathRule">
<description>Use this rule to produce a processing error.</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value><![CDATA[
error()
]]></value></property>
</properties>
</rule>
</ruleset>

View File

@ -30,8 +30,12 @@
</executions>
<configuration>
<printFailingErrors>true</printFailingErrors>
<skipPmdError>false</skipPmdError>
<skipPmdError>true</skipPmdError> <!-- we want to capture processing errors -->
<minimumTokens>5</minimumTokens>
<rulesets>
<ruleset>/rulesets/java/maven-pmd-plugin-default.xml</ruleset>
<ruleset>${project.basedir}/exception_ruleset.xml</ruleset>
</rulesets>
</configuration>
<dependencies>
<dependency>

View File

@ -29,10 +29,13 @@ String pmdXml = readFile(pmdXmlReport);
if (!pmdXml.contains("<violation beginline=\"5\" endline=\"5\" begincolumn=\"16\" endcolumn=\"37\" rule=\"UnusedLocalVariable\" ruleset=\"Best Practices\" package=\"org.example\" class=\"Main\" method=\"main\" variable=\"thisIsAUnusedLocalVar\"")) {
throw new RuntimeException("Expected violation has not been reported");
}
File mainFile = new File("pmd-for-java/src/main/java/org/example/Main.java");
File mainFile = new File(basedir, "src/main/java/org/example/Main.java");
if (!pmdXml.contains(mainFile + "\">")) {
throw new RuntimeException("Expected violation has not been reported");
}
if (!pmdXml.contains("<error filename=\"" + mainFile.getAbsolutePath()) || !pmdXml.contains(mainFile + "\" msg=\"PmdXPathException")) {
throw new RuntimeException("Processing error has not been reported");
}
File pmdCsvReport = new File(basedir, "target/pmd.csv");
if (!pmdCsvReport.exists()) {

View File

@ -143,6 +143,11 @@ public final class Report {
public Throwable getError() {
return error;
}
// ------------------- compat extensions --------------------
public String getFile() {
return file.getAbsolutePath();
}
}
/**

View File

@ -7,14 +7,27 @@ package net.sourceforge.pmd.internal.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@ -33,29 +46,38 @@ public class ClasspathClassLoader extends URLClassLoader {
private static final Logger LOG = LoggerFactory.getLogger(ClasspathClassLoader.class);
String javaHome;
private FileSystem fileSystem;
private Map<String, Set<String>> packagesDirsToModules;
static {
registerAsParallelCapable();
}
public ClasspathClassLoader(List<File> files, ClassLoader parent) throws IOException {
super(fileToURL(files), parent);
super(new URL[0], parent);
for (URL url : fileToURL(files)) {
addURL(url);
}
}
public ClasspathClassLoader(String classpath, ClassLoader parent) throws IOException {
super(initURLs(classpath), parent);
}
private static URL[] fileToURL(List<File> files) throws IOException {
List<URL> urlList = new ArrayList<>();
for (File f : files) {
urlList.add(f.toURI().toURL());
super(new URL[0], parent);
for (URL url : initURLs(classpath)) {
addURL(url);
}
return urlList.toArray(new URL[0]);
}
private static URL[] initURLs(String classpath) {
private List<URL> fileToURL(List<File> files) throws IOException {
List<URL> urlList = new ArrayList<>();
for (File f : files) {
urlList.add(createURLFromPath(f.getAbsolutePath()));
}
return urlList;
}
private List<URL> initURLs(String classpath) {
AssertionUtil.requireParamNotNull("classpath", classpath);
final List<URL> urls = new ArrayList<>();
try {
@ -69,10 +91,10 @@ public class ClasspathClassLoader extends URLClassLoader {
} catch (IOException e) {
throw new IllegalArgumentException("Cannot prepend classpath " + classpath + "\n" + e.getMessage(), e);
}
return urls.toArray(new URL[0]);
return urls;
}
private static void addClasspathURLs(final List<URL> urls, final String classpath) throws MalformedURLException {
private void addClasspathURLs(final List<URL> urls, final String classpath) throws MalformedURLException {
StringTokenizer toker = new StringTokenizer(classpath, File.pathSeparator);
while (toker.hasMoreTokens()) {
String token = toker.nextToken();
@ -81,7 +103,7 @@ public class ClasspathClassLoader extends URLClassLoader {
}
}
private static void addFileURLs(List<URL> urls, URL fileURL) throws IOException {
private void addFileURLs(List<URL> urls, URL fileURL) throws IOException {
try (BufferedReader in = new BufferedReader(new InputStreamReader(fileURL.openStream()))) {
String line;
while ((line = in.readLine()) != null) {
@ -95,9 +117,67 @@ public class ClasspathClassLoader extends URLClassLoader {
}
}
private static URL createURLFromPath(String path) throws MalformedURLException {
File file = new File(path);
return file.getAbsoluteFile().toURI().normalize().toURL();
private URL createURLFromPath(String path) throws MalformedURLException {
Path filePath = Paths.get(path).toAbsolutePath();
if (filePath.endsWith(Paths.get("lib", "jrt-fs.jar"))) {
initializeJrtFilesystem(filePath);
// don't add jrt-fs.jar to the normal aux classpath
return null;
}
return filePath.toUri().normalize().toURL();
}
/**
* Initializes a Java Runtime Filesystem that will be used to load class files.
* This allows end users to provide in the aux classpath another Java Runtime version
* than the one used for executing PMD.
*
* @param filePath path to the file "lib/jrt-fs.jar" inside the java installation directory.
* @see <a href="https://openjdk.org/jeps/220">JEP 220: Modular Run-Time Images</a>
*/
private void initializeJrtFilesystem(Path filePath) {
try {
LOG.debug("Detected Java Runtime Filesystem Provider in {}", filePath);
if (fileSystem != null) {
throw new IllegalStateException("There is already a jrt filesystem. Do you have multiple jrt-fs.jar files on the classpath?");
}
if (filePath.getNameCount() < 2) {
throw new IllegalArgumentException("Can't determine java home from " + filePath + " - please provide a complete path.");
}
try (URLClassLoader loader = new URLClassLoader(new URL[] { filePath.toUri().toURL() })) {
Map<String, String> env = new HashMap<>();
// note: providing java.home here is crucial, so that the correct runtime image is loaded.
// the class loader is only used to provide an implementation of JrtFileSystemProvider, if the current
// Java runtime doesn't provide one (e.g. if running in Java 8).
javaHome = filePath.getParent().getParent().toString();
env.put("java.home", javaHome);
LOG.debug("Creating jrt-fs with env {}", env);
fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env, loader);
}
packagesDirsToModules = new HashMap<>();
Path packages = fileSystem.getPath("packages");
try (Stream<Path> packagesStream = Files.list(packages)) {
packagesStream.forEach(p -> {
String packageName = p.getFileName().toString().replace('.', '/');
try (Stream<Path> modulesStream = Files.list(p)) {
Set<String> modules = modulesStream
.map(Path::getFileName)
.map(Path::toString)
.collect(Collectors.toSet());
packagesDirsToModules.put(packageName, modules);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
@ -105,7 +185,42 @@ public class ClasspathClassLoader extends URLClassLoader {
return getClass().getSimpleName()
+ "[["
+ StringUtils.join(getURLs(), ":")
+ "] parent: " + getParent() + ']';
+ "] jrt-fs: " + javaHome + " parent: " + getParent() + ']';
}
@Override
public InputStream getResourceAsStream(String name) {
// always first search in jrt-fs, if available
// note: we can't override just getResource(String) and return a jrt:/-URL, because the URL itself
// won't be connected to the correct JrtFileSystem and would just load using the system classloader.
if (fileSystem != null) {
int lastSlash = name.lastIndexOf('/');
String packageName = name.substring(0, Math.max(lastSlash, 0));
Set<String> moduleNames = packagesDirsToModules.get(packageName);
if (moduleNames != null) {
LOG.trace("Trying to find {} in jrt-fs with packageName={} and modules={}",
name, packageName, moduleNames);
for (String moduleCandidate : moduleNames) {
Path candidate = fileSystem.getPath("modules", moduleCandidate, name);
if (Files.exists(candidate)) {
LOG.trace("Found {}", candidate);
try {
// Note: The input streams from JrtFileSystem are ByteArrayInputStreams and do not
// need to be closed - we don't need to track these. The filesystem itself needs to be closed at the end.
// See https://github.com/openjdk/jdk/blob/970cd202049f592946f9c1004ea92dbd58abf6fb/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java#L334
return Files.newInputStream(candidate);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}
}
// search in the other jars of the aux classpath.
// this will call this.getResource, which will do a child-first search, see below.
return super.getResourceAsStream(name);
}
@Override
@ -126,24 +241,22 @@ public class ClasspathClassLoader extends URLClassLoader {
@Override
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// checking local
c = findClass(name);
} catch (final ClassNotFoundException | SecurityException e) {
// checking parent
// This call to loadClass may eventually call findClass again, in case the parent doesn't find anything.
c = super.loadClass(name, resolve);
}
}
throw new IllegalStateException("This class loader shouldn't be used to load classes");
}
if (resolve) {
resolveClass(c);
@Override
public void close() throws IOException {
if (fileSystem != null) {
fileSystem.close();
// jrt created an own classloader to load the JrtFileSystemProvider class out of the
// jrt-fs.jar. This needs to be closed manually.
ClassLoader classLoader = fileSystem.getClass().getClassLoader();
if (classLoader instanceof URLClassLoader) {
((URLClassLoader) classLoader).close();
}
return c;
packagesDirsToModules = null;
fileSystem = null;
}
super.close();
}
}

View File

@ -168,4 +168,11 @@ public class LanguageVersionDiscoverer {
public void onlyRecognizeLanguages(LanguageRegistry lang) {
this.languageRegistry = Objects.requireNonNull(lang);
}
@Override
public String toString() {
return "LanguageVersionDiscoverer(" + languageRegistry
+ (forcedVersion != null ? ",forcedVersion=" + forcedVersion : "")
+ ")";
}
}

View File

@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.ast;
import static java.lang.Math.max;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -25,14 +27,14 @@ public final class TokenMgrError extends FileAnalysisException {
*
* @param line Line number
* @param column Column number
* @param filename Filename. If unknown, it can be completed with {@link #setFileName(String)} later
* @param filename Filename. If unknown, it can be completed with {@link #setFileId(FileId)}} later
* @param message Message of the error
* @param cause Cause of the error, if any
*/
public TokenMgrError(int line, int column, @Nullable FileId filename, String message, @Nullable Throwable cause) {
super(message, cause);
this.line = line;
this.column = column;
this.line = max(line, 1);
this.column = max(column, 1);
if (filename != null) {
super.setFileId(filename);
}
@ -44,8 +46,8 @@ public final class TokenMgrError extends FileAnalysisException {
@InternalApi
public TokenMgrError(boolean eofSeen, String lexStateName, int errorLine, int errorColumn, String errorAfter, char curChar) {
super(makeReason(eofSeen, lexStateName, errorAfter, curChar));
line = errorLine;
column = errorColumn;
line = max(errorLine, 1);
column = max(errorColumn, 1);
}
public int getLine() {

View File

@ -122,6 +122,7 @@ public class AntlrNameDictionary {
case "@": return "at-symbol";
case "$": return "dollar";
case "#": return "hash";
case "\\": return "backslash";
case "/": return "slash";

View File

@ -70,6 +70,7 @@ public final class FileCollector implements AutoCloseable {
this.discoverer = discoverer;
this.reporter = reporter;
this.outerFsPath = outerFsPath;
LOG.debug("Created new FileCollector with {}", discoverer);
}
public void setRecursive(boolean collectFilesRecursively) {

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