Merge branch 'master' into experimental-apex-parser
This commit is contained in:
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
#
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -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: |
|
||||
|
2
.github/workflows/git-repo-sync.yml
vendored
2
.github/workflows/git-repo-sync.yml
vendored
@ -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
|
||||
|
2
.github/workflows/troubleshooting.yml
vendored
2
.github/workflows/troubleshooting.yml
vendored
@ -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: |
|
||||
|
16
Gemfile.lock
16
Gemfile.lock
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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<Pattern> 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
|
||||
|
@ -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**
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
)));
|
||||
|
16
pmd-compat6/src/it/pmd-for-java/exception_ruleset.xml
Normal file
16
pmd-compat6/src/it/pmd-for-java/exception_ruleset.xml
Normal 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>
|
||||
|
@ -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>
|
||||
|
@ -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()) {
|
||||
|
@ -143,6 +143,11 @@ public final class Report {
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
// ------------------- compat extensions --------------------
|
||||
public String getFile() {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 : "")
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -122,6 +122,7 @@ public class AntlrNameDictionary {
|
||||
|
||||
case "@": return "at-symbol";
|
||||
case "$": return "dollar";
|
||||
case "#": return "hash";
|
||||
|
||||
case "\\": return "backslash";
|
||||
case "/": return "slash";
|
||||
|
@ -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
Reference in New Issue
Block a user