diff --git a/.all-contributorsrc b/.all-contributorsrc index 924c39021e..2fdad95a30 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -7071,7 +7071,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/26581168?v=4", "profile": "https://github.com/mluckam", "contributions": [ - "code" + "code", + "bug" ] }, { @@ -7534,6 +7535,61 @@ "contributions": [ "bug" ] + }, + { + "login": "cowwoc", + "name": "Gili Tzabari", + "avatar_url": "https://avatars.githubusercontent.com/u/633348?v=4", + "profile": "https://github.com/cowwoc", + "contributions": [ + "bug" + ] + }, + { + "login": "bobalicious", + "name": "Rob Baillie", + "avatar_url": "https://avatars.githubusercontent.com/u/6523911?v=4", + "profile": "http://robertbaillie.blogspot.co.uk/", + "contributions": [ + "bug" + ] + }, + { + "login": "mdagcilar", + "name": "Metin Dagcilar", + "avatar_url": "https://avatars.githubusercontent.com/u/6627550?v=4", + "profile": "https://github.com/mdagcilar", + "contributions": [ + "bug" + ] + }, + { + "login": "kesslerj", + "name": "Jonas Keßler", + "avatar_url": "https://avatars.githubusercontent.com/u/25590499?v=4", + "profile": "https://github.com/kesslerj", + "contributions": [ + "bug" + ] + }, + { + "login": "Gold856", + "name": "Gold856", + "avatar_url": "https://avatars.githubusercontent.com/u/117957790?v=4", + "profile": "https://github.com/Gold856", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "anuragagarwal561994", + "name": "Anurag Agarwal", + "avatar_url": "https://avatars.githubusercontent.com/u/6075379?v=4", + "profile": "https://github.com/anuragagarwal561994", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e37d01fa33..b19e8cc950 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,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/24/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: | diff --git a/.github/workflows/git-repo-sync.yml b/.github/workflows/git-repo-sync.yml index d0327c4e2b..c564eeea92 100644 --- a/.github/workflows/git-repo-sync.yml +++ b/.github/workflows/git-repo-sync.yml @@ -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/24/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 diff --git a/.github/workflows/troubleshooting.yml b/.github/workflows/troubleshooting.yml index 010e2a0fc4..58b1c56fcc 100644 --- a/.github/workflows/troubleshooting.yml +++ b/.github/workflows/troubleshooting.yml @@ -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/24/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: | diff --git a/Gemfile.lock b/Gemfile.lock index 10d7e8eadd..858d9626a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ GEM addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) base64 (0.2.0) - bigdecimal (3.1.6) + bigdecimal (3.1.8) claide (1.1.0) claide-plugins (0.9.2) cork @@ -28,7 +28,7 @@ GEM octokit (>= 4.0) terminal-table (>= 1, < 4) differ (0.1.2) - et-orbi (1.2.8) + et-orbi (1.2.11) tzinfo faraday (2.9.0) faraday-net_http (>= 2.0, < 3.2) @@ -36,8 +36,8 @@ GEM faraday (>= 0.8) faraday-net_http (3.1.0) net-http - fugit (1.10.1) - et-orbi (~> 1, >= 1.2.7) + fugit (1.11.0) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) git (1.19.1) addressable (~> 2.8) @@ -46,13 +46,13 @@ GEM rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - liquid (5.4.0) + liquid (5.5.0) logger-colors (1.0.0) nap (1.1.0) net-http (0.4.1) uri no_proxy_fix (0.1.2) - nokogiri (1.16.2-x86_64-linux) + nokogiri (1.16.5-x86_64-linux) racc (~> 1.4) octokit (8.1.0) base64 @@ -66,12 +66,13 @@ GEM nokogiri (~> 1.13) rufus-scheduler (~> 3.8) slop (~> 4.9) - public_suffix (5.0.4) + public_suffix (5.0.5) raabro (1.4.0) - racc (1.7.3) + racc (1.8.0) rchardet (1.8.0) - rexml (3.2.6) - rouge (4.2.0) + rexml (3.2.8) + strscan (>= 3.0.9) + rouge (4.2.1) rufus-scheduler (3.9.1) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) @@ -79,6 +80,7 @@ GEM addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) slop (4.10.1) + strscan (3.1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) tzinfo (2.0.6) diff --git a/README.md b/README.md index 43c3df58a1..a08c66d76c 100644 --- a/README.md +++ b/README.md @@ -5,28 +5,26 @@ [](https://app.gitter.im/#/room/#pmd_pmd:gitter.im?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://github.com/pmd/pmd/actions) [](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd) -[](https://github.com/jvm-repo-rebuild/reproducible-central#net.sourceforge.pmd:pmd) +[](https://github.com/jvm-repo-rebuild/reproducible-central/tree/master/content/net/sourceforge/pmd#readme) [](https://coveralls.io/github/pmd/pmd) -[](https://www.codacy.com/gh/pmd/pmd/dashboard?utm_source=github.com&utm_medium=referral&utm_content=pmd/pmd&utm_campaign=Badge_Grade) +[](https://app.codacy.com/organizations/gh/pmd/dashboard) [](code_of_conduct.md) [](https://docs.pmd-code.org/latest/) -**PMD** is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, -unnecessary object creation, and so forth. It supports many languages. It can be extended with custom rules. -It uses JavaCC and Antlr to parse source files into abstract syntax trees (AST) and runs rules against them to find violations. -Rules can be written in Java or using a XPath query. +**PMD** is an extensible multilanguage static code analyzer. It finds common programming flaws like unused variables, +empty catch blocks, unnecessary object creation, and so forth. It's mainly concerned with **Java and +Apex**, but **supports 16 other languages**. It comes with **400+ built-in rules**. It can be +extended with custom rules. It uses JavaCC and Antlr to parse source files into abstract syntax trees +(AST) and runs rules against them to find violations. Rules can be written in Java or using a XPath query. -It supports Java, JavaScript, Salesforce.com Apex and Visualforce, -Modelica, PLSQL, Apache Velocity, HTML, XML and XSL. +Currently, PMD supports Java, JavaScript, Salesforce.com Apex and Visualforce, +Kotlin, Swift, Modelica, PLSQL, Apache Velocity, JSP, WSDL, Maven POM, HTML, XML and XSL. Scala is supported, but there are currently no Scala rules available. Additionally, it includes **CPD**, the copy-paste-detector. CPD finds duplicated code in -C/C++, C#, Dart, Fortran, Gherkin, Go, Groovy, HTML, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, -Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex and Visualforce, Scala, Swift, T-SQL, -Apache Velocity, and XML. - -In the future we hope to add support for data/control flow analysis and automatic (quick) fixes where -it makes sense. +Coco, C/C++, C#, Dart, Fortran, Gherkin, Go, Groovy, HTML, Java, JavaScript, JSP, Julia, Kotlin, +Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex and +Visualforce, Scala, Swift, T-SQL, Typescript, Apache Velocity, WSDL, XML and XSL. ## 🚀 Installation and Usage diff --git a/do-release.sh b/do-release.sh index 03807ce8b9..ef34e1063a 100755 --- a/do-release.sh +++ b/do-release.sh @@ -254,7 +254,7 @@ permalink: pmd_release_notes.html keywords: changelog, release notes --- -## {{ site.pmd.date }} - {{ site.pmd.version }} +## {{ site.pmd.date | date: "%d-%B-%Y" }} - {{ site.pmd.version }} The PMD team is pleased to announce PMD {{ site.pmd.version }}. diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 8f8b94acac..bd44b12521 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.1.3.2) + activesupport (7.1.3.3) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -14,7 +14,7 @@ GEM addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) base64 (0.2.0) - bigdecimal (3.1.6) + bigdecimal (3.1.8) coffee-script (2.4.1) coffee-script-source execjs @@ -23,8 +23,8 @@ GEM commonmarker (0.23.10) concurrent-ruby (1.2.3) connection_pool (2.4.1) - csv (3.2.8) - dnsruby (1.71.0) + csv (3.3.0) + dnsruby (1.72.1) simpleidn (~> 0.2.1) drb (2.2.1) em-websocket (0.5.3) @@ -96,7 +96,7 @@ GEM activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) - i18n (1.14.4) + i18n (1.14.5) concurrent-ruby (~> 1.0) jekyll (3.9.5) addressable (~> 2.4) @@ -219,23 +219,24 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.22.2) + minitest (5.23.1) mutex_m (0.2.0) net-http (0.4.1) uri - nokogiri (1.16.2-x86_64-linux) + nokogiri (1.16.5-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) sawyer (~> 0.9) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (5.0.4) - racc (1.7.3) + public_suffix (5.0.5) + racc (1.8.0) rb-fsevent (0.11.2) - rb-inotify (0.10.1) + rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.2.6) + rexml (3.2.8) + strscan (>= 3.0.9) rouge (3.30.0) rubyzip (2.3.2) safe_yaml (1.0.5) @@ -247,17 +248,14 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - simpleidn (0.2.1) - unf (~> 0.1.4) + simpleidn (0.2.3) + strscan (3.1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) 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.9.1) unicode-display_width (1.8.0) uri (0.13.0) webrick (1.8.1) diff --git a/docs/_config.yml b/docs/_config.yml index f53795d99a..29e464a84f 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,16 +1,17 @@ repository: pmd/pmd pmd: - version: 7.2.0-SNAPSHOT - previous_version: 7.1.0 - date: 31-May-2024 + version: 7.3.0-SNAPSHOT + previous_version: 7.2.0 + date: 2024-06-28 + # release types: major, minor, bugfix release_type: minor -# release types: major, minor, bugfix - output: web # this property is useful for conditional filtering of content that is separate from the PDF. +sidebar_title: PMD + topnav_title: PMD Source Code Analyzer Project # this appears on the top navigation bar next to the home button diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 1902b8761d..7fbceac6f1 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -1,7 +1,5 @@ entries: - title: sidebar - product: PMD - version: '!PMD_VERSION!' folders: - title: null output: pdf @@ -31,6 +29,12 @@ entries: - title: Getting help url: /pmd_about_help.html output: web, pdf + - title: Release policies + url: /pmd_about_release_policies.html + output: web, pdf + - title: Support lifecycle + url: /pmd_about_support_lifecycle.html + output: web, pdf - title: User Documentation output: web, pdf folderitems: diff --git a/docs/_includes/sidebar.html b/docs/_includes/sidebar.html index 46b916d90e..c68eb9a8a0 100644 --- a/docs/_includes/sidebar.html +++ b/docs/_includes/sidebar.html @@ -1,8 +1,8 @@ {% include custom/sidebarconfigs.html %}
ASTResource#getStableName
and the corresponding attribute `@StableName`
+
+### ✨ External Contributions
+
+* [#5020](https://github.com/pmd/pmd/issues/5020): \[java] Fix AvoidUsingOctalValues false-positive - [Gold856](https://github.com/Gold856) (@Gold856)
+
+### 📈 Stats
+* 152 commits
+* 46 closed tickets & PRs
+* Days since last release: 35
+
## 26-April-2024 - 7.1.0
The PMD team is pleased to announce PMD 7.1.0.
diff --git a/pmd-ant/pom.xml b/pmd-ant/pom.xml
index ce517cdd1a..dc93238cd3 100644
--- a/pmd-ant/pom.xml
+++ b/pmd-ant/pom.xml
@@ -7,7 +7,7 @@
Note that {@link TypeSystem#ERROR} and {@link TypeSystem#UNKNOWN}
+ * are considered subtypes of anything.
+ *
+ * @param t A type T
+ * @param s A type S
+ */
+ Convertibility isConvertible(@NonNull JTypeMirror t, @NonNull JTypeMirror s, boolean capture) {
+ // This is commented out as it makes JTypeMirror#isSubtypeOf partial,
+ // which is not nice for the API... But this assert caught a bug and
+ // should probably be enabled.
+ // assert !(t instanceof JWildcardType || s instanceof JWildcardType) : "Wildcards do not support subtyping";
+
+ if (t == s) {
+ Objects.requireNonNull(t);
return Convertibility.SUBTYPING;
+ } else if (s.isTop()) {
+ return Convertibility.subtypeIf(!t.isPrimitive());
+ } else if (s.isVoid() || t.isVoid()) { // t != s
+ return Convertibility.NEVER;
+ } else if (s instanceof InferenceVar) {
+ if (!pure) {
+ // it's possible to add a bound to UNKNOWN or ERROR
+ ((InferenceVar) s).addBound(BoundKind.LOWER, t);
+ }
+ return Convertibility.SUBTYPING;
+ } else if (isTypeRange(s)) {
+ // If s is a type range L..U,
+ // then showing t <: s is the same thing as t <: L
+ JTypeMirror lower = lowerBoundRec(s);
+ if (!lower.isBottom()) {
+ return isConvertible(t, lower);
+ }
+ // otherwise fallthrough
+ } else if (isSpecialUnresolved(t)) {
+ // error type or unresolved type is subtype of everything
+ if (s instanceof JArrayType) {
+ // In case the array has an ivar 'a as element type, a bound will be added 'a >: (*unknown*)
+ // This helps inference recover in call chains and propagate the (*unknown*) types gracefully.
+ return TypeOps.isConvertible(t, ((JArrayType) s).getElementType());
+ }
+ return Convertibility.SUBTYPING;
+ } else if (hasUnresolvedSymbol(t) && t instanceof JClassType) {
+ // This also considers types with an unresolved symbol
+ // subtypes of (nearly) anything. This allows them to
+ // pass bound checks on type variables.
+ if (Objects.equals(t.getSymbol(), s.getSymbol())) {
+ return typeArgsAreContained((JClassType) t, (JClassType) s);
+ } else {
+ return Convertibility.subtypeIf(s instanceof JClassType); // excludes array or so
+ }
+ } else if (s instanceof JIntersectionType) { // TODO test intersection with tvars & arrays
+ // If S is an intersection, then T must conform to *all* bounds of S
+ // Symmetrically, if T is an intersection, T <: S requires only that
+ // at least one bound of T is a subtype of S.
+ return subtypesAll(t, asList(s));
}
- if (sw.isUpperBound()) {
- // Test U(T) <: U(S), we already know L(S) <: L(T), because L(S) is bottom
- return isConvertible(wildUpperBound(t), sw.asUpperBound());
- } else {
- // Test L(S) <: L(T), we already know U(T) <: U(S), because U(S) is top
- return isConvertible(sw.asLowerBound(), wildLowerBound(t));
+ if (capture) {
+ t = capture(t);
}
+ return t.acceptVisitor(this, s);
}
- return Convertibility.NEVER;
- }
+ Convertibility subtypesAll(JTypeMirror t, Iterable extends JTypeMirror> supers) {
+ Convertibility result = Convertibility.SUBTYPING;
+ for (JTypeMirror ui : supers) {
+ Convertibility sub = isConvertible(t, ui);
+ if (sub == Convertibility.NEVER) {
+ return Convertibility.NEVER;
+ }
+ result = result.and(sub);
+ }
+ return result;
+ }
-
- /**
- * Generalises containment to check if for each i, {@code Ti <= Si}.
- */
- static Convertibility typeArgsAreContained(JClassType t, JClassType s) {
- List S contains T if:
+ *
+ * {@code L(S) <: L(T) && U(T) <: U(S)}
+ *
+ * This only makes sense for type arguments, it's a component of
+ * subtype checks for parameterized types:
+ *
+ * {@code C Defined in JLS§4.5.1 (Type Arguments of Parameterized Types)
+ */
+ Convertibility typeArgContains(JTypeMirror s, JTypeMirror t) {
+ // the contains relation can be understood intuitively if we
+ // represent types as ranges on a line:
- private static final class SubtypeVisitor implements JTypeVisitor If only one explicit test is written, then use "parserTest" as the grouping
+ * and just put in assertions. A single test case will be defined implicitly then.
+ *
+ * <: C
*
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java
index ff635788b1..5a47ae6223 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/internal/LazyTypeResolver.java
@@ -326,8 +326,8 @@ public final class LazyTypeResolver extends JavaVisitorBasefoo().{@code
+ * class MyTest : ParserTestSpec({
+ * parserTestContainer("Simple additive expression should be flat") {
+ * inContext(ExpressionParsingCtx) {
+ * "1 + 2 + 3" should parseAs {
+ * infixExpr(ADD) {
+ * infixExpr(ADD) {
+ * int(1)
+ * int(2)
+ * }
+ * int(3)
+ * }
+ * }
+ * }
+ * doTest("another test case") {
+ * //...
+ * }
+ * }
+ *
+ * parserTest("Test annotations on module", javaVersions = since(J9)) {
+ * val root: ASTCompilationUnit = parser.withProcessing(true).parse("@A @a.B module foo { } ")
+ * root.moduleDeclaration.shouldMatchNode
*
* @author Clément Fournier
*/
@@ -50,7 +84,6 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
test = test
)
-
/**
* Defines a group of tests that should be named similarly,
* with separate tests for separate versions.
@@ -67,7 +100,7 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
* new parser test.
*
*/
- fun parserTestGroup(name: String,
+ private fun parserTestGroup(name: String,
disabled: Boolean = false,
spec: suspend GroupTestCtx.() -> Unit) =
addContainer(
@@ -77,31 +110,47 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
config = null
)
- /**
- * Defines a group of tests that should be named similarly.
- * Calls to "should" in the block are intercepted to create
- * a new test, with the given [name] as a common prefix.
- *
- * This is useful to make a batch of grammar specs for grammar
- * regression tests without bothering to find a name.
- *
- * @param name Name of the container test
- * @param javaVersion Language versions to use when parsing
- * @param spec Assertions. Each call to [io.kotest.matchers.should] on a string
- * receiver is replaced by a [GroupTestCtx.should], which creates a
- * new parser test.
- *
- */
fun parserTest(name: String,
javaVersion: JavaVersion = JavaVersion.Latest,
spec: suspend GroupTestCtx.VersionedTestCtx.() -> Unit) =
- parserTest(name, listOf(javaVersion), spec)
+ parserTest(name, listOf(javaVersion), spec)
+
+ /**
+ * Defines a single test case, that is executed on several java versions
+ * and grouped by the given [name].
+ *
+ * @param name Name of the container test
+ * @param javaVersions Language versions fo which to generate tests
+ * @param spec Assertions.
+ */
+ fun parserTest(name: String,
+ javaVersions: List