diff --git a/.all-contributorsrc b/.all-contributorsrc index dcac68bf60..77965bd302 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -3384,7 +3384,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/242337?v=4", "profile": "https://github.com/jjlharrison", "contributions": [ - "bug" + "bug", + "code" ] }, { @@ -6639,6 +6640,96 @@ "contributions": [ "code" ] + }, + { + "login": "lukelukes", + "name": "lukelukes", + "avatar_url": "https://avatars.githubusercontent.com/u/45536418?v=4", + "profile": "https://github.com/lukelukes", + "contributions": [ + "code" + ] + }, + { + "login": "vibhory2j", + "name": "Vibhor Goyal", + "avatar_url": "https://avatars.githubusercontent.com/u/15845016?v=4", + "profile": "https://github.com/vibhory2j", + "contributions": [ + "bug" + ] + }, + { + "login": "Ramel0921", + "name": "Ramel0921", + "avatar_url": "https://avatars.githubusercontent.com/u/104978096?v=4", + "profile": "https://github.com/Ramel0921", + "contributions": [ + "bug" + ] + }, + { + "login": "flyhard", + "name": "Per Abich", + "avatar_url": "https://avatars.githubusercontent.com/u/409466?v=4", + "profile": "https://github.com/flyhard", + "contributions": [ + "code" + ] + }, + { + "login": "filipponova", + "name": "Filippo Nova", + "avatar_url": "https://avatars.githubusercontent.com/u/12506636?v=4", + "profile": "https://github.com/filipponova", + "contributions": [ + "bug" + ] + }, + { + "login": "dalizi007", + "name": "dalizi007", + "avatar_url": "https://avatars.githubusercontent.com/u/90743616?v=4", + "profile": "https://github.com/dalizi007", + "contributions": [ + "code" + ] + }, + { + "login": "shiomiyan", + "name": "shiomiyan", + "avatar_url": "https://avatars.githubusercontent.com/u/35842766?v=4", + "profile": "https://github.com/shiomiyan", + "contributions": [ + "doc" + ] + }, + { + "login": "lgemeinhardt", + "name": "lgemeinhardt", + "avatar_url": "https://avatars.githubusercontent.com/u/1395165?v=4", + "profile": "https://github.com/lgemeinhardt", + "contributions": [ + "bug" + ] + }, + { + "login": "HaelC", + "name": "Haoliang Chen", + "avatar_url": "https://avatars.githubusercontent.com/u/16898273?v=4", + "profile": "https://haelchan.me/", + "contributions": [ + "bug" + ] + }, + { + "login": "FSchliephacke", + "name": "FSchliephacke", + "avatar_url": "https://avatars.githubusercontent.com/u/10260493?v=4", + "profile": "https://github.com/FSchliephacke", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/.ci/build.sh b/.ci/build.sh index ebacd1cf0c..828fcb9bbf 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -245,12 +245,15 @@ ${rendered_release_notes}" # Runs the dogfood ruleset with the currently built pmd against itself # function pmd_ci_dogfood() { + local mpmdVersion=() ./mvnw versions:set -DnewVersion="${PMD_CI_MAVEN_PROJECT_VERSION}-dogfood" -DgenerateBackupPoms=false sed -i 's/[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}.*<\/version>\( *\)/'"${PMD_CI_MAVEN_PROJECT_VERSION}"'<\/version>\1/' pom.xml if [ "${PMD_CI_MAVEN_PROJECT_VERSION}" = "7.0.0-SNAPSHOT" ]; then sed -i 's/pmd-dogfood-config\.xml/pmd-dogfood-config7.xml/' pom.xml + mpmdVersion=(-Denforcer.skip=true -Dpmd.plugin.version=3.18.0-pmd7-SNAPSHOT) fi ./mvnw verify --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" \ + "${mpmdVersion[@]}" \ -DskipTests \ -Dmaven.javadoc.skip=true \ -Dmaven.source.skip=true \ diff --git a/.ci/files/project-list.xml b/.ci/files/project-list.xml index 3ecd924ece..ce357698bc 100644 --- a/.ci/files/project-list.xml +++ b/.ci/files/project-list.xml @@ -40,6 +40,11 @@ mvn dependency:build-classpath -DincludeScope=test -Dmdep.outputFile=classpath.t .*/build/generated-sources/.* classpath.txt ]]> diff --git a/.ci/git-repo-sync.sh b/.ci/git-repo-sync.sh index a32d6c9b03..af0635d9ba 100755 --- a/.ci/git-repo-sync.sh +++ b/.ci/git-repo-sync.sh @@ -27,7 +27,7 @@ function git_repo_sync() { pmd_ci_log_group_start "Git Sync" git remote add pmd-sf "${PMD_SF_USER}@git.code.sf.net:/p/pmd/code" if [ -n "${PMD_CI_BRANCH}" ]; then - git push pmd-sf "${PMD_CI_BRANCH}:${PMD_CI_BRANCH}" + retry 5 git push pmd-sf "${PMD_CI_BRANCH}:${PMD_CI_BRANCH}" pmd_ci_log_success "Successfully pushed ${PMD_CI_BRANCH} to sourceforge" elif [ -n "${PMD_CI_TAG}" ]; then git push pmd-sf tag "${PMD_CI_TAG}" @@ -39,6 +39,43 @@ function git_repo_sync() { pmd_ci_log_group_end } + +# +# From: https://gist.github.com/sj26/88e1c6584397bb7c13bd11108a579746 +# +# Retry a command up to a specific number of times until it exits successfully, +# with exponential back off. +# +# $ retry 5 echo Hello +# Hello +# +# $ retry 5 false +# Retry 1/5 exited 1, retrying in 1 seconds... +# Retry 2/5 exited 1, retrying in 2 seconds... +# Retry 3/5 exited 1, retrying in 4 seconds... +# Retry 4/5 exited 1, retrying in 8 seconds... +# Retry 5/5 exited 1, no more retries left. +# +function retry { + local retries=$1 + shift + + local count=0 + until "$@"; do + exit=$? + wait=$((2 ** $count)) + count=$(($count + 1)) + if [ $count -lt $retries ]; then + echo "Retry $count/$retries exited $exit, retrying in $wait seconds..." + sleep $wait + else + echo "Retry $count/$retries exited $exit, no more retries left." + return $exit + fi + done + return 0 +} + git_repo_sync exit 0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d52aa82b5..01b91ce2d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: run: | echo "LANG=en_US.UTF-8" >> $GITHUB_ENV echo "MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV - echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/17/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/18/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 250dd00933..a990a6726b 100644 --- a/.github/workflows/git-repo-sync.yml +++ b/.github/workflows/git-repo-sync.yml @@ -22,7 +22,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/17/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/18/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 04ac1cd39c..ea4bd54fd9 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=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV - echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/17/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/18/scripts" >> $GITHUB_ENV - name: Check Environment shell: bash run: | diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index db95c131dd..25a1876bb8 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/Gemfile.lock b/Gemfile.lock index f20255105a..b41e9645e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,14 +68,14 @@ GEM multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.13.5) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) - pmdtester (1.5.0) + pmdtester (1.5.1) differ (~> 0.1) liquid (~> 5.2) logger-colors (~> 1.0) diff --git a/antlr4-wrapper.xml b/antlr4-wrapper.xml index 2e4b617e27..c2d3e224ff 100644 --- a/antlr4-wrapper.xml +++ b/antlr4-wrapper.xml @@ -8,7 +8,7 @@ - lang-name: matches the grammar name (eg "Swift") - lang-terse-name: uncapitalized package name (eg "swift") - node-prefix: prefix for generated AST nodes (eg "Sw") - - root-node-name: name of the root node (eg "TopLevel"), will be made to implement RootNode + - root-node-name: name of the root node without prefix (eg "TopLevel"), will be made to implement RootNode See AntlrGeneratedParserBase @@ -31,9 +31,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -45,7 +81,7 @@ - + diff --git a/do-release.sh b/do-release.sh index 5cd900bf32..3ddb331bbd 100755 --- a/do-release.sh +++ b/do-release.sh @@ -72,6 +72,14 @@ export RELEASE_VERSION export DEVELOPMENT_VERSION export CURRENT_BRANCH +# check for SNAPSHOT version of pmd.build-tools.version +BUILD_TOOLS_VERSION=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=pmd.build-tools.version -q -DforceStdout) +BUILD_TOOLS_VERSION_RELEASE=${BUILD_TOOLS_VERSION%-SNAPSHOT} +if [ "${BUILD_TOOLS_VERSION}" != "${BUILD_TOOLS_VERSION_RELEASE}" ]; then + echo "Error: version pmd.build-tools.version is ${BUILD_TOOLS_VERSION} - snapshot is not allowed" + exit 1 +fi + RELEASE_RULESET="pmd-core/src/main/resources/rulesets/releases/${RELEASE_VERSION//\./}.xml" echo "* Update date info in **docs/_config.yml**." diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 020814c547..af044ad8d5 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.4.8) + activesupport (6.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -232,7 +232,7 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.15.0) multipart-post (2.1.1) - nokogiri (1.13.5) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) diff --git a/docs/_config.yml b/docs/_config.yml index 13469ef975..f89896c168 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,7 +2,7 @@ repository: pmd/pmd pmd: version: 7.0.0-SNAPSHOT - previous_version: 6.45.0 + previous_version: 6.47.0 date: ??-?????-2022 release_type: major diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 75aa7aeda3..2680579b33 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -244,6 +244,21 @@ entries: - title: Security output: web, pdf url: /pmd_rules_jsp_security.html + - title: null + output: web, pdf + subfolders: + - title: Kotlin Rules + output: web, pdf + subfolderitems: + - title: Index + output: web, pdf + url: /pmd_rules_kotlin.html + - title: Best Practices + output: web, pdf + url: /pmd_rules_kotlin_bestpractices.html + - title: Error Prone + output: web, pdf + url: /pmd_rules_kotlin_errorprone.html - title: null output: web, pdf subfolders: @@ -382,6 +397,9 @@ entries: - title: JSP url: /pmd_languages_jsp.html output: web, pdf + - title: Kotlin + url: /pmd_languages_kotlin.html + output: web, pdf - title: PLSQL url: /pmd_languages_plsql.html output: web, pdf diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index cc3d9a0dc9..be94436737 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -49,6 +49,26 @@ Given the full Antlr support, PMD now fully supports Swift. We are pleased to an * {% rule "swift/bestpractices/UnavailableFunction" %} (`swift-bestpractices`) flags any function throwing a `fatalError` not marked as `@available(*, unavailable)` to ensure no calls are actually performed in the codebase. +Contributors: [@lsoncini](https://github.com/lsoncini), [@matifraga](https://github.com/matifraga), [@tomidelucca](https://github.com/tomidelucca) + +#### Kotlin support (experimental) + +PMD now supports Kotlin as an additional language for analyzing source code. It is based on +the official kotlin Antlr grammar. Java-based rules and XPath-based rules are supported. + +Kotlin support has **experimental** stability level, meaning no compatibility should +be expected between even incremental releases. Any functionality can be added, removed or changed without +warning. + +We are shipping the following rules: + +* {% rule kotlin/bestpractices/FunctionNameTooShort %} (`kotlin-bestpractices`) finds functions with a too short name. +* {% rule kotlin/errorprone/OverrideBothEqualsAndHashcode %} (`kotlin-errorprone`) finds classes with only either `equals` + or `hashCode` overridden, but not both. This leads to unexpected behavior once instances of such classes + are used in collections (Lists, HashMaps, ...). + +Contributors: [@jborgers](https://github.com/jborgers), [@stokpop](https://github.com/stokpop) + #### XPath 3.1 support Support for XPath versions 1.0, 1.0-compatibility was removed, support for XPath 2.0 is deprecated. The default (and only) supported XPath version is now XPath 3.1. This version of the XPath language is mostly identical to XPath 2.0. Notable changes: @@ -161,14 +181,14 @@ The following previously deprecated rules have been finally removed: ### Fixed Issues -* miscellaneous +* miscellaneous * [#896](https://github.com/pmd/pmd/issues/896): \[all] Use slf4j * [#1451](https://github.com/pmd/pmd/issues/1451): \[core] RulesetFactoryCompatibility stores the whole ruleset file in memory as a string -* cli +* cli * [#3828](https://github.com/pmd/pmd/issues/3828): \[core] Progress reporting -* apex-design +* apex-design * [#2667](https://github.com/pmd/pmd/issues/2667): \[apex] Integrate nawforce/ApexLink to build robust Unused rule -* java-bestpractices +* 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 * [#770](https://github.com/pmd/pmd/issues/770): \[java] UnusedPrivateMethod yields false positive for counter-variant arguments @@ -243,6 +263,8 @@ The following previously deprecated rules have been finally removed: * java-performance * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class * [#2712](https://github.com/pmd/pmd/issues/2712): \[java] SimplifyStartsWith false-positive with AssertJ +* kotlin + * [#419](https://github.com/pmd/pmd/issues/419): \[kotlin] Add support for Kotlin ### API Changes diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index 0deeb1ba60..deccde46f6 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -246,6 +246,43 @@ the breaking API changes will be performed in 7.0.0. an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0, we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %} +#### 6.47.0 + +No changes. + +#### 6.46.0 + +##### Deprecated ruleset references + +Ruleset references with the following formats are now deprecated and will produce a warning +when used on the CLI or in a ruleset XML file: +- `-`, eg `java-basic`, which resolves to `rulesets/java/basic.xml` +- the internal release number, eg `600`, which resolves to `rulesets/releases/600.xml` + +Use the explicit forms of these references to be compatible with PMD 7. + +##### Deprecated API + +- {% jdoc core::RuleSetReferenceId#toString() %} is now deprecated. The format of this + method will remain the same until PMD 7. The deprecation is intended to steer users + away from relying on this format, as it may be changed in PMD 7. +- {% jdoc core::PMDConfiguration#getInputPaths() %} and + {% jdoc core::PMDConfiguration#setInputPaths(java.lang.String) %} are now deprecated. + A new set of methods have been added, which use lists and do not rely on comma splitting. + +##### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +- {% jdoc core::cpd.CPDCommandLineInterface %} has been internalized. In order to execute CPD either + {% jdoc !!core::cpd.CPD#run(java.lang.String...) %} or {% jdoc !!core::cpd.CPD#main(java.lang.String[]) %} + should be used. +- Several members of {% jdoc test::cli.BaseCPDCLITest %} have been deprecated with replacements. +- The methods {% jdoc !!core::ant.Formatter#start(java.lang.String) %}, + {% jdoc !!core::ant.Formatter#end(net.sourceforge.pmd.Report) %}, {% jdoc !!core::ant.Formatter#getRenderer() %}, + and {% jdoc !!core::ant.Formatter#isNoOutputSupplied() %} have been internalized. + #### 6.45.0 ##### Experimental APIs @@ -1468,18 +1505,18 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see * The Java rules NcssConstructorCount (java-design), NcssMethodCount (java-design), and NcssTypeCount (java-design) have been deprecated. They will be replaced by the new rule {% rule java/design/NcssCount %} in the category `design`. -* The Java rule `LooseCoupling` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `bestpractices` instead. +* The Java rule `LooseCoupling` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `bestpractices` instead. -* The Java rule `CloneMethodMustImplementCloneable` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `errorprone` instead. +* The Java rule `CloneMethodMustImplementCloneable` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `errorprone` instead. -* The Java rule `UnusedImports` in ruleset `java-typeresolution` is deprecated. Use the rule with +* The Java rule `UnusedImports` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `bestpractices` instead. -* The Java rule `SignatureDeclareThrowsException` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `design` instead. +* The Java rule `SignatureDeclareThrowsException` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name from category `design` instead. -* The Java rule `EmptyStaticInitializer` in ruleset `java-empty` is deprecated. Use the rule {% rule java/errorprone/EmptyInitializer %}, which covers both static and non-static empty initializers.` +* The Java rule `EmptyStaticInitializer` in ruleset `java-empty` is deprecated. Use the rule {% rule java/errorprone/EmptyInitializer %}, which covers both static and non-static empty initializers.` -* The Java rules `GuardDebugLogging` (ruleset `java-logging-jakarta-commons`) and `GuardLogStatementJavaUtil` +* The Java rules `GuardDebugLogging` (ruleset `java-logging-jakarta-commons`) and `GuardLogStatementJavaUtil` (ruleset `java-logging-java`) have been deprecated. Use the rule {% rule java/bestpractices/GuardLogStatement %}, which covers all cases regardless of the logging framework. * The Java rule "java/multithreading/UnsynchronizedStaticDateFormatter" has been deprecated and @@ -1490,20 +1527,20 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see and [`PositionLiteralsFirstInCaseInsensitiveComparisons`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_bestpractices.html#positionliteralsfirstincaseinsensitivecomparisons) (ruleset `java-bestpractices`) have been deprecated in favor of the new rule {% rule "java/bestpractices/LiteralsFirstInComparisons" %}. -* The Java rule [`AvoidFinalLocalVariable`](https://pmd.github.io/pmd-6.16.0/pmd_rules_java_codestyle.html#avoidfinallocalvariable) (`java-codestyle`) has been deprecated +* The Java rule [`AvoidFinalLocalVariable`](https://pmd.github.io/pmd-6.16.0/pmd_rules_java_codestyle.html#avoidfinallocalvariable) (`java-codestyle`) has been deprecated and will be removed with PMD 7.0.0. The rule is controversial and also contradicts other existing rules such as [`LocalVariableCouldBeFinal`](https://pmd.github.io/pmd-6.16.0/pmd_rules_java_codestyle.html#localvariablecouldbefinal). If the goal is to avoid defining constants in a scope smaller than the class, then the rule [`AvoidDuplicateLiterals`](https://pmd.github.io/pmd-6.16.0/pmd_rules_java_errorprone.html#avoidduplicateliterals) should be used instead. -* The Apex rule [`VariableNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#variablenamingconventions) (`apex-codestyle`) has been deprecated and +* The Apex rule [`VariableNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#variablenamingconventions) (`apex-codestyle`) has been deprecated and will be removed with PMD 7.0.0. The rule is replaced by the more general rules [`FieldNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#fieldnamingconventions), [`FormalParameterNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#formalparameternamingconventions), [`LocalVariableNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#localvariablenamingconventions), and [`PropertyNamingConventions`](https://pmd.github.io/pmd-6.15.0/pmd_rules_apex_codestyle.html#propertynamingconventions). -* The Java rule [`LoggerIsNotStaticFinal`](https://pmd.github.io/pmd-6.15.0/pmd_rules_java_errorprone.html#loggerisnotstaticfinal) (`java-errorprone`) has been deprecated +* The Java rule [`LoggerIsNotStaticFinal`](https://pmd.github.io/pmd-6.15.0/pmd_rules_java_errorprone.html#loggerisnotstaticfinal) (`java-errorprone`) has been deprecated and will be removed with PMD 7.0.0. The rule is replaced by [`ProperLogger`](https://pmd.github.io/pmd-6.15.0/pmd_rules_java_errorprone.html#properlogger). * The Java rule [`DataflowAnomalyAnalysis`](https://pmd.github.io/pmd-6.27.0/pmd_rules_java_errorprone.html#dataflowanomalyanalysis) (`java-errorprone`) @@ -1516,7 +1553,7 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see * The Java rule `CloneThrowsCloneNotSupportedException` (java-errorprone) has been deprecated without replacement. -* The following Java rules are deprecated and removed from the quickstart ruleset, +* The following Java rules are deprecated and removed from the quickstart ruleset, as the new rule {% rule java/bestpractices/SimplifiableTestAssertion %} merges their functionality: * `UseAssertEqualsInsteadOfAssertTrue` (java-bestpractices) @@ -1529,7 +1566,7 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see the quickstart ruleset, as the new rule {% rule java/errorprone/ReturnEmptyCollectionRatherThanNull %} supersedes it. -* The following Java rules are deprecated and removed from the quickstart ruleset, +* The following Java rules are deprecated and removed from the quickstart ruleset, as the new rule {% rule java/bestpractices/PrimitiveWrapperInstantiation %} merges their functionality: * java/performance/BooleanInstantiation @@ -1543,3 +1580,17 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see as it finds only contrived cases of creating a primitive wrapper and unboxing it explicitly in the same expression. In PMD 7 this and more cases will be covered by a new rule `UnnecessaryBoxing`. + +* Since 6.46.0: The following Java rules are deprecated and removed from the quickstart ruleset, as the new rule + {% rule java/codestyle/EmptyControlStatement %} merges their functionality: + * {% rule java/errorprone/EmptyFinallyBlock %} + * {% rule java/errorprone/EmptyIfStmt %} + * {% rule java/errorprone/EmptyInitializer %} + * {% rule java/errorprone/EmptyStatementBlock %} + * {% rule java/errorprone/EmptySwitchStatements %} + * {% rule java/errorprone/EmptySynchronizedBlock %} + * {% rule java/errorprone/EmptyTryBlock %} + * {% rule java/errorprone/EmptyWhileStmt %} + +* Since 6.46.0: The Java rule {% rule java/errorprone/EmptyStatementNotInLoop %} is deprecated and removed from the quickstart + ruleset. Use the new rule {% rule java/codestyle/UnnecessarySemicolon %} instead. diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md index 7f156054d7..567db3d6c2 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md @@ -3,17 +3,54 @@ title: Adding PMD support for a new ANTLR grammar based language short_title: Adding a new language with ANTLR tags: [devdocs, extending] summary: "How to add a new language to PMD using ANTLR grammar." -last_updated: July 21, 2019 +last_updated: October 2021 sidebar: pmd_sidebar permalink: pmd_devdocs_major_adding_new_language_antlr.html folder: pmd/devdocs -# needs to be changed to branch master instead of pmd/7.0.x +# +# needs to be changed to branch master instead of pmd/7.0.x once pmd7 is released # https://github.com/pmd/pmd/blob/pmd/7.0.x -> https://github.com/pmd/pmd/blob/master +# --- +{% include callout.html type="warning" content=" -## 1. Start with a new sub-module. +**Before you start...**

+ +This is really a big contribution and can't be done with a drive by contribution. It requires dedicated passion +and long commitment to implement support for a new language.

+ +This step by step guide is just a small intro to get the basics started and it's also not necessarily up-to-date +or complete and you have to be able to fill in the blanks.

+ +Currently the Antlr integration has some basic limitations compared to JavaCC: The output of the +Antlr parser generator is not an abstract syntax tree (AST) but a parse tree. As such, a parse tree is +much more fine-grained than what a typical JavaCC grammar will produce. This means that the +parse tree is much deeper and contains nodes down to the different token types.

+ +The Antlr nodes themselves don't have any attributes because they are on the wrong abstraction level. +As they don't have attributes, there are no attributes that can be used in XPath based rules.

+ +In order to overcome these limitations, one would need to implement a post-processing step that transforms +a parse tree into an abstract syntax tree and introducing real nodes on a higher abstraction level. This +step is **not** described in this guide.

+ +After the basic support for a language is there, there are lots of missing features left. Typical features +that can greatly improve rule writing are: symbol table, type resolution, call/data flow analysis.

+ +Symbol table keeps track of variables and their usages. Type resolution tries to find the actual class type +of each used type, following along method calls (including overloaded and overwritten methods), allowing +to query sub types and type hierarchy. This requires additional configuration of an auxiliary classpath. +Call and data flow analysis keep track of the data as it is moving through different execution paths +a program has.

+ +These features are out of scope of this guide. Type resolution and data flow are features that +definitely don't come for free. It is much effort and requires perseverance to implement.

+ +" %} + +## 1. Start with a new sub-module * See pmd-swift for examples. ## 2. Implement an AST parser for your language @@ -24,7 +61,7 @@ folder: pmd/devdocs ## 3. Create AST node classes * The individual AST nodes are generated, but you need to define the common interface for them. -* You need a need to define the supertype interface for all nodes of the language. For that, we provide +* You need to define the supertype interface for all nodes of the language. For that, we provide [`AntlrNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrNode.java). * See [`SwiftNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftNode.java) as an example. @@ -52,7 +89,7 @@ folder: pmd/devdocs ## 4. Generate your parser * Make sure, you have the property `true` in your `pom.xml` file. * This is just a matter of building the language module. ANTLR is called via ant, and this step is added - to the phase `generate-sources`. So you can just call e.g. `./mvnw generate-source -pl pmd-swift` to + to the phase `generate-sources`. So you can just call e.g. `./mvnw generate-sources -pl pmd-swift` to have the parser generated. * The generated code will be placed under `target/generated-sources/antlr4` and will not be committed to source control. diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md index c3b470247c..7e14a56a24 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md @@ -1,16 +1,40 @@ --- -title: Adding PMD support for a new JAVACC grammar based language -short_title: Adding a new language with JAVACC +title: Adding PMD support for a new JavaCC grammar based language +short_title: Adding a new language with JavaCC tags: [devdocs, extending] -summary: "How to add a new language to PMD using JAVACC grammar." -last_updated: October 5, 2019 +summary: "How to add a new language to PMD using JavaCC grammar." +last_updated: October 2021 sidebar: pmd_sidebar permalink: pmd_devdocs_major_adding_new_language_javacc.html folder: pmd/devdocs --- +{% include callout.html type="warning" content=" -## 1. Start with a new sub-module. +**Before you start...**

+ +This is really a big contribution and can't be done with a drive by contribution. It requires dedicated passion +and long commitment to implement support for a new language.

+ +This step by step guide is just a small intro to get the basics started and it's also not necessarily up-to-date +or complete and you have to be able to fill in the blanks.

+ +After the basic support for a language is there, there are lots of missing features left. Typical features +that can greatly improve rule writing are: symbol table, type resolution, call/data flow analysis.

+ +Symbol table keeps track of variables and their usages. Type resolution tries to find the actual class type +of each used type, following along method calls (including overloaded and overwritten methods), allowing +to query sub types and type hierarchy. This requires additional configuration of an auxiliary classpath. +Call and data flow analysis keep track of the data as it is moving through different execution paths +a program has.

+ +These features are out of scope of this guide. Type resolution and data flow are features that +definitely don't come for free. It is much effort and requires perseverance to implement.

+ +" %} + + +## 1. Start with a new sub-module * See pmd-java or pmd-vm for examples. ## 2. Implement an AST parser for your language diff --git a/docs/pages/pmd/languages/html.md b/docs/pages/pmd/languages/html.md index acb96fc416..9c905571e1 100644 --- a/docs/pages/pmd/languages/html.md +++ b/docs/pages/pmd/languages/html.md @@ -14,5 +14,11 @@ last_updated: April 2022 (6.45.0) The HTML language module uses [jsoup](https://jsoup.org/) for parsing. -XPath rules are supported, but the DOM is not a typical XML/XPath DOM. E.g. -text nodes are normal nodes. This might change in the future. +XPath 2.0 rules are supported, but the DOM is not always a typical XML/XPath DOM. +In the Designer, text nodes appear as nodes with name "#text", but they can +be selected as usual using `text()`. + +XML Namespaces are not supported. The local name of attributes include the prefix, +so that you have to select attributes by e.g. `//*[@*[local-name() = 'if:true']]`. + +Only XPath 1.0 rules are not supported. diff --git a/docs/pages/pmd/languages/kotlin.md b/docs/pages/pmd/languages/kotlin.md new file mode 100644 index 0000000000..ad9297a248 --- /dev/null +++ b/docs/pages/pmd/languages/kotlin.md @@ -0,0 +1,14 @@ +--- +title: Kotlin Support +permalink: pmd_languages_kotlin.html +tags: [languages] +summary: "Kotlin-specific features and guidance" +--- + +Kotlin support in PMD is based on the official grammar from . + +Java-based rules and XPath-based rules are supported. + +{% include note.html content="Kotlin support has **experimental** stability level, meaning no compatibility should +be expected between even incremental releases. Any functionality can be added, removed or changed without +warning." %} diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 392dce14e8..8f1893b2dd 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -244,526 +244,535 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Erik Bleske

🐛
Ernst Reissner

🐛
F.W. Dekker

🐛 +
FSchliephacke

🐛
Facundo

🐛 -
Federico Giust

🐛 +
Federico Giust

🐛
Fedor Sherstobitov

🐛
Felix Lampe

🐛
Filip Golonka

🐛
Filipe Esperandio

💻 🐛 +
Filippo Nova

🐛
Francesco la Torre

🐛 -
Francisco Duarte

🐛 -
Frieder Bluemle

🐛 +
Francisco Duarte

🐛 +
Frieder Bluemle

🐛
Frits Jalvingh

💻 🐛
G. Bazior

🐛
Gabe Henkes

🐛
Genoud Magloire

🐛
Geoffrey555

🐛 -
Georg Romstorfer

🐛 -
Gio

🐛 +
Georg Romstorfer

🐛 +
Gio

🐛
Gol

🐛
Gonzalo Exequiel Ibars Ingman

💻 🐛
GooDer

🐛
Gregor Riegler

🐛
Grzegorz Olszewski

🐛 -
Gunther Schrijvers

💻 🐛 -
Gustavo Krieger

🐛 +
Gunther Schrijvers

💻 🐛 +
Gustavo Krieger

🐛
Guy Elsmore-Paddock

🐛
Görkem Mülayim

🐛
Hanzel Godinez

🐛 +
Haoliang Chen

🐛
Harsh Kukreja

🐛 + +
Heber

🐛
Henning Schmiedehausen

💻 🐛
Henning von Bargen

💻 - -
Hervé Boutemy

🐛
Himanshu Pandey

🐛
Hokwang Lee

🐛
Hooperbloob

💻 + +
Hung PHAN

🐛
IDoCodingStuffs

💻 🐛
Iccen Gan

🐛 - -
Ignacio Mariano Tirabasso

🐛
Igor Melnichenko

🐛
Igor Moreno

🐛
Intelesis-MS

🐛 + +
Iroha_

🐛
Ishan Srivastava

🐛
Ivano Guerini

🐛 - -
Ivar Andreas Bonsaksen

🐛
Ivo Šmíd

🐛
JJengility

🐛
Jake Hemmerle

🐛 -
James Harrison

🐛 -
Jan

🐛 -
Jan Aertgeerts

💻 🐛 +
James Harrison

🐛 💻 +
Jan

🐛 +
Jan Aertgeerts

💻 🐛
Jan Brümmer

🐛
Jan Tříska

🐛
Jan-Lukas Else

🐛
Jason Qiu

💻 📖 + +
Jason Williams

🐛
Jean-Paul Mayer

🐛
Jean-Simon Larochelle

🐛 - -
Jeff Bartolotta

💻 🐛
Jeff Hube

💻 🐛
Jeff Jensen

🐛
Jeff May

🐛 + +
Jens Gerdes

🐛
Jeroen Borgers

🐛
Jerome Russ

🐛 - -
JerritEic

💻 📖
Jiri Pejchal

🐛
Jithin Sunny

🐛
Jiří Škorpil

🐛 + +
Joao Machado

🐛
Jochen Krauss

🐛
Johan Hammar

🐛 - -
John Karp

🐛
John Zhang

🐛
John-Teng

💻 🐛
Jon Moroney

💻 🐛 + +
Jonas Geiregat

🐛
Jonathan Wiesel

💻 🐛
Jordan

🐛 - -
Jordi Llach

🐛
Jorge Solórzano

🐛
JorneVL

🐛
Jose Palafox

🐛 + +
Jose Stovall

🐛
Joseph

💻
Joseph Heenan

🐛 - -
Josh Feingold

💻 🐛
Josh Holthaus

🐛
Joshua S Arquilevich

🐛
João Ferreira

💻 🐛 + +
João Pedro Schmitt

🐛
Juan Martín Sotuyo Dodero

💻 📖 🐛 🚧
Juan Pablo Civile

🐛 - -
Julian Voronetsky

🐛
Julien

🐛
Julius

🐛
JustPRV

🐛 + +
Jörn Huxhorn

🐛
KThompso

🐛
Kai Amundsen

🐛 - -
Karel Vervaeke

🐛
Karl-Andero Mere

🐛
Karl-Philipp Richter

🐛
Karsten Silz

🐛 + +
Kazuma Watanabe

🐛
Kev

🐛
Keve Müller

🐛 - -
Kevin Guerra

💻
Kevin Jones

🐛
Kevin Wayne

🐛
Kieran Black

🐛 + +
Kirill Zubov

🐛
Kirk Clemens

💻 🐛
Klaus Hartl

🐛 - -
Koen Van Looveren

🐛
Kris Scheibe

💻 🐛
Kunal Thanki

🐛
LaLucid

💻 + +
Larry Diamond

💻 🐛
Lars Knickrehm

🐛
Leo Gutierrez

🐛 - -
LiGaOg

💻
Lintsi

🐛
Linus Fernandes

🐛
Lixon Lookose

🐛 + +
Logesh

🐛
Lorenzo Gabriele

🐛
Loïc Ledoyen

🐛 - -
Lucas Silva

🐛
Lucas Soncini

💻 🐛
Lukasz Slonina

🐛
Lukebray

🐛 + +
Lyor Goldstein

🐛
MCMicS

🐛
Macarse

🐛 - -
Machine account for PMD

💻
Maciek Siemczyk

🐛
Maikel Steneker

💻 🐛
Maksim Moiseikin

🐛 + +
Manfred Koch

🐛
Manuel Moya Ferrer

💻 🐛
Manuel Ryan

🐛 - -
Marat Vyshegorodtsev

🐛
Marcel Härle

🐛
Marcello Fialho

🐛
Marcin Rataj

🐛 + +
Mark Adamcin

🐛
Mark Hall

💻 🐛
Mark Kolich

🐛 - -
Mark Pritchard

🐛
Markus Rathgeb

🐛
Marquis Wang

🐛
Martin Feldsztejn

🐛 + +
Martin Lehmann

🐛
Martin Spamer

🐛
Martin Tarjányi

🐛 - -
MatFl

🐛
Mateusz Stefanski

🐛
Mathieu Gouin

🐛
MatiasComercio

💻 🐛 + +
Matt Benson

🐛
Matt De Poorter

🐛
Matt Harrah

🐛 - -
Matt Nelson

🐛
Matthew Amos

🐛
Matthew Duggan

🐛
Matthew Hall

🐛 + +
Matías Fraga

💻 🐛
Maxime Robert

💻 🐛
MetaBF

🐛 - -
Michael

🐛
Michael Bell

🐛
Michael Bernstein

🐛
Michael Clay

🐛 + +
Michael Dombrowski

🐛
Michael Hausegger

🐛
Michael Hoefer

🐛 - -
Michael Möbius

🐛
Michael N. Lipp

🐛
Michael Pellegrini

🐛
Michal Kordas

🐛 + +
Michał Borek

🐛
Michał Kuliński

🐛
Miguel Núñez Díaz-Montes

🐛 - -
Mihai Ionut

🐛
Mirek Hankus

🐛
Mladjan Gadzic

🐛
MrAngry52

🐛 + +
Muminur Choudhury

🐛
Mykhailo Palahuta

💻 🐛
Nagendra Kumar Singh

🐛 - -
Nahuel Barrios

🐛
Nathan Braun

🐛
Nathan Reynolds

🐛
Nathan Reynolds

🐛 + +
Nathanaël

🐛
Naveen

💻
Nazdravi

🐛 - -
Neha-Dhonde

🐛
Nicholas Doyle

🐛
Nick Butcher

🐛
Nico Gallinal

🐛 + +
Nicola Dal Maso

🐛
Nicolas Filotto

💻
Nikita Chursin

🐛 - -
Niklas Baudy

🐛
Nikolas Havrikov

🐛
Nilesh Virkar

🐛
Nimit Patel

🐛 + +
Niranjan Harpale

🐛
Noah Sussman

🐛
Noah0120

🐛 - -
Noam Tamim

🐛
Noel Grandin

🐛
Olaf Haalstra

🐛
Oleg Pavlenko

🐛 + +
Oleksii Dykov

💻
Oliver Eikemeier

🐛
Olivier Parent

💻 🐛 - -
Ollie Abbey

💻 🐛
OverDrone

🐛
Ozan Gulle

💻 🐛
PUNEET JAIN

🐛 + +
Parbati Bose

🐛
Paul Berg

🐛
Pavel Bludov

🐛 - -
Pavel Mička

🐛
Pedro Nuno Santos

🐛
Pedro Rijo

🐛
Pelisse Romain

💻 📖 🐛 + + +
Per Abich

💻
Pete Davids

🐛
Peter Bruin

🐛
Peter Chittum

💻 🐛 - -
Peter Cudmore

🐛
Peter Kasson

🐛
Peter Kofler

🐛 + +
Pham Hai Trung

🐛
Philip Graf

💻 🐛
Philip Hachey

🐛
Philippe Ozil

🐛 - -
Phinehas Artemix

🐛
Phokham Nonava

🐛
Piotr Szymański

🐛 + +
Piotrek Żygieło

💻 🐛
Pranay Jaiswal

🐛
Prasad Kamath

🐛
Prasanna

🐛 - -
Presh-AR

🐛
Puneet1726

🐛
Rafael Cortês

🐛 + +
RaheemShaik999

🐛
RajeshR

💻 🐛
Ramachandra Mohan

🐛 +
Ramel0921

🐛
Raquel Pau

🐛 - -
Ravikiran Janardhana

🐛
Reda Benhemmouche

🐛 + +
Renato Oliveira

💻 🐛
Rich DiCroce

🐛
Riot R1cket

🐛
Rishabh Jain

🐛
RishabhDeep Singh

🐛 - -
Robbie Martinus

💻 🐛
Robert Henry

🐛 + +
Robert Painsi

🐛
Robert Russell

🐛
Robert Sösemann

💻 📖 📢 🐛
Robert Whitebit

🐛
Robin Richtsfeld

🐛 - -
Robin Stocker

💻 🐛
Robin Wils

🐛 + +
RochusOest

🐛
Rodolfo Noviski

🐛
Rodrigo Casara

🐛
Rodrigo Fernandes

🐛
Roman Salvador

💻 🐛 - -
Ronald Blaschke

🐛
Róbert Papp

🐛 + +
Saikat Sengupta

🐛
Saksham Handu

🐛
Saladoc

🐛
Salesforce Bob Lightning

🐛
Sam Carlberg

🐛 - -
Satoshi Kubo

🐛
Scott Kennedy

🐛 + +
Scott Wells

🐛 💻
Scrsloota

💻
Sebastian Bögl

🐛
Sebastian Schuberth

🐛
Sebastian Schwarz

🐛 - -
Sergey Gorbaty

🐛
Sergey Kozlov

🐛 + +
Sergey Yanzin

💻 🐛
Seth Wilcox

💻
Shubham

💻 🐛
Simon Xiao

🐛
Srinivasan Venkatachalam

🐛 - -
Stanislav Gromov

🐛
Stanislav Myachenkov

💻 + +
Stefan Birkner

🐛
Stefan Bohn

🐛
Stefan Endrullis

🐛
Stefan Klöss-Schuster

🐛
Stefan Wolf

🐛 - -
Stephan H. Wissel

🐛
Stephen

🐛 + +
Stephen Friedrich

🐛
Steve Babula

💻
Stexxe

🐛
Stian Lågstad

🐛
StuartClayton5

🐛 - -
Supun Arunoda

🐛
Suren Abrahamyan

🐛 + +
SwatiBGupta1110

🐛
SyedThoufich

🐛
Szymon Sasin

🐛
T-chuangxin

🐛
TERAI Atsuhiro

🐛 - -
TIOBE Software

💻 🐛
Taylor Smock

🐛 + +
Techeira Damián

💻 🐛
Ted Husted

🐛
TehBakker

🐛
The Gitter Badger

🐛
Theodoor

🐛 - -
Thiago Henrique Hüpner

🐛
Thibault Meyer

🐛 + +
Thomas Güttler

🐛
Thomas Jones-Low

🐛
Thomas Smith

💻 🐛
ThrawnCA

🐛
Thunderforge

💻 🐛 - -
Tim van der Lippe

🐛
Tobias Weimer

💻 🐛 + +
Tom Daly

🐛
Tomer Figenblat

🐛
Tomi De Lucca

💻 🐛
Torsten Kleiber

🐛
TrackerSB

🐛 - -
Ullrich Hafner

🐛
Utku Cuhadaroglu

💻 🐛 + +
Valentin Brandl

🐛
Valeria

🐛
Vasily Anisimov

🐛 +
Vibhor Goyal

🐛
Vickenty Fesunov

🐛
Victor Noël

🐛 +
Vincent Galloy

💻 -
Vincent Galloy

💻
Vincent HUYNH

🐛
Vincent Maurin

🐛
Vincent Privat

🐛
Vishhwas

🐛
Vitaly

🐛
Vitaly Polonetsky

🐛 +
Vojtech Polivka

🐛 -
Vojtech Polivka

🐛
Vsevolod Zholobov

🐛
Vyom Yadav

💻
Wang Shidong

🐛
Waqas Ahmed

🐛
Wayne J. Earl

🐛
Wchenghui

🐛 +
Will Winder

🐛 -
Will Winder

🐛
William Brockhus

💻 🐛
Wilson Kurniawan

🐛
Wim Deblauwe

🐛
Woongsik Choi

🐛
XenoAmess

💻 🐛
Yang

💻 +
YaroslavTER

🐛 -
YaroslavTER

🐛
Young Chan

💻 🐛
YuJin Kim

🐛
Yuri Dolzhenko

🐛
Yurii Dubinka

🐛
Zoltan Farkas

🐛
Zustin

🐛 +
aaronhurst-google

🐛 -
aaronhurst-google

🐛
alexmodis

🐛
andreoss

🐛
andrey81inmd

💻 🐛
anicoara

🐛
arunprasathav

🐛
asiercamara

🐛 +
astillich-igniti

💻 -
astillich-igniti

💻
avesolovksyy

🐛
avishvat

🐛
avivmu

🐛
axelbarfod1

🐛
b-3-n

🐛
balbhadra9

🐛 +
base23de

🐛 -
base23de

🐛
bergander

🐛
berkam

💻 🐛
breizh31

🐛
caesarkim

🐛
carolyujing

🐛
cesares-basilico

🐛 +
chrite

🐛 -
chrite

🐛
cobratbq

🐛
coladict

🐛
cosmoJFH

🐛
cristalp

🐛
crunsk

🐛
cwholmes

🐛 +
cyberjj999

🐛 -
cyberjj999

🐛
cyw3

🐛
d1ss0nanz

🐛 +
dalizi007

💻
danbrycefairsailcom

🐛
dariansanity

🐛
darrenmiliband

🐛 @@ -829,118 +838,121 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
khalidkh

🐛
krzyk

🐛
lasselindqvist

🐛 +
lgemeinhardt

🐛
lihuaib

🐛 -
lonelyma1021

🐛 +
lonelyma1021

🐛
lpeddy

🐛
lujiefsi

💻 +
lukelukes

💻
lyriccoder

🐛
marcelmore

🐛
matchbox

🐛 -
matthiaskraaz

🐛 -
meandonlyme

🐛 +
matthiaskraaz

🐛 +
meandonlyme

🐛
mikesive

🐛
milossesic

🐛
mriddell95

🐛
mrlzh

🐛
msloan

🐛 -
mucharlaravalika

🐛 -
mvenneman

🐛 +
mucharlaravalika

🐛 +
mvenneman

🐛
nareshl119

🐛
nicolas-harraudeau-sonarsource

🐛
noerremark

🐛
novsirion

🐛
oggboy

🐛 -
oinume

🐛 -
orimarko

💻 🐛 +
oinume

🐛 +
orimarko

💻 🐛
pallavi agarwal

🐛
parksungrin

🐛
patpatpat123

🐛
patriksevallius

🐛
pbrajesh1

🐛 -
phoenix384

🐛 -
piotrszymanski-sc

💻 +
phoenix384

🐛 +
piotrszymanski-sc

💻
plan3d

🐛
poojasix

🐛
prabhushrikant

🐛
pujitha8783

🐛
r-r-a-j

🐛 -
raghujayjunk

🐛 -
rajeshveera

🐛 +
raghujayjunk

🐛 +
rajeshveera

🐛
rajeswarreddy88

🐛
recdevs

🐛
reudismam

💻 🐛
rijkt

🐛
rillig-tk

🐛 -
rmohan20

💻 🐛 -
rxmicro

🐛 +
rmohan20

💻 🐛 +
rxmicro

🐛
ryan-gustafson

💻 🐛
sabi0

🐛
scais

🐛
sebbASF

🐛
sergeygorbaty

💻 -
shilko2013

🐛 -
simeonKondr

🐛 +
shilko2013

🐛 +
shiomiyan

📖 +
simeonKondr

🐛
snajberk

🐛
sniperrifle2004

🐛
snuyanzin

🐛 💻
sratz

🐛 + +
stonio

🐛
sturton

💻 🐛
sudharmohan

🐛 - -
suruchidawar

🐛
svenfinitiv

🐛
tashiscool

🐛
test-git-hook

🐛 + +
testation21

💻 🐛
thanosa

🐛
tiandiyixian

🐛 - -
tobwoerk

🐛
tprouvot

🐛
trentchilders

🐛
triandicAnt

🐛 + +
trishul14

🐛
tsui

🐛
winhkey

🐛 - -
witherspore

🐛
wjljack

🐛
wuchiuwong

🐛
xingsong

🐛 + +
xioayuge

🐛
xnYi9wRezm

💻 🐛
xuanuy

🐛 - -
xyf0921

🐛
yalechen-cyw3

🐛
yasuharu-sato

🐛
zenglian

🐛 + +
zgrzyt93

💻 🐛
zh3ng

🐛
zt_soft

🐛 - -
ztt79

🐛
zzzzfeng

🐛
Árpád Magosányi

🐛 diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index ce4214b9fa..8c1f59df60 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -76,14 +76,14 @@ The tool comes with a rather extensive help text, simply running with `--help`! %} {% include custom/cli_option_row.html options="--file-list" option_arg="filepath" - description="Path to file containing a comma delimited list of files to analyze. + description="Path to file containing a list of files to analyze, one path per line. If this is given, then you don't need to provide `--dir`." %} {% include custom/cli_option_row.html options="--force-language" option_arg="lang" description="Force a language to be used for all input files, irrespective of - filenames. When using this option, the automatic language selection - by extension is disabled and all files are tried to be parsed with + file names. When using this option, the automatic language selection + by extension is disabled and PMD tries to parse all files with the given language `<lang>`. Parsing errors are ignored and unparsable files are skipped. @@ -92,9 +92,9 @@ The tool comes with a rather extensive help text, simply running with `--help`! %} {% include custom/cli_option_row.html options="--ignore-list" option_arg="filepath" - description="Path to file containing a comma delimited list of files to ignore. + description="Path to file containing a list of files to ignore, one path per line. This option can be combined with `--dir` and `--file-list`. - This ignore list takes precedence over any files in the filelist." + This ignore list takes precedence over any files in the file-list." %} {% include custom/cli_option_row.html options="--help,-h,-H" description="Display help on usage." diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index dc4577e41d..d909eb4f2f 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -112,9 +112,9 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt languages="Java" %} {% include custom/cli_option_row.html options="--ignore-annotations" - description="Ignore language annotations when comparing text" + description="Ignore language annotations (Java) or attributes (C#) when comparing text" default="false" - languages="Java" + languages="C#, Java" %} {% include custom/cli_option_row.html options="--ignore-literal-sequences" description="Ignore sequences of literals (common e.g. in list initializers)" diff --git a/docs/pages/pmd/userdocs/tools/tools.md b/docs/pages/pmd/userdocs/tools/tools.md index 78d7b7d316..c889ef0091 100644 --- a/docs/pages/pmd/userdocs/tools/tools.md +++ b/docs/pages/pmd/userdocs/tools/tools.md @@ -197,7 +197,7 @@ To install the PMD plugin for Eclipse: * Start Eclipse and open a project * Select "Help"->"Software Updates"->"Find and Install" * Click "Next", then click "New remote site" -* Enter "PMD" into the Name field and into the URL field +* Enter "PMD" into the Name field and into the URL field * Click through the rest of the dialog boxes to install the plugin Alternatively, you can download the latest zip file and follow the above procedures diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index a1be51b66c..8abb75f161 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,9 +21,6 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues -* javascript - * [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal - ### API Changes ### External Contributions diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 08788a3064..52432fe7c6 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,220 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 25-June-2022 - 6.47.0 + +The PMD team is pleased to announce PMD 6.47.0. + +This is a minor release. + +### Table Of Contents + +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### Fixed Issues +* core + * [#3999](https://github.com/pmd/pmd/issues/3999): \[cli] All files are analyzed despite parameter `--file-list` + * [#4009](https://github.com/pmd/pmd/issues/4009): \[core] Cannot build PMD with Temurin 17 +* java-bestpractices + * [#3824](https://github.com/pmd/pmd/issues/3824): \[java] UnusedPrivateField: Do not flag fields annotated with @Version + * [#3825](https://github.com/pmd/pmd/issues/3825): \[java] UnusedPrivateField: Do not flag fields annotated with @Id or @EmbeddedId +* java-design + * [#3823](https://github.com/pmd/pmd/issues/3823): \[java] ImmutableField: Do not flag fields in @Entity + * [#3981](https://github.com/pmd/pmd/issues/3981): \[java] ImmutableField reports fields annotated with @Value (Spring) + * [#3998](https://github.com/pmd/pmd/issues/3998): \[java] ImmutableField reports fields annotated with @Captor (Mockito) + * [#4004](https://github.com/pmd/pmd/issues/4004): \[java] ImmutableField reports fields annotated with @GwtMock (GwtMockito) and @Spy (Mockito) + * [#4008](https://github.com/pmd/pmd/issues/4008): \[java] ImmutableField not reporting fields that are only initialized in the declaration + * [#4011](https://github.com/pmd/pmd/issues/4011): \[java] ImmutableField: Do not flag fields annotated with @Inject + * [#4020](https://github.com/pmd/pmd/issues/4020): \[java] ImmutableField reports fields annotated with @FindBy and @FindBys (Selenium) +* java-errorprone + * [#3936](https://github.com/pmd/pmd/issues/3936): \[java] AvoidFieldNameMatchingMethodName should consider enum class + * [#3937](https://github.com/pmd/pmd/issues/3937): \[java] AvoidDuplicateLiterals - uncompilable test cases + +### API Changes + +No changes. + +### External Contributions +* [#3985](https://github.com/pmd/pmd/pull/3985): \[java] Fix false negative problem about Enum in AvoidFieldNameMatchingMethodName #3936 - [@Scrsloota](https://github.com/Scrsloota) +* [#3993](https://github.com/pmd/pmd/pull/3993): \[java] AvoidDuplicateLiterals - Add the method "buz" definition to test cases - [@dalizi007](https://github.com/dalizi007) +* [#4002](https://github.com/pmd/pmd/pull/4002): \[java] ImmutableField - Ignore fields annotated with @Value (Spring) or @Captor (Mockito) - [@jjlharrison](https://github.com/jjlharrison) +* [#4003](https://github.com/pmd/pmd/pull/4003): \[java] UnusedPrivateField - Ignore fields annotated with @Id/@EmbeddedId/@Version (JPA) or @Mock/@Spy/@MockBean (Mockito/Spring) - [@jjlharrison](https://github.com/jjlharrison) +* [#4006](https://github.com/pmd/pmd/pull/4006): \[doc] Fix eclipse plugin update site URL - [@shiomiyan](https://github.com/shiomiyan) +* [#4010](https://github.com/pmd/pmd/pull/4010): \[core] Bump kotlin to version 1.7.0 - [@maikelsteneker](https://github.com/maikelsteneker) + +### Stats +* 45 commits +* 23 closed tickets & PRs +* Days since last release: 27 + +## 28-May-2022 - 6.46.0 + +The PMD team is pleased to announce PMD 6.46.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [CLI improvements](#cli-improvements) + * [C# Improvements](#c#-improvements) + * [New Rules](#new-rules) + * [Deprecated Rules](#deprecated-rules) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Deprecated ruleset references](#deprecated-ruleset-references) + * [Deprecated API](#deprecated-api) + * [Internal API](#internal-api) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### CLI improvements + +The PMD CLI now allows repeating the `--dir` (`-d`) and `--rulesets` (`-R`) options, + as well as providing several space-separated arguments to either of them. For instance: +```shell +pmd -d src/main/java src/test/java -R rset1.xml -R rset2.xml +``` +This also allows globs to be used on the CLI if your shell supports shell expansion. +For instance, the above can be written +```shell +pmd -d src/*/java -R rset*.xml +``` +Please use theses new forms instead of using comma-separated lists as argument to these options. + +#### C# Improvements + +When executing CPD on C# sources, the option `--ignore-annotations` is now supported as well. +It ignores C# attributes when detecting duplicated code. This option can also be enabled via +the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details. + +#### New Rules + +This release ships with 2 new Java rules. + +* [`EmptyControlStatement`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_codestyle.html#emptycontrolstatement) reports many instances of empty things, e.g. control statements whose + body is empty, as well as empty initializers. + + EmptyControlStatement also works for empty `for` and `do` loops, while there were previously + no corresponding rules. + + This new rule replaces the rules EmptyFinallyBlock, EmptyIfStmt, EmptyInitializer, EmptyStatementBlock, + EmptySwitchStatements, EmptySynchronizedBlock, EmptyTryBlock, and EmptyWhileStmt. + +```xml + +``` + +The rule is part of the quickstart.xml ruleset. + +* [`UnnecessarySemicolon`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_codestyle.html#unnecessarysemicolon) reports semicolons that are unnecessary (so called "empty statements" + and "empty declarations"). + + This new rule replaces the rule EmptyStatementNotInLoop. + +```xml + +``` + +The rule is part of the quickstart.xml ruleset. + +#### Deprecated Rules + +* The following Java rules are deprecated and removed from the quickstart ruleset, as the new rule +[`EmptyControlStatement`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_codestyle.html#emptycontrolstatement) merges their functionality: + * [`EmptyFinallyBlock`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptyfinallyblock) + * [`EmptyIfStmt`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptyifstmt) + * [`EmptyInitializer`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptyinitializer) + * [`EmptyStatementBlock`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptystatementblock) + * [`EmptySwitchStatements`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptyswitchstatements) + * [`EmptySynchronizedBlock`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptysynchronizedblock) + * [`EmptyTryBlock`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptytryblock) + * [`EmptyWhileStmt`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptywhilestmt) +* The Java rule [`EmptyStatementNotInLoop`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_errorprone.html#emptystatementnotinloop) is deprecated and removed from the quickstart +ruleset. Use the new rule [`UnnecessarySemicolon`](https://pmd.github.io/pmd-6.46.0/pmd_rules_java_codestyle.html#unnecessarysemicolon) instead. + +### Fixed Issues + +* cli + * [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters +* core + * [#2352](https://github.com/pmd/pmd/issues/2352): \[core] Deprecate \-\ hyphen notation for ruleset references + * [#3787](https://github.com/pmd/pmd/issues/3787): \[core] Internalize some methods in Ant Formatter + * [#3835](https://github.com/pmd/pmd/issues/3835): \[core] Deprecate system properties of CPDCommandLineInterface + * [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425) +* cs (c#) + * [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) +* go + * [#2752](https://github.com/pmd/pmd/issues/2752): \[go] Error parsing unicode values +* html + * [#3955](https://github.com/pmd/pmd/pull/3955): \[html] Improvements for handling text and comment nodes + * [#3978](https://github.com/pmd/pmd/pull/3978): \[html] Add additional file extensions htm, xhtml, xht, shtml +* java + * [#3423](https://github.com/pmd/pmd/issues/3423): \[java] Error processing identifiers with Unicode +* java-bestpractices + * [#3954](https://github.com/pmd/pmd/issues/3954): \[java] NPE in UseCollectionIsEmptyRule when .size() is called in a record +* java-design + * [#3874](https://github.com/pmd/pmd/issues/3874): \[java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito) +* java-errorprone + * [#3096](https://github.com/pmd/pmd/issues/3096): \[java] EmptyStatementNotInLoop FP in 6.30.0 with IfStatement +* java-performance + * [#3379](https://github.com/pmd/pmd/issues/3379): \[java] UseArraysAsList must ignore primitive arrays + * [#3965](https://github.com/pmd/pmd/issues/3965): \[java] UseArraysAsList false positive with non-trivial loops +* javascript + * [#2605](https://github.com/pmd/pmd/issues/2605): \[js] Support unicode characters + * [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal +* python + * [#2604](https://github.com/pmd/pmd/issues/2604): \[python] Support unicode identifiers + +### API Changes + +#### Deprecated ruleset references + +Ruleset references with the following formats are now deprecated and will produce a warning +when used on the CLI or in a ruleset XML file: +- `-`, eg `java-basic`, which resolves to `rulesets/java/basic.xml` +- the internal release number, eg `600`, which resolves to `rulesets/releases/600.xml` + +Use the explicit forms of these references to be compatible with PMD 7. + +#### Deprecated API + +- toString is now deprecated. The format of this + method will remain the same until PMD 7. The deprecation is intended to steer users + away from relying on this format, as it may be changed in PMD 7. +- getInputPaths and +setInputPaths are now deprecated. +A new set of methods have been added, which use lists and do not rely on comma splitting. + +#### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +- CPDCommandLineInterface has been internalized. In order to execute CPD either +CPD#run or CPD#main +should be used. +- Several members of BaseCPDCLITest have been deprecated with replacements. +- The methods Formatter#start, +Formatter#end, Formatter#getRenderer, +and Formatter#isNoOutputSupplied have been internalized. + +### External Contributions + +* [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard) +* [#3964](https://github.com/pmd/pmd/pull/3964): \[java] Fix #3874 - ImmutableField: fix mockito/spring false positives - [@lukelukes](https://github.com/lukelukes) +* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) - [@maikelsteneker](https://github.com/maikelsteneker) + +### Stats +* 92 commits +* 30 closed tickets & PRs +* Days since last release: 28 + ## 30-April-2022 - 6.45.0 The PMD team is pleased to announce PMD 6.45.0. diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 56d5058b44..1e1c81fdce 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -9,7 +9,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 50ede2c0da..d728aa1ef8 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -61,10 +61,6 @@ - - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java index 51ad07f5c8..412c4ea024 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java @@ -11,18 +11,18 @@ import java.io.File; import java.io.IOException; import java.util.Iterator; -import org.apache.commons.io.FilenameUtils; import org.junit.Before; import org.junit.Test; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +import net.sourceforge.pmd.util.IOUtil; public class ApexCpdTest { private File testdir; @Before public void setUp() { - String path = FilenameUtils.normalize("src/test/resources/net/sourceforge/pmd/cpd/issue427"); + String path = IOUtil.normalizePath("src/test/resources/net/sourceforge/pmd/cpd/issue427"); testdir = new File(path); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index ae6b37127d..5358e1beb5 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -17,13 +17,12 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.util.IOUtil; public class ApexParserTest extends ApexParserTestBase { @@ -158,7 +157,7 @@ public class ApexParserTest extends ApexParserTestBase { for (File file : fList) { if (file.isFile() && file.getName().endsWith(".cls")) { - String sourceCode = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + String sourceCode = IOUtil.readFileToString(file, StandardCharsets.UTF_8); Assert.assertNotNull(parse(sourceCode)); } } @@ -170,7 +169,7 @@ public class ApexParserTest extends ApexParserTestBase { */ @Test public void parseInheritedSharingClass() throws IOException { - String source = IOUtils.toString(ApexParserTest.class.getResourceAsStream("InheritedSharing.cls"), + String source = IOUtil.readToString(ApexParserTest.class.getResourceAsStream("InheritedSharing.cls"), StandardCharsets.UTF_8); parse(source); } @@ -182,7 +181,7 @@ public class ApexParserTest extends ApexParserTestBase { */ @Test public void stackOverflowDuringClassParsing() throws Exception { - String source = IOUtils.toString(ApexParserTest.class.getResourceAsStream("StackOverflowClass.cls"), + String source = IOUtil.readToString(ApexParserTest.class.getResourceAsStream("StackOverflowClass.cls"), StandardCharsets.UTF_8); ASTUserClassOrInterface rootNode = parse(source); diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java index 319f265ecf..7f3effa2b7 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java @@ -15,13 +15,14 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; -import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; import org.junit.rules.TemporaryFolder; +import net.sourceforge.pmd.util.IOUtil; + public class ApexMultifileAnalysisTest { @Rule @@ -67,7 +68,7 @@ public class ApexMultifileAnalysisTest { private void copyResource(String resourcePath, String relativePathInTempDir) throws IOException { File file = tempFolder.newFile(relativePathInTempDir); - String fileContents = IOUtils.toString(getClass().getResourceAsStream(resourcePath), StandardCharsets.UTF_8); + String fileContents = IOUtil.readToString(getClass().getResourceAsStream(resourcePath), StandardCharsets.UTF_8); Files.write(file.toPath(), Arrays.asList(fileContents.split("\\R").clone())); } diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index b96f988a88..710a57f54c 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -91,10 +91,6 @@ com.beust jcommander - - commons-io - commons-io - net.sf.saxon diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index 4dd8f5f2b7..ecd8b2db8e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -232,6 +232,7 @@ public final class PMD { return StatusCode.ERROR; } try { + log.debug("Current classpath:\n{}", System.getProperty("java.class.path")); ReportStats stats; stats = PMD.runAndReturnStats(pmd); if (pmdReporter.numErrors() > 0) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 589f8b2241..314e4ce3ea 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Objects; import java.util.Properties; -import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; @@ -464,7 +463,6 @@ public class PMDConfiguration extends AbstractConfiguration { * @deprecated Use {@link #getAllInputPaths()} */ @Deprecated - @DeprecatedUntil700 public @Nullable String getInputPaths() { return inputPaths.isEmpty() ? null : String.join(",", inputPaths); } @@ -479,20 +477,34 @@ public class PMDConfiguration extends AbstractConfiguration { /** * Set the comma separated list of input paths to process for source files. * - * @param inputPaths - * The comma separated list. + * @param inputPaths The comma separated list. + * + * @throws NullPointerException If the parameter is null + * @deprecated Use {@link #setInputPaths(List)} or {@link #addInputPath(String)} */ - public void setInputPaths(@NonNull String inputPaths) { + @Deprecated + public void setInputPaths(String inputPaths) { List paths = new ArrayList<>(); Collections.addAll(paths, inputPaths.split(",")); - paths.removeIf(StringUtils::isBlank); this.inputPaths = paths; } - public void setInputPaths(@NonNull List inputPaths) { - List paths = new ArrayList<>(inputPaths); - paths.removeIf(StringUtils::isBlank); - this.inputPaths = paths; + /** + * Set the input paths to the given list of paths. + * @throws NullPointerException If the parameter is null + */ + public void setInputPaths(List inputPaths) { + this.inputPaths = new ArrayList<>(inputPaths); + } + + /** + * Add an input path. It is not split on commas. + * + * @throws NullPointerException If the parameter is null + */ + public void addInputPath(String inputPath) { + Objects.requireNonNull(inputPath); + this.inputPaths.add(inputPath); } public String getInputFilePath() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index f2a178e71a..d45d7261c0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -261,18 +261,19 @@ final class RuleSetFactory { private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, boolean withDeprecatedRuleReferences, Set rulesetReferences) { Element ruleElement = (Element) ruleNode; - String ref = ruleElement.getAttribute("ref"); - ref = compatibilityFilter.applyRef(ref, this.warnDeprecated); - if (ref == null) { - return; // deleted rule - } - if (ref.endsWith("xml")) { - parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); - } else if (StringUtils.isBlank(ref)) { - parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode); - } else { - parseRuleReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, ref, withDeprecatedRuleReferences); + if (ruleElement.hasAttribute("ref")) { + String ref = ruleElement.getAttribute("ref"); + RuleSetReferenceId refId = parseReferenceAndWarn(ruleSetBuilder, ref); + if (refId != null) { + if (refId.isAllRules()) { + parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, refId, rulesetReferences); + } else { + parseRuleReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, ref, refId, withDeprecatedRuleReferences); + } + return; + } } + parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode); } /** @@ -289,7 +290,9 @@ final class RuleSetFactory { * The RuleSet reference. * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ - private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) { + private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, + String ref, + RuleSetReferenceId ruleSetReferenceId, Set rulesetReferences) { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); Set excludedRulesCheck = new HashSet<>(); @@ -311,7 +314,7 @@ final class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); - RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0)); + RuleSet otherRuleSet = ruleSetFactory.createRuleSet(ruleSetReferenceId); List potentialRules = new ArrayList<>(); int countDeprecated = 0; for (Rule rule : otherRuleSet.getRules()) { @@ -360,6 +363,24 @@ final class RuleSetFactory { rulesetReferences.add(ref); } + private RuleSetReferenceId parseReferenceAndWarn(RuleSetBuilder ruleSetBuilder, String ref) { + ref = compatibilityFilter.applyRef(ref, this.warnDeprecated); + if (ref == null) { + LOG.debug("Rule ref {} references a deleted rule, ignoring", ref); + return null; // deleted rule + } + + List references = RuleSetReferenceId.parse(ref, warnDeprecated); + if (references.size() > 1 && warnDeprecated) { + LOG.warn("Using a comma separated list as a ref attribute is deprecated. " + + "All references but the first are ignored. Reference: '{}'", ref); + } else if (references.isEmpty()) { + LOG.warn("Empty ref attribute in ruleset '{}'", ruleSetBuilder.getName()); + return null; + } + return references.get(0); + } + /** * Parse a rule node as a single Rule. The Rule has been fully defined * within the context of the current RuleSet. @@ -413,7 +434,9 @@ final class RuleSetFactory { * or not */ private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, String ref, boolean withDeprecatedRuleReferences) { + Node ruleNode, String ref, + RuleSetReferenceId otherRuleSetReferenceId, + boolean withDeprecatedRuleReferences) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not @@ -428,7 +451,6 @@ final class RuleSetFactory { RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); boolean isSameRuleSet = false; - RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); if (!otherRuleSetReferenceId.isExternal() && containsRule(ruleSetReferenceId, otherRuleSetReferenceId.getRuleName())) { otherRuleSetReferenceId = new RuleSetReferenceId(ref, ruleSetReferenceId); @@ -504,6 +526,7 @@ final class RuleSetFactory { * @return {@code true} if the ruleName exists */ private boolean containsRule(RuleSetReferenceId ruleSetReferenceId, String ruleName) { + // TODO: avoid reloading the ruleset once again boolean found = false; try (InputStream ruleSet = ruleSetReferenceId.getInputStream(resourceLoader)) { DocumentBuilder builder = createDocumentBuilder(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java index 1a75e5d3a5..12da1ad941 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -153,7 +153,7 @@ public final class RuleSetLoader { * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found) */ public RuleSet loadFromResource(String rulesetPath) { - return loadFromResource(new RuleSetReferenceId(rulesetPath)); + return loadFromResource(new RuleSetReferenceId(rulesetPath, null, warnDeprecated)); } /** @@ -165,7 +165,7 @@ public final class RuleSetLoader { * @throws RuleSetLoadException If any error occurs (eg, invalid syntax) */ public RuleSet loadFromString(String filename, final String rulesetXmlContent) { - return loadFromResource(new RuleSetReferenceId(filename) { + return loadFromResource(new RuleSetReferenceId(filename, null, warnDeprecated) { @Override public InputStream getInputStream(ResourceLoader rl) { return new ByteArrayInputStream(rulesetXmlContent.getBytes(StandardCharsets.UTF_8)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java index 32387d4e3e..6359c2aad4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.util.ResourceLoader; @@ -59,16 +61,6 @@ import net.sourceforge.pmd.util.ResourceLoader; * all * * - * java-basic - * rulesets/java/basic.xml - * all - * - * - * 50 - * rulesets/releases/50.xml - * all - * - * * rulesets/java/basic.xml/EmptyCatchBlock * rulesets/java/basic.xml * EmptyCatchBlock @@ -86,12 +78,21 @@ import net.sourceforge.pmd.util.ResourceLoader; @Deprecated @InternalApi public class RuleSetReferenceId { + + // todo this class has issues... What is even an "external" ruleset? + // terminology and API should be clarified. + + // use the logger of RuleSetFactory, because the warnings conceptually come from there. + private static final Logger LOG = LoggerFactory.getLogger(RuleSetFactory.class); + private final boolean external; private final String ruleSetFileName; private final boolean allRules; private final String ruleName; private final RuleSetReferenceId externalRuleSetReferenceId; + private final String originalRef; + /** * Construct a RuleSetReferenceId for the given single ID string. * @@ -111,19 +112,34 @@ public class RuleSetReferenceId { * Rule. The external RuleSetReferenceId will be responsible for producing * the InputStream containing the Rule. * - * @param id - * The id string. - * @param externalRuleSetReferenceId - * A RuleSetReferenceId to associate with this new instance. - * @throws IllegalArgumentException - * If the ID contains a comma character. - * @throws IllegalArgumentException - * If external RuleSetReferenceId is not external. - * @throws IllegalArgumentException - * If the ID is not Rule reference when there is an external - * RuleSetReferenceId. + * @param id The id string. + * @param externalRuleSetReferenceId A RuleSetReferenceId to associate with this new instance. + * + * @throws IllegalArgumentException If the ID contains a comma character. + * @throws IllegalArgumentException If external RuleSetReferenceId is not external. + * @throws IllegalArgumentException If the ID is not Rule reference when there is an external + * RuleSetReferenceId. */ public RuleSetReferenceId(final String id, final RuleSetReferenceId externalRuleSetReferenceId) { + this(id, externalRuleSetReferenceId, false); + } + + /** + * Construct a RuleSetReferenceId for the given single ID string. If an + * external RuleSetReferenceId is given, the ID must refer to a non-external + * Rule. The external RuleSetReferenceId will be responsible for producing + * the InputStream containing the Rule. + * + * @param id The id string. + * @param externalRuleSetReferenceId A RuleSetReferenceId to associate with this new instance. + * + * @throws IllegalArgumentException If the ID contains a comma character. + * @throws IllegalArgumentException If external RuleSetReferenceId is not external. + * @throws IllegalArgumentException If the ID is not Rule reference when there is an external + * RuleSetReferenceId. + */ + RuleSetReferenceId(final String id, final RuleSetReferenceId externalRuleSetReferenceId, boolean warnDeprecated) { + this.originalRef = id; if (externalRuleSetReferenceId != null && !externalRuleSetReferenceId.isExternal()) { throw new IllegalArgumentException("Cannot pair with non-external <" + externalRuleSetReferenceId + ">."); @@ -178,8 +194,16 @@ public class RuleSetReferenceId { allRules = tempRuleName == null; } else { // resolve the ruleset name - it's maybe a built in ruleset - String builtinRuleSet = resolveBuiltInRuleset(tempRuleSetFileName); + String expandedRuleset = resolveDeprecatedBuiltInRulesetShorthand(tempRuleSetFileName); + String builtinRuleSet = expandedRuleset == null ? tempRuleSetFileName : expandedRuleset; if (checkRulesetExists(builtinRuleSet)) { + if (expandedRuleset != null && warnDeprecated) { + LOG.warn( + "Ruleset reference '{}' uses a deprecated form, use '{}' instead", + tempRuleSetFileName, builtinRuleSet + ); + } + external = true; ruleSetFileName = builtinRuleSet; ruleName = tempRuleName; @@ -250,25 +274,22 @@ public class RuleSetReferenceId { * the ruleset name * @return the full classpath to the ruleset */ - private String resolveBuiltInRuleset(final String name) { - String result = null; - if (name != null) { - // Likely a simple RuleSet name - int index = name.indexOf('-'); - if (index >= 0) { - // Standard short name - result = "rulesets/" + name.substring(0, index) + '/' + name.substring(index + 1) + ".xml"; - } else { - // A release RuleSet? - if (name.matches("[0-9]+.*")) { - result = "rulesets/releases/" + name + ".xml"; - } else { - // Appears to be a non-standard RuleSet name - result = name; - } - } + private String resolveDeprecatedBuiltInRulesetShorthand(final String name) { + if (name == null) { + return null; } - return result; + // Likely a simple RuleSet name + int index = name.indexOf('-'); + if (index > 0) { + // Standard short name + return "rulesets/" + name.substring(0, index) + '/' + name.substring(index + 1) + ".xml"; + } + // A release RuleSet? + if (name.matches("[0-9]+.*")) { + return "rulesets/releases/" + name + ".xml"; + } + // Appears to be a non-standard RuleSet name + return null; } /** @@ -327,19 +348,31 @@ public class RuleSetReferenceId { * Parse a String comma separated list of RuleSet reference IDs into a List * of RuleReferenceId instances. * - * @param referenceString - * A comma separated list of RuleSet reference IDs. + * @param referenceString A comma separated list of RuleSet reference IDs. + * * @return The corresponding List of RuleSetReferenceId instances. */ public static List parse(String referenceString) { + return parse(referenceString, false); + } + + /** + * Parse a String comma separated list of RuleSet reference IDs into a List + * of RuleReferenceId instances. + * + * @param referenceString A comma separated list of RuleSet reference IDs. + * + * @return The corresponding List of RuleSetReferenceId instances. + */ + public static List parse(String referenceString, boolean warnDeprecated) { List references = new ArrayList<>(); if (referenceString != null && referenceString.trim().length() > 0) { if (referenceString.indexOf(',') == -1) { - references.add(new RuleSetReferenceId(referenceString)); + references.add(new RuleSetReferenceId(referenceString, null, warnDeprecated)); } else { for (String name : referenceString.split(",")) { - references.add(new RuleSetReferenceId(name.trim())); + references.add(new RuleSetReferenceId(name.trim(), null, warnDeprecated)); } } } @@ -411,10 +444,9 @@ public class RuleSetReferenceId { } private FileNotFoundException notFoundException() { - return new FileNotFoundException("Can't find resource '" + ruleSetFileName + "' for rule '" + ruleName - + "'" + ". Make sure the resource is a valid file or URL and is on the classpath. " - + "Here's the current classpath: " - + System.getProperty("java.class.path")); + return new FileNotFoundException("Cannot resolve rule/ruleset reference '" + originalRef + + "'" + ". Make sure the resource is a valid file or URL and is on the CLASSPATH. " + + "Use --debug (or a fine log level) to see the current classpath."); } /** @@ -424,8 +456,11 @@ public class RuleSetReferenceId { * ruleSetFileName for all Rule external references, * ruleSetFileName/ruleName, for a single Rule external * references, or ruleName otherwise. + * + * @deprecated Do not rely on the format of this method, it may be changed in PMD 7. */ @Override + @Deprecated public String toString() { if (ruleSetFileName != null) { if (allRules) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 522e5e9741..2d0a2a08c7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -21,7 +21,6 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.CDATASection; @@ -37,6 +36,7 @@ import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.util.IOUtil; /** * This class represents a way to serialize a RuleSet to an XML configuration @@ -62,7 +62,7 @@ public class RuleSetWriter { } public void close() { - IOUtils.closeQuietly(outputStream); + IOUtil.closeQuietly(outputStream); } public void write(RuleSet ruleSet) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java index df35e7571d..043666ae98 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; @@ -33,6 +32,7 @@ import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.renderers.RendererFactory; import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; @InternalApi public class Formatter { @@ -65,10 +65,14 @@ public class Formatter { this.parameters.add(parameter); } + @Deprecated + @InternalApi public Renderer getRenderer() { return renderer; } + @Deprecated + @InternalApi public void start(String baseDir) { Properties properties = createProperties(); @@ -115,6 +119,8 @@ public class Formatter { } } + @Deprecated + @InternalApi public void end(Report errorReport) { try { renderer.renderFileReport(errorReport); @@ -129,6 +135,8 @@ public class Formatter { } } + @Deprecated + @InternalApi public boolean isNoOutputSupplied() { return toFile == null && !toConsole; } @@ -190,8 +198,8 @@ public class Formatter { isOnError = false; } finally { if (isOnError) { - IOUtils.closeQuietly(output); - IOUtils.closeQuietly(writer); + IOUtil.closeQuietly(output); + IOUtil.closeQuietly(writer); } } return writer; @@ -236,6 +244,7 @@ public class Formatter { return null; } + @Deprecated @InternalApi public GlobalAnalysisListener newListener(Project project) throws IOException { start(project.getBaseDir().toString()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java index e640d44d58..74af13c400 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +35,7 @@ import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.cache.internal.ClasspathFingerprinter; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.reporting.FileAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; /** * Abstract implementation of the analysis cache. Handles all operations, except for persistence. @@ -180,7 +180,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - String extension = FilenameUtils.getExtension(file.toString()); + String extension = IOUtil.getFilenameExtension(file.toString()); if ("jar".equalsIgnoreCase(extension)) { fileVisitor.visitFile(file, attrs); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java index be93115f1d..29e6457efe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java @@ -13,10 +13,11 @@ import java.util.Set; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.util.IOUtil; + /** * Base fingerprinter for raw files. */ @@ -41,7 +42,7 @@ public class RawFileFingerprinter implements ClasspathEntryFingerprinter { public void fingerprint(URL entry, Checksum checksum) throws IOException { try (CheckedInputStream inputStream = new CheckedInputStream(entry.openStream(), checksum)) { // Just read it, the CheckedInputStream will update the checksum on it's own - while (IOUtils.skip(inputStream, Long.MAX_VALUE) == Long.MAX_VALUE) { + while (IOUtil.skipFully(inputStream, Long.MAX_VALUE) == Long.MAX_VALUE) { // just loop } } catch (final FileNotFoundException ignored) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index df10f008f7..ecbadad53e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.List; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.PMD; @@ -32,20 +33,44 @@ import com.beust.jcommander.validators.PositiveInteger; @InternalApi public class PMDParameters { - @Parameter(names = { "--rulesets", "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", - required = true) - private String rulesets; + @Parameter(names = { "--rulesets", "-rulesets", "-R" }, + description = "Path to a ruleset xml file. " + + "The path may reference a resource on the classpath of the application, be a local file system path, or a URL. " + + "The option can be repeated, and multiple arguments can be provided to a single occurrence of the option.", + required = true, + variableArity = true) + private List rulesets; - @Parameter(names = { "--uri", "-uri", "-u" }, description = "Database URI for sources.") + @Parameter(names = { "--uri", "-uri", "-u" }, + description = "Database URI for sources. " + + "One of --dir, --file-list or --uri must be provided. " + ) private String uri; - @Parameter(names = { "--dir", "-dir", "-d" }, description = "Root directory for sources.") - private String sourceDir; + @Parameter(names = { "--dir", "-dir", "-d" }, + description = "Path to a source file, or directory containing source files to analyze. " + // About the following line: + // In PMD 6, this is only the case for files found in directories. If you + // specify a file directly, and it is unknown, then the Java parser is used. + + "Note that a file is only effectively added if it matches a language known by PMD. " + + "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. ", + variableArity = true) + private List inputPaths = new ArrayList<>(); - @Parameter(names = { "--file-list", "-filelist" }, description = "Path to a file containing a list of files to analyze.") + @Parameter(names = { "--file-list", "-filelist" }, + description = + "Path to a file containing a list of files to analyze, one path per line. " + + "One of --dir, --file-list or --uri must be provided. " + ) private String fileListPath; - @Parameter(names = { "--ignore-list", "-ignorelist" }, description = "Path to a file containing a list of files to ignore.") + @Parameter(names = { "--ignore-list", "-ignorelist" }, + description = "Path to a file containing a list of files to exclude from the analysis, one path per line. " + + "This option can be combined with --dir and --file-list. " + ) private String ignoreListPath; @Parameter(names = { "--format", "-format", "-f" }, description = "Report format type.") @@ -107,12 +132,17 @@ public class PMDParameters { @Parameter(names = { "-language", "-l" }, description = "Specify a language PMD should use.") private String language = null; - @Parameter(names = { "--force-language", "-force-language" }, description = "Force a language to be used for all input files, irrespective of filenames.") + @Parameter(names = { "--force-language", "-force-language" }, + description = "Force a language to be used for all input files, irrespective of file names. " + + "When using this option, the automatic language selection by extension is disabled, and PMD " + + "tries to parse all input files with the given language's parser. " + + "Parsing errors are ignored." + ) private String forceLanguage = null; @Parameter(names = { "--aux-classpath", "-auxclasspath" }, description = "Specifies the classpath for libraries used by the source code. " - + "This is used by the type resolution. The platform specific path delimiter " + + "This is used to resolve types in Java source files. The platform specific path delimiter " + "(\":\" on Linux, \";\" on Windows) is used to separate the entries. " + "Alternatively, a single 'file:' URL to a text file containing path elements on consecutive lines " + "can be specified.") @@ -199,12 +229,8 @@ public class PMDParameters { * @throws IllegalArgumentException if the parameters are inconsistent or incomplete */ public PMDConfiguration toConfiguration() { - if (null == this.getSourceDir() && null == this.getUri() && null == this.getFileListPath()) { - throw new IllegalArgumentException( - "Please provide a parameter for source root directory (-dir or -d), database URI (-uri or -u), or file list path (-filelist)."); - } PMDConfiguration configuration = new PMDConfiguration(); - configuration.setInputPaths(this.getSourceDir()); + configuration.setInputPaths(this.getInputPaths()); configuration.setInputFilePath(this.getFileListPath()); configuration.setIgnoreFilePath(this.getIgnoreListPath()); configuration.setInputUri(this.getUri()); @@ -348,12 +374,22 @@ public class PMDParameters { return auxclasspath; } + @Deprecated public String getRulesets() { + return StringUtils.join(rulesets, ","); + } + + public List getRulesetRefs() { return rulesets; } + public List getInputPaths() { + return inputPaths; + } + + @Deprecated public String getSourceDir() { - return sourceDir; + return StringUtils.join(inputPaths, ","); } public String getFileListPath() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java index 0ffc8465d0..2f863ba4d9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java @@ -105,13 +105,29 @@ public final class PmdParametersParseResult { jcommander.setProgramName("pmd"); try { - jcommander.parse(args); + parseAndValidate(jcommander, result, args); return new PmdParametersParseResult(result, filterDeprecatedOptions(args)); } catch (ParameterException e) { return new PmdParametersParseResult(e, filterDeprecatedOptions(args)); } } + private static void parseAndValidate(JCommander jcommander, PMDParameters result, String[] args) { + jcommander.parse(args); + if (result.isHelp() || result.isVersion()) { + return; + } + // jcommander has no special support for global parameter validation like this + // For consistency we report this with a ParameterException + if (result.getInputPaths().isEmpty() + && null == result.getUri() + && null == result.getFileListPath()) { + throw new ParameterException( + "Please provide a parameter for source root directory (--dir or -d), database URI (--uri or -u), or file list path (--file-list)."); + } + + } + private static Map filterDeprecatedOptions(String... args) { Map argSet = new LinkedHashMap<>(SUGGESTED_REPLACEMENT); argSet.keySet().retainAll(new HashSet<>(Arrays.asList(args))); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index bcc83fb7b5..68acf184d0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -4,9 +4,11 @@ package net.sourceforge.pmd.cpd; +import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -15,13 +17,14 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.cli.internal.CliMessages; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.util.FileFinder; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -93,8 +96,7 @@ public class CPD { current.add(signature); } - if (!FilenameUtils.equalsNormalizedOnSystem(file.getAbsoluteFile().getCanonicalPath(), - file.getAbsolutePath())) { + if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { System.err.println("Skipping " + file + " since it appears to be a symlink"); return; } @@ -174,7 +176,75 @@ public class CPD { return new ArrayList<>(source.values()); } + /** + * Entry to invoke CPD as command line tool. Note that this will + * invoke {@link System#exit(int)}. + * + * @param args command line arguments + */ public static void main(String[] args) { - CPDCommandLineInterface.main(args); + StatusCode statusCode = runCpd(args); + CPDCommandLineInterface.setStatusCodeOrExit(statusCode.toInt()); + } + + /** + * Parses the command line and executes CPD. Returns the status code + * without exiting the VM. + * + * @param args command line arguments + * + * @return the status code + */ + public static StatusCode runCpd(String... args) { + CPDConfiguration arguments = new CPDConfiguration(); + CPD.StatusCode statusCode = CPDCommandLineInterface.parseArgs(arguments, args); + if (statusCode != null) { + return statusCode; + } + + CPD cpd = new CPD(arguments); + + try { + CPDCommandLineInterface.addSourceFilesToCPD(cpd, arguments); + + cpd.go(); + if (arguments.getCPDRenderer() == null) { + // legacy writer + System.out.println(arguments.getRenderer().render(cpd.getMatches())); + } else { + arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out))); + } + if (cpd.getMatches().hasNext()) { + if (arguments.isFailOnViolation()) { + statusCode = StatusCode.DUPLICATE_CODE_FOUND; + } else { + statusCode = StatusCode.OK; + } + } else { + statusCode = StatusCode.OK; + } + } catch (IOException | RuntimeException e) { + LOG.debug(e.toString(), e); + LOG.error(CliMessages.errorDetectedMessage(1, CPDCommandLineInterface.PROGRAM_NAME)); + statusCode = StatusCode.ERROR; + } + return statusCode; + } + + public enum StatusCode { + OK(0), + ERROR(1), + DUPLICATE_CODE_FOUND(4); + + private final int code; + + StatusCode(int code) { + this.code = code; + } + + /** Returns the exit code as used in CLI. */ + public int toInt() { + return this.code; + } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java index fce76c1ae6..919d02cb45 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java @@ -4,11 +4,9 @@ package net.sourceforge.pmd.cpd; -import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.OutputStreamWriter; import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; @@ -26,27 +24,45 @@ import org.slf4j.LoggerFactory; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.cli.internal.CliMessages; +import net.sourceforge.pmd.cpd.CPD.StatusCode; import net.sourceforge.pmd.util.FileUtil; import net.sourceforge.pmd.util.database.DBURI; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterException; +/** + * @deprecated Internal API. Use {@link CPD#runCpd(String...)} or {@link CPD#main(String[])} + * in order to execute CPD. + */ +@Deprecated +@InternalApi public final class CPDCommandLineInterface { private static final Logger LOG = LoggerFactory.getLogger(CPDCommandLineInterface.class); - private static final int NO_ERRORS_STATUS = 0; - private static final int ERROR_STATUS = 1; - private static final int DUPLICATE_CODE_FOUND = 4; - + /** + * @deprecated This is used for testing, but support for it will be removed in PMD 7. + * Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7, + * {@link CPD#main(String[])} will call {@link System#exit(int)} always. + */ + @Deprecated public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit"; + + /** + * @deprecated This is used for testing, but support for it will be removed in PMD 7. + * Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7, + * {@link CPD#main(String[])} will call {@link System#exit(int)} always. + */ + @Deprecated public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status"; - private static final String PROGRAM_NAME = "cpd"; + static final String PROGRAM_NAME = "cpd"; private CPDCommandLineInterface() { } + @Deprecated public static void setStatusCodeOrExit(int status) { if (isExitAfterRunSet()) { System.exit(status); @@ -67,8 +83,7 @@ public final class CPDCommandLineInterface { System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode)); } - public static void main(String[] args) { - CPDConfiguration arguments = new CPDConfiguration(); + static StatusCode parseArgs(CPDConfiguration arguments, String... args) { JCommander jcommander = new JCommander(arguments); jcommander.setProgramName(PROGRAM_NAME); @@ -77,14 +92,12 @@ public final class CPDCommandLineInterface { if (arguments.isHelp()) { jcommander.usage(); System.out.println(buildUsageText()); - setStatusCodeOrExit(NO_ERRORS_STATUS); - return; + return StatusCode.OK; } } catch (ParameterException e) { System.err.println(e.getMessage()); System.err.println(CliMessages.runWithHelpFlagMessage()); - setStatusCodeOrExit(ERROR_STATUS); - return; + return StatusCode.ERROR; } Map deprecatedOptions = filterDeprecatedOptions(args); @@ -98,33 +111,16 @@ public final class CPDCommandLineInterface { // Pass extra parameters as System properties to allow language // implementation to retrieve their associate values... CPDConfiguration.setSystemProperties(arguments); - CPD cpd = new CPD(arguments); - try { - addSourceFilesToCPD(cpd, arguments); + return null; + } - cpd.go(); - if (arguments.getCPDRenderer() == null) { - // legacy writer - System.out.println(arguments.getRenderer().render(cpd.getMatches())); - } else { - arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out))); - } - if (cpd.getMatches().hasNext()) { - if (arguments.isFailOnViolation()) { - setStatusCodeOrExit(DUPLICATE_CODE_FOUND); - } else { - setStatusCodeOrExit(NO_ERRORS_STATUS); - } - } else { - setStatusCodeOrExit(NO_ERRORS_STATUS); - } - } catch (IOException | RuntimeException e) { - e.printStackTrace(); - LOG.debug(e.toString(), e); - LOG.error(CliMessages.errorDetectedMessage(1, "CPD")); - setStatusCodeOrExit(ERROR_STATUS); - } + /** + * @deprecated Use {@link CPD#main(String[])} + */ + @Deprecated + public static void main(String[] args) { + setStatusCodeOrExit(CPD.runCpd(args).toInt()); } private static Map filterDeprecatedOptions(String... args) { @@ -207,6 +203,8 @@ public final class CPDCommandLineInterface { } } + @Deprecated + @InternalApi public static String buildUsageText() { String helpText = " For example on Windows:" + PMD.EOL; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 91f8979744..9524fef9c2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -147,7 +147,16 @@ public class GUI implements CPDListener { @Override public boolean canIgnoreAnnotations() { - return "java".equals(terseName); + if (terseName == null) { + return false; + } + switch (terseName) { + case "cs": + case "java": + return true; + default: + return false; + } } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java index db7c88c22e..20529bdea6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java @@ -14,8 +14,7 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; -import org.apache.commons.io.ByteOrderMark; -import org.apache.commons.io.input.BOMInputStream; +import net.sourceforge.pmd.util.IOUtil; public class SourceCode { @@ -112,11 +111,10 @@ public class SourceCode { @Override public Reader getReader() throws Exception { - BOMInputStream inputStream = new BOMInputStream(Files.newInputStream(file.toPath()), ByteOrderMark.UTF_8, - ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE); + IOUtil.BomAwareInputStream inputStream = new IOUtil.BomAwareInputStream(Files.newInputStream(file.toPath())); - if (inputStream.hasBOM()) { - encoding = inputStream.getBOMCharsetName(); + if (inputStream.hasBom()) { + encoding = inputStream.getBomCharsetName(); } return new InputStreamReader(inputStream, encoding); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java index 488c55f0c1..d8fe9c682e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,6 +22,7 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.util.FileUtil; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -156,7 +156,7 @@ public final class FileCollectionUtil { LOG.trace("Adding database source object {}", falseFilePath); try (Reader sourceCode = dbmsMetadata.getSourceCode(sourceObject)) { - String source = IOUtils.toString(sourceCode); + String source = IOUtil.readToString(sourceCode); collector.addSourceFile(source, falseFilePath); } catch (SQLException ex) { collector.getReporter().warnEx("Cannot get SourceCode for {} - skipping ...", diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporter.java index ba41117640..4f5c5b484c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporter.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.ast; import java.text.MessageFormat; -import org.slf4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.event.Level; import net.sourceforge.pmd.util.StringUtil; @@ -20,21 +20,11 @@ public interface SemanticErrorReporter { // TODO use resource bundle keys instead of string messages. - /** - * Report an informational message at the given location. - * - * @param location Location where the message should be reported - * @param message Message (rendered using a {@link MessageFormat}) - * @param formatArgs Format arguments - */ - default void info(Node location, String message, Object... formatArgs) { - // noop - } - - /** * Report a warning at the given location. Warnings do not abort - * the analysis. + * the analysis. They are usually recoverable errors. They are used + * to warn the user that something wrong is going on, which may cause + * subsequent errors or inconsistent behavior. * * @param location Location where the warning should be reported * @param message Message (rendered using a {@link MessageFormat}) @@ -44,9 +34,9 @@ public interface SemanticErrorReporter { /** - * Report an error at the given location. Errors abort subsequent analysis. - * The produced error can be thrown by the caller if it cannot be recovered - * from. + * Report an error at the given location. Errors abort subsequent analysis + * and cause a processing error to be put in the report. The produced error + * can be thrown by the caller if it cannot be recovered from. * * @param location Location where the error should be reported * @param message Message (rendered using a {@link MessageFormat}) @@ -56,14 +46,17 @@ public interface SemanticErrorReporter { /** - * Returns true if at least one error has been reported. + * If {@link #error(Node, String, Object...)} has been called, return + * a semantic exception instance with the correct message. If it has been + * called more than once, return the first exception, possibly with suppressed + * exceptions for subsequent calls to {@link #error(Node, String, Object...)}. */ - boolean hasError(); + @Nullable SemanticException getFirstError(); static SemanticErrorReporter noop() { return new SemanticErrorReporter() { - private boolean hasError = false; + private SemanticException exception; @Override public void warning(Node location, String message, Object... formatArgs) { @@ -72,13 +65,18 @@ public interface SemanticErrorReporter { @Override public SemanticException error(Node location, String message, Object... formatArgs) { - hasError = true; - return new SemanticException(MessageFormat.format(message, formatArgs)); + SemanticException ex = new SemanticException(MessageFormat.format(message, formatArgs)); + if (this.exception == null) { + this.exception = ex; + } else { + this.exception.addSuppressed(ex); + } + return ex; } @Override - public boolean hasError() { - return hasError; + public @Nullable SemanticException getFirstError() { + return exception; } }; } @@ -88,9 +86,10 @@ public interface SemanticErrorReporter { * Forwards to a {@link MessageReporter}, except trace and debug * messages which are reported on a logger. */ - static SemanticErrorReporter reportToLogger(MessageReporter reporter, Logger logger) { + static SemanticErrorReporter reportToLogger(MessageReporter reporter) { return new SemanticErrorReporter() { - private boolean hasError = false; + + private SemanticException exception = null; private String locPrefix(Node loc) { return "at " + loc.getReportLocation() @@ -103,19 +102,10 @@ public interface SemanticErrorReporter { private String logMessage(Level level, Node location, String message, Object[] args) { String fullMessage = makeMessage(location, message, args); - if (level.compareTo(Level.INFO) > 0) { - logger.atLevel(level).log(fullMessage); - } else { - reporter.log(level, StringUtil.quoteMessageFormat(fullMessage)); // already formatted - } + reporter.log(level, StringUtil.quoteMessageFormat(fullMessage)); // already formatted return fullMessage; } - @Override - public void info(Node location, String message, Object... formatArgs) { - logMessage(Level.INFO, location, message, formatArgs); - } - @Override public void warning(Node location, String message, Object... args) { logMessage(Level.WARN, location, message, args); @@ -123,14 +113,19 @@ public interface SemanticErrorReporter { @Override public SemanticException error(Node location, String message, Object... args) { - hasError = true; String fullMessage = logMessage(Level.ERROR, location, message, args); - return new SemanticException(fullMessage); + SemanticException ex = new SemanticException(fullMessage); + if (this.exception == null) { + this.exception = ex; + } else { + this.exception.addSuppressed(ex); + } + return ex; } @Override - public boolean hasError() { - return hasError; + public @Nullable SemanticException getFirstError() { + return exception; } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/BaseAntlrInnerNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/BaseAntlrInnerNode.java index 5833547776..03c37ca247 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/BaseAntlrInnerNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/BaseAntlrInnerNode.java @@ -88,6 +88,12 @@ public abstract class BaseAntlrInnerNode> extends BaseAnt return pmdWrapper != null ? pmdWrapper.asAntlrNode() : null; } + protected List getTokens(int kind) { + return children(BaseAntlrTerminalNode.class) + .filter(it -> it.getTokenKind() == kind) + .toList(BaseAntlrTerminalNode::asAntlrNode); + } + protected void copyFrom(BaseAntlrInnerNode other) { asAntlrNode().copyFrom(other.asAntlrNode()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java index 7cf4967830..6e4a26b1f1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java @@ -14,7 +14,6 @@ import java.io.Reader; import java.nio.charset.Charset; import java.nio.file.Path; -import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.PMDConfiguration; @@ -25,6 +24,7 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.TextFileBuilder.ForCharSeq; import net.sourceforge.pmd.lang.document.TextFileBuilder.ForNio; import net.sourceforge.pmd.lang.document.TextFileBuilder.ForReader; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; /** @@ -283,7 +283,7 @@ public interface TextFile extends Closeable { ensureOpen(); try (InputStream is = ds.getInputStream(); Reader reader = new BufferedReader(new InputStreamReader(is, config.getSourceEncoding()))) { - String contents = IOUtils.toString(reader); + String contents = IOUtil.readToString(reader); return TextFileContent.fromCharSeq(contents); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java index cb77db852c..0219448387 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java @@ -20,11 +20,11 @@ import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; -import org.apache.commons.io.ByteOrderMark; -import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.util.IOUtil; + /** * Contents of a text file. */ @@ -161,7 +161,7 @@ public final class TextFileContent { static @NonNull TextFileContent normalizeCharSeq(CharSequence text, String fallBackLineSep) { long checksum = getCheckSum(text); // the checksum is computed on the original file - if (text.length() > 0 && text.charAt(0) == ByteOrderMark.UTF_BOM) { + if (text.length() > 0 && text.charAt(0) == IOUtil.UTF_BOM) { text = text.subSequence(1, text.length()); // skip the BOM } Matcher matcher = NEWLINE_PATTERN.matcher(text); @@ -208,11 +208,11 @@ public final class TextFileContent { int bufOffset = 0; int nextCharToCopy = 0; int n = input.read(cbuf); - if (n > 0 && cbuf[0] == ByteOrderMark.UTF_BOM) { + if (n > 0 && cbuf[0] == IOUtil.UTF_BOM) { nextCharToCopy = 1; } - while (n != IOUtils.EOF) { + while (n != IOUtil.EOF) { if (updateChecksum) { // if we use a checked input stream we dont need to update the checksum manually // note that this checksum operates on non-normalized characters diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java index 1a83310247..847b96175a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java @@ -52,7 +52,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin BaseNodeInfo parent, Node wrappedNode, Configuration configuration) { - super(Type.ELEMENT, configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent); + super(determineType(wrappedNode), configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent); this.treeInfo = document; this.wrappedNode = wrappedNode; @@ -65,6 +65,19 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin } } + private static int determineType(Node node) { + // As of PMD 6.48.0, only the experimental HTML module uses this naming + // convention to identify non-element nodes. + // TODO PMD 7: maybe generalize this to other languages + String name = node.getXPathNodeName(); + if ("#text".equals(name)) { + return Type.TEXT; + } else if ("#comment".equals(name)) { + return Type.COMMENT; + } + return Type.ELEMENT; + } + public Map makeAttributes(Node wrappedNode) { Map atts = new HashMap<>(); Iterator it = wrappedNode.getXPathAttributesIterator(); @@ -179,6 +192,10 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin @Override public CharSequence getStringValueCS() { + if (getNodeKind() == Type.TEXT || getNodeKind() == Type.COMMENT) { + return getUnderlyingNode().getImage(); + } + // https://www.w3.org/TR/xpath-datamodel-31/#ElementNode // The string-value property of an Element Node must be the // concatenation of the string-values of all its Text Node @@ -187,6 +204,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin // Since we represent all our Nodes as elements, there are no // text nodes + // TODO: for some languages like html we have text nodes return ""; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java index 8aea0f054e..4a79f9e516 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java @@ -24,6 +24,7 @@ import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; +import net.sourceforge.pmd.lang.ast.SemanticException; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.reporting.FileAnalysisListener; @@ -124,7 +125,7 @@ abstract class PmdRunnable implements Runnable { TextDocument textDocument, RuleSets ruleSets) throws FileAnalysisException { - SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(configuration.getReporter(), LOG); + SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(configuration.getReporter()); ParserTask task = new ParserTask( textDocument, reporter, @@ -141,9 +142,10 @@ abstract class PmdRunnable implements Runnable { RootNode rootNode = parse(parser, task); - if (reporter.hasError()) { - reporter.info(rootNode, "Errors occurred in file, skipping rule analysis"); - return; + SemanticException semanticError = reporter.getFirstError(); + if (semanticError != null) { + // cause a processing error to be reported and rule analysis to be skipped + throw semanticError; } ruleSets.apply(rootNode, listener); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java index 0627a50834..dabd3a99ae 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java @@ -7,8 +7,6 @@ package net.sourceforge.pmd.renderers; import java.io.IOException; import java.io.Writer; -import org.apache.commons.io.IOUtils; - import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.cli.PMDParameters; @@ -103,7 +101,7 @@ public abstract class AbstractRenderer extends AbstractPropertySource implements } catch (IOException e) { throw new IllegalStateException(e); } finally { - IOUtils.closeQuietly(writer); + IOUtil.closeQuietly(writer); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index 92d26e1adb..566ed812b9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -22,7 +22,6 @@ import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; -import org.apache.commons.io.output.WriterOutputStream; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.PMDVersion; @@ -30,6 +29,7 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.StringUtil; /** @@ -258,7 +258,7 @@ public class XMLRenderer extends AbstractIncrementingRenderer { public void setWriter(final Writer writer) { String encoding = getProperty(ENCODING); // for backwards compatibility, create a OutputStream that writes to the writer. - this.stream = new WriterOutputStream(writer, encoding); + this.stream = IOUtil.fromWriter(writer, encoding); XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); try { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java index 874a004bcf..c632997409 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java @@ -8,10 +8,9 @@ import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; -import org.apache.commons.io.comparator.PathFileComparator; - import net.sourceforge.pmd.annotation.InternalApi; /** @@ -49,7 +48,12 @@ public class FileFinder { return; } - Arrays.sort(candidates, PathFileComparator.PATH_INSENSITIVE_COMPARATOR); + Arrays.sort(candidates, new Comparator() { + @Override + public int compare(File o1, File o2) { + return o1.getPath().compareToIgnoreCase(o2.getPath()); + } + }); for (File tmp : candidates) { if (tmp.isDirectory()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java index 1c9a64194e..2ee4a303af 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java @@ -6,22 +6,30 @@ package net.sourceforge.pmd.util; import java.io.Closeable; import java.io.File; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; import java.util.List; +import java.util.Objects; -import org.apache.commons.io.ByteOrderMark; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; @@ -35,6 +43,13 @@ import net.sourceforge.pmd.annotation.InternalApi; @InternalApi @Deprecated public final class IOUtil { + /** + * Unicode BOM character. Replaces commons io ByteOrderMark. + */ + public static final char UTF_BOM = '\uFEFF'; + /** Conventional return value for readers. */ + public static final int EOF = -1; + private static final int BUFFER_SIZE = 8192; private IOUtil() { } @@ -104,36 +119,9 @@ public final class IOUtil { } } - public static Reader skipBOM(Reader source) throws IOException { - int firstCharacter = source.read(); - if (firstCharacter == ByteOrderMark.UTF_BOM) { - return source; // with one less char - } - return new Reader() { - boolean done; - - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - if (done) { - return source.read(cbuf, off, len); - } else if (len > 0) { - done = true; - cbuf[off] = (char) firstCharacter; - return 1; - } - return 0; - } - - @Override - public void close() throws IOException { - source.close(); - } - }; - } - public static void tryCloseClassLoader(ClassLoader classLoader) { if (classLoader instanceof Closeable) { - IOUtils.closeQuietly((Closeable) classLoader); + closeQuietly((Closeable) classLoader); } } @@ -182,4 +170,315 @@ public final class IOUtil { throw pendingException; } } + + + // The following methods are taken from Apache Commons IO. + // The dependency was removed from PMD 6 because it had a security issue, + // and upgrading was not possible without upgrading to Java 8. + // See https://github.com/pmd/pmd/pull/3968 + // TODO PMD 7: consider bringing back commons-io and cleaning this class up. + + public static void closeQuietly(Closeable closeable) { + try { + closeable.close(); + } catch (IOException ignored) { + // ignored + } + } + + public static byte[] toByteArray(InputStream stream) throws IOException { + byte[] result = new byte[0]; + byte[] buffer = new byte[BUFFER_SIZE]; + int count = stream.read(buffer); + while (count > -1) { + byte[] newResult = new byte[result.length + count]; + System.arraycopy(result, 0, newResult, 0, result.length); + System.arraycopy(buffer, 0, newResult, result.length, count); + result = newResult; + count = stream.read(buffer); + } + return result; + } + + public static long skipFully(InputStream stream, long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException(); + } + + long bytesToSkip = n; + byte[] buffer = new byte[(int) Math.min(BUFFER_SIZE, bytesToSkip)]; + while (bytesToSkip > 0) { + int count = stream.read(buffer, 0, (int) Math.min(BUFFER_SIZE, bytesToSkip)); + if (count < 0) { + // reached eof + break; + } + bytesToSkip -= count; + } + return n - bytesToSkip; + } + + public static String normalizePath(String path) { + Path path1 = Paths.get(path); + path1.isAbsolute(); + String normalized = path1.normalize().toString(); + if (normalized.contains("." + File.separator) || normalized.contains(".." + File.separator) || "".equals(normalized)) { + return null; + } + return normalized; + } + + public static boolean equalsNormalizedPaths(String path1, String path2) { + return Objects.equals(normalizePath(path1), normalizePath(path2)); + } + + public static String getFilenameExtension(String name) { + String filename = Paths.get(name).getFileName().toString(); + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(dot + 1); + } + return ""; + } + + public static String getFilenameBase(String name) { + String filename = Paths.get(name).getFileName().toString(); + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(0, dot); + } + return filename; + } + + public static void copy(InputStream from, OutputStream to) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int count = from.read(buffer); + while (count > -1) { + to.write(buffer, 0, count); + count = from.read(buffer); + } + } + + public static void copy(Reader from, Writer to) throws IOException { + char[] buffer = new char[BUFFER_SIZE]; + int count = from.read(buffer); + while (count > -1) { + to.write(buffer, 0, count); + count = from.read(buffer); + } + } + + public static String readFileToString(File file) throws IOException { + return readFileToString(file, Charset.defaultCharset()); + } + + public static String readFileToString(File file, Charset charset) throws IOException { + byte[] bytes = Files.readAllBytes(file.toPath()); + return charset.decode(ByteBuffer.wrap(bytes)).toString(); + } + + public static String readToString(Reader reader) throws IOException { + StringBuilder sb = new StringBuilder(BUFFER_SIZE); + char[] buffer = new char[BUFFER_SIZE]; + int count = reader.read(buffer); + while (count > -1) { + sb.append(buffer, 0, count); + count = reader.read(buffer); + } + return sb.toString(); + } + + public static String readToString(InputStream stream, Charset charset) throws IOException { + byte[] bytes = toByteArray(stream); + return charset.decode(ByteBuffer.wrap(bytes)).toString(); + } + + public static InputStream fromReader(Reader reader) throws IOException { + class ReaderInputStream extends InputStream { + private final Reader reader; + private final CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE); + private final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private final CharsetEncoder encoder; + + private boolean eof; + + ReaderInputStream(Reader reader) { + this.reader = reader; + encoder = Charset.defaultCharset().newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + charBuffer.clear(); + byteBuffer.clear(); + byteBuffer.flip(); // byte buffer is empty at the beginning, no bytes read yet + } + + @Override + public int read() throws IOException { + if (!byteBuffer.hasRemaining()) { + if (charBuffer.hasRemaining() && !eof) { + int count = reader.read(charBuffer); + eof = count == -1; + } + byteBuffer.clear(); + charBuffer.flip(); + encoder.encode(charBuffer, byteBuffer, eof); + byteBuffer.flip(); + charBuffer.compact(); + } + + if (byteBuffer.hasRemaining()) { + return byteBuffer.get(); + } + + return -1; + } + + @Override + public int available() throws IOException { + return byteBuffer.remaining(); + } + + @Override + public void close() throws IOException { + reader.close(); + } + } + + return new ReaderInputStream(reader); + } + + public static OutputStream fromWriter(Writer writer, String encoding) throws UnsupportedCharsetException { + class WriterOutputStream extends OutputStream { + private final Writer writer; + private final CharsetDecoder decoder; + private final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private final CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE); + + WriterOutputStream(Writer writer, String encoding) throws UnsupportedCharsetException { + this.writer = writer; + Charset charset = Charset.forName(encoding); + decoder = charset.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + byteBuffer.clear(); + charBuffer.clear(); + } + + @Override + public void write(int b) throws IOException { + if (!byteBuffer.hasRemaining()) { + decodeByteBuffer(false); + } + byteBuffer.put((byte) b); + } + + @Override + public void flush() throws IOException { + decodeByteBuffer(false); + } + + private void decodeByteBuffer(boolean isClosing) throws IOException { + byteBuffer.flip(); + charBuffer.clear(); + decoder.decode(byteBuffer, charBuffer, isClosing); + writer.write(charBuffer.array(), 0, charBuffer.position()); + writer.flush(); + byteBuffer.compact(); + } + + @Override + public void close() throws IOException { + flush(); + decodeByteBuffer(true); + writer.close(); + } + } + + return new WriterOutputStream(writer, encoding); + } + + /** + *

+ * Input stream that skips an optional byte order mark at the beginning + * of the stream. Whether the stream had a byte order mark (encoded in either UTF-8, + * UTF-16LE or UTF-16BE) can be checked with {@link #hasBom()}. The corresponding + * charset can be retrieved with {@link #getBomCharsetName()}. + *

+ * + *

+ * If the stream didn't had a BOM, then no bytes are skipped. + *

+ */ + public static class BomAwareInputStream extends FilterInputStream { + + private byte[] begin; + int beginIndex; + + private String charset; + + public BomAwareInputStream(InputStream in) { + super(in); + begin = determineBom(); + } + + private byte[] determineBom() { + byte[] bytes = new byte[3]; + try { + int count = in.read(bytes); + if (count == 3 && bytes[0] == (byte) 0xef && bytes[1] == (byte) 0xbb && bytes[2] == (byte) 0xbf) { + charset = StandardCharsets.UTF_8.name(); + return new byte[0]; // skip all 3 bytes + } else if (count >= 2 && bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) { + charset = StandardCharsets.UTF_16BE.name(); + return new byte[] { bytes[2] }; + } else if (count >= 2 && bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe) { + charset = StandardCharsets.UTF_16LE.name(); + return new byte[] { bytes[2] }; + } else if (count == 3) { + return bytes; + } + + if (count < 0) { + return new byte[0]; + } + + byte[] read = new byte[count]; + for (int i = 0; i < count; i++) { + read[i] = bytes[i]; + } + return read; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public int read() throws IOException { + if (beginIndex < begin.length) { + return begin[beginIndex++]; + } + return super.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (beginIndex < begin.length) { + int count = 0; + for (; count < len && beginIndex < begin.length; beginIndex++) { + b[off + count] = begin[beginIndex]; + count++; + } + return count; + } + return super.read(b, off, len); + } + + public boolean hasBom() { + return charset != null; + } + + public String getBomCharsetName() { + return charset; + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/FileDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/FileDataSource.java index b0624e75b7..77ddb6c354 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/FileDataSource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/FileDataSource.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.util.datasource.internal.AbstractDataSource; /** * DataSource implementation to read data from a file. */ +@Deprecated public class FileDataSource extends AbstractDataSource { private final File file; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java index ec21c09f4b..6edb9e6553 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java @@ -8,13 +8,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import org.apache.commons.io.input.ReaderInputStream; - +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.internal.AbstractDataSource; /** * DataSource implementation to read data from a Reader. */ +@Deprecated public class ReaderDataSource extends AbstractDataSource { /** * Reader @@ -50,7 +50,7 @@ public class ReaderDataSource extends AbstractDataSource { */ @Override public InputStream getInputStream() throws IOException { - return new ReaderInputStream(reader); + return IOUtil.fromReader(reader); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ZipDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ZipDataSource.java index 09ac322b5e..6cbfa9ce80 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ZipDataSource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ZipDataSource.java @@ -14,6 +14,7 @@ import net.sourceforge.pmd.util.datasource.internal.AbstractDataSource; /** * DataSource implementation to read data from an entry in a zip or jar file. */ +@Deprecated public class ZipDataSource extends AbstractDataSource { private final ZipFile zipFile; private final ZipEntry zipEntry; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/AbstractDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/AbstractDataSource.java index 56c54547b3..81484cad5d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/AbstractDataSource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/AbstractDataSource.java @@ -8,6 +8,7 @@ import java.io.IOException; import net.sourceforge.pmd.util.datasource.DataSource; +@Deprecated public abstract class AbstractDataSource implements DataSource { @Override diff --git a/pmd-core/src/main/resources/rulesets/releases/6460.xml b/pmd-core/src/main/resources/rulesets/releases/6460.xml new file mode 100644 index 0000000000..9601247606 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6460.xml @@ -0,0 +1,14 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.46.0 + + + + + + diff --git a/pmd-core/src/main/resources/rulesets/releases/700.xml b/pmd-core/src/main/resources/rulesets/releases/700.xml index 75821d4a05..4646553133 100644 --- a/pmd-core/src/main/resources/rulesets/releases/700.xml +++ b/pmd-core/src/main/resources/rulesets/releases/700.xml @@ -12,5 +12,13 @@ This ruleset contains links to rules that are new in PMD v7.0.0 + + + + + + + + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 21eb127a96..e158259c49 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.hamcrest.MatcherAssert; import org.junit.Assert; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; @@ -128,6 +129,19 @@ public class RuleSetFactoryTest { assertEquals("avoid the mock rule", r.getMessage()); } + @Test + public void testSingleRuleEmptyRef() { + RuleSet rs = loadRuleSet(SINGLE_RULE_EMPTY_REF); + assertEquals(1, rs.size()); + + MatcherAssert.assertThat(systemErrRule.getLog(), containsString("Empty ref attribute in ruleset 'test'")); + + Rule r = rs.getRules().iterator().next(); + assertEquals("MockRuleName", r.getName()); + assertEquals("net.sourceforge.pmd.lang.rule.MockRule", r.getRuleClass()); + assertEquals("avoid the mock rule", r.getMessage()); + } + @Test public void testMultipleRules() { RuleSet rs = loadRuleSet(MULTIPLE_RULES); @@ -881,16 +895,31 @@ public class RuleSetFactoryTest { @Test public void testMissingRuleSetDescriptionIsWarning() { loadRuleSetWithDeprecationWarnings( - "\n" + "\n" - + " \n" - + " \n" + "\n" + "\n" + + " \n" + + " \n" ); assertTrue(systemErrRule.getLog().contains("RuleSet description is missing.")); } + @Test + public void testDeprecatedRulesetReferenceProducesWarning() throws Exception { + loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n"); + + MatcherAssert.assertThat(systemErrRule.getLog(), containsString("Ruleset reference 'dummy-basic' uses a deprecated form, use 'rulesets/dummy/basic.xml' instead")); + } + private static final String REF_OVERRIDE_ORIGINAL_NAME = "\n" + "\n" + " testdesc\n" @@ -1017,6 +1046,18 @@ public class RuleSetFactoryTest { + "3\n" + ""; + private static final String SINGLE_RULE_EMPTY_REF = "\n" + + "\n" + + "testdesc\n" + + "\n" + + "3\n" + + ""; + private static final String MULTIPLE_RULES = "\n" + "\n" + "\n" diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java index 0914045572..fa48c86698 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java @@ -22,9 +22,9 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.List; -import org.apache.commons.io.IOUtils; import org.junit.Test; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.ResourceLoader; import com.github.tomakehurst.wiremock.junit.WireMockRule; @@ -123,7 +123,7 @@ public class RuleSetReferenceIdTest { assertRuleSetReferenceId(true, rulesetUrl, true, null, rulesetUrl, ruleSetReferenceId); try (InputStream inputStream = ruleSetReferenceId.getInputStream(new ResourceLoader())) { - String loaded = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + String loaded = IOUtil.readToString(inputStream, StandardCharsets.UTF_8); assertEquals("xyz", loaded); } @@ -139,8 +139,8 @@ public class RuleSetReferenceIdTest { String path = "/profiles/export?format=pmd&language=java&name=Sonar%2520way"; String completePath = path + "/DummyBasicMockRule"; String hostpart = "http://localhost:" + wireMockRule.port(); - String basicRuleSet = IOUtils - .toString(RuleSetReferenceId.class.getResourceAsStream("/rulesets/dummy/basic.xml"), StandardCharsets.UTF_8); + String basicRuleSet = IOUtil + .readToString(RuleSetReferenceId.class.getResourceAsStream("/rulesets/dummy/basic.xml"), StandardCharsets.UTF_8); stubFor(head(urlEqualTo(completePath)).willReturn(aResponse().withStatus(404))); stubFor(head(urlEqualTo(path)).willReturn(aResponse().withStatus(200).withHeader("Content-type", "text/xml"))); @@ -152,7 +152,7 @@ public class RuleSetReferenceIdTest { ruleSetReferenceId); try (InputStream inputStream = ruleSetReferenceId.getInputStream(new ResourceLoader())) { - String loaded = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + String loaded = IOUtil.readToString(inputStream, StandardCharsets.UTF_8); assertEquals(basicRuleSet, loaded); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index c896f00f3d..13a672ddef 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -31,7 +31,6 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.commons.io.FilenameUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.Test; @@ -47,6 +46,7 @@ import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; +import net.sourceforge.pmd.util.IOUtil; public class RuleSetTest { @@ -528,7 +528,7 @@ public class RuleSetTest { assertThat(error.getMsg(), containsString("java.lang.IllegalStateException: Test exception while applying rule\n")); assertThat(error.getMsg(), containsString("Rule applied on node=dummyRootNode[@Image=Foo]")); assertThat(error.getError().getCause(), instanceOf(IllegalStateException.class)); - assertThat(FilenameUtils.normalize(error.getFile(), true), equalTo("samplefile.dummy")); + assertThat(IOUtil.normalizePath(error.getFile()), equalTo("samplefile.dummy")); assertThat(report.getViolations(), hasSize(1)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 0e4666817c..426efc38e9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import org.apache.commons.io.IOUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileRule; import org.junit.AfterClass; @@ -25,6 +24,7 @@ import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TestRule; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.util.IOUtil; public class PMDTaskTest { @@ -91,7 +91,7 @@ public class PMDTaskTest { buildRule.executeTarget("testWithShortFilenames"); try (InputStream in = new FileInputStream("target/pmd-ant-test.txt")) { - String actual = IOUtils.toString(in, StandardCharsets.UTF_8); + String actual = IOUtil.readToString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.trim(); assertThat(actual, containsString("sample.dummy:1:\tSampleXPathRule:\tTest Rule 2")); @@ -104,11 +104,11 @@ public class PMDTaskTest { try (InputStream in = new FileInputStream("target/pmd-ant-xml.xml"); InputStream expectedStream = PMDTaskTest.class.getResourceAsStream("xml/expected-pmd-ant-xml.xml")) { - String actual = IOUtils.toString(in, StandardCharsets.UTF_8); + String actual = IOUtil.readToString(in, StandardCharsets.UTF_8); actual = actual.replaceFirst("timestamp=\"[^\"]+\"", "timestamp=\"\""); actual = actual.replaceFirst("\\.xsd\" version=\"[^\"]+\"", ".xsd\" version=\"\""); - String expected = IOUtils.toString(expectedStream, StandardCharsets.UTF_8); + String expected = IOUtil.readToString(expectedStream, StandardCharsets.UTF_8); expected = expected.replaceFirst("timestamp=\"[^\"]+\"", "timestamp=\"\""); expected = expected.replaceFirst("\\.xsd\" version=\"[^\"]+\"", ".xsd\" version=\"\""); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java index 47106eb626..1b71d3f87a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java @@ -20,8 +20,8 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.commons.io.IOUtils; import org.hamcrest.Matcher; +import org.hamcrest.MatcherAssert; import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; @@ -34,6 +34,7 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMD.StatusCode; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.util.IOUtil; /** * @@ -135,7 +136,7 @@ public class CoreCliTest { runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile, "--debug"); assertTrue("Report file should have been created", Files.exists(reportFile)); - String reportText = IOUtils.toString(Files.newBufferedReader(reportFile, StandardCharsets.UTF_8)); + String reportText = IOUtil.readToString(Files.newBufferedReader(reportFile, StandardCharsets.UTF_8)); assertThat(reportText, not(containsStringIgnoringCase("error"))); } @@ -204,6 +205,12 @@ public class CoreCliTest { assertThat(errStreamCaptor.getLog(), containsString("[main] INFO net.sourceforge.pmd.PMD - Log level is at INFO")); } + @Test + public void testDeprecatedRulesetSyntaxOnCommandLine() { + runPmd(StatusCode.VIOLATIONS_FOUND, "--no-cache", "--dir", srcDir, "--rulesets", "dummy-basic"); + MatcherAssert.assertThat(errStreamCaptor.getLog(), containsString("Ruleset reference 'dummy-basic' uses a deprecated form, use 'rulesets/dummy/basic.xml' instead")); + } + @Test public void testWrongCliOptionsDoNotPrintUsage() { @@ -229,7 +236,7 @@ public class CoreCliTest { private static void runPmdSuccessfully(Object... args) { - runPmd(0, args); + runPmd(StatusCode.OK, args); } private static String[] argsToString(Object... args) { @@ -254,9 +261,9 @@ public class CoreCliTest { return StandardCharsets.UTF_8.decode(buf).toString(); } - private static void runPmd(int expectedExitCode, Object[] args) { + private static void runPmd(StatusCode expectedExitCode, Object... args) { StatusCode actualExitCode = PMD.runPmd(argsToString(args)); - assertEquals("Exit code", expectedExitCode, actualExitCode.toInt()); + assertEquals("Exit code", expectedExitCode, actualExitCode); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java index 5923717395..897884da7a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.cli; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.Assert; @@ -100,4 +102,15 @@ public class PMDCommandLineInterfaceTest { Assert.assertNotNull(PMDCommandLineInterface.buildUsageText()); } + @Test + public void testOnlyFileListOption() { + PMDParameters params = new PMDParameters(); + String[] args = {"--file-list", "pmd.filelist", "-f", "text", "-R", "rulesets/java/quickstart.xml", "--no-cache", }; + PMDCommandLineInterface.extractParameters(params, args, "PMD"); + + PMDConfiguration config = params.toConfiguration(); + assertEquals("pmd.filelist", config.getInputFilePath()); + assertTrue(config.getAllInputPaths().isEmpty()); // no additional input paths + assertNull(config.getInputPaths()); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java index 22ca614a06..40ff67955e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDParametersTest.java @@ -4,10 +4,17 @@ package net.sourceforge.pmd.cli; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.Assert; import org.junit.Test; +import net.sourceforge.pmd.PMDConfiguration; + public class PMDParametersTest { @Test @@ -20,4 +27,60 @@ public class PMDParametersTest { FieldUtils.writeDeclaredField(parameters, "language", "dummy2", true); Assert.assertEquals("1.0", parameters.getVersion()); } + + @Test + public void testMultipleDirsAndRuleSets() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a", "b", "-R", "x.xml", "y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testMultipleDirsAndRuleSetsWithCommas() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a,b", "-R", "x.xml,y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testMultipleDirsAndRuleSetsWithRepeatedOption() { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters( + "-d", "a", "-d", "b", "-R", "x.xml", "-R", "y.xml" + ); + assertMultipleDirsAndRulesets(result); + } + + @Test + public void testNoPositionalParametersAllowed() { + assertError( + // vvvv + "-R", "x.xml", "-d", "a", "--", "-d", "b" + ); + } + + + private void assertMultipleDirsAndRulesets(PmdParametersParseResult result) { + assertFalse(result.isError()); + PMDConfiguration config = result.toConfiguration(); + assertEquals(config.getAllInputPaths(), listOf("a", "b")); + assertEquals(config.getRuleSetPaths(), listOf("x.xml", "y.xml")); + } + + @Test + public void testEmptyDirOption() { + assertError("-d", "-R", "y.xml"); + } + + @Test + public void testEmptyRulesetOption() { + assertError("-R", "-d", "something"); + } + + private void assertError(String... params) { + PmdParametersParseResult result = PmdParametersParseResult.extractParameters(params); + assertTrue(result.isError()); + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index b611b2ccec..a1e9a4cc09 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -14,20 +14,15 @@ import java.nio.file.Files; import java.util.Arrays; import org.junit.Assert; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.contrib.java.lang.system.SystemErrRule; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; -import org.junit.rules.TestRule; public class CPDCommandLineInterfaceTest { private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/"; - @Rule - public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Rule public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Rule @@ -36,15 +31,11 @@ public class CPDCommandLineInterfaceTest { public TemporaryFolder tempDir = new TemporaryFolder(); - @Before - public void setup() { - System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); - } - @Test public void testEmptyResultRendering() { - CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files", - SRC_DIR, "--format", "xml", }); + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files", + SRC_DIR, "--format", "xml"); + Assert.assertEquals(CPD.StatusCode.OK, statusCode); Assert.assertEquals("" + "\n" + "", log.getLog().trim()); } @@ -55,8 +46,9 @@ public class CPDCommandLineInterfaceTest { new File(SRC_DIR, "dup1.java").getAbsolutePath(), new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8); - CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--filelist", - filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true" }); + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--filelist", + filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true"); + Assert.assertEquals(CPD.StatusCode.OK, statusCode); Assert.assertEquals("" + "\n" + "", log.getLog().trim()); assertTrue(systemErrRule.getLog().contains("Some deprecated options were used on the command-line, including -failOnViolation")); assertTrue(systemErrRule.getLog().contains("Consider replacing it with --fail-on-violation")); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index 5c21740f85..74604cc10a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -7,11 +7,11 @@ package net.sourceforge.pmd.cpd; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.apache.commons.io.FilenameUtils; import org.junit.Test; public class CPDFilelistTest { @@ -28,7 +28,7 @@ public class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(FilenameUtils.getName(path)); + simpleNames.add(Paths.get(path).getFileName().toString()); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); @@ -46,7 +46,7 @@ public class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(FilenameUtils.getName(path)); + simpleNames.add(Paths.get(path).getFileName().toString()); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java index 085475f8d0..af8d5ada20 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java @@ -17,10 +17,11 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; +import net.sourceforge.pmd.util.IOUtil; + public class CpdXsltTest { /* Sample ant build.xml file. Run with "ant cpdxsl". @@ -49,7 +50,7 @@ public class CpdXsltTest { transformer.setErrorListener(errorListener); transformer.transform(cpdReport, result); - String expected = IOUtils.toString(CpdXsltTest.class.getResourceAsStream("ExpectedCpdHtmlReport.html"), StandardCharsets.UTF_8); + String expected = IOUtil.readToString(CpdXsltTest.class.getResourceAsStream("ExpectedCpdHtmlReport.html"), StandardCharsets.UTF_8); Assert.assertEquals(expected, result.getWriter().toString()); Assert.assertTrue("XSLT errors occured: " + errorListener, errorListener.hasNoErrors()); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java index 69768f7ecb..b7d2612554 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java @@ -12,9 +12,10 @@ import java.io.FileReader; import java.io.IOException; import java.io.Reader; -import org.apache.commons.io.IOUtils; import org.junit.Test; +import net.sourceforge.pmd.util.IOUtil; + /** * @author Philippe T'Seyen */ @@ -56,7 +57,7 @@ public class FileReporterTest { private String readFile(File file) throws IOException { try (Reader reader = new FileReader(file)) { - String text = IOUtils.toString(reader); + String text = IOUtil.readToString(reader); return text.replaceAll("\\R", "\n"); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index b3cb7cefa2..ffea3bce29 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -14,7 +14,6 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; -import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileLocation; @@ -23,6 +22,7 @@ import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; +import net.sourceforge.pmd.processor.PmdRunnableTest; /** * Dummy language used for testing PMD. @@ -43,8 +43,11 @@ public class DummyLanguageModule extends BaseLanguageModule { addVersion("1.6", new Handler(), "6"); addDefaultVersion("1.7", new Handler(), "7"); addVersion("1.8", new Handler(), "8"); - addVersion("1.9-throws", new HandlerWithParserThatThrows()); - addVersion("1.9-semantic_error", new HandlerWithParserThatReportsSemanticError()); + PmdRunnableTest.registerCustomVersions(this::addVersion); + } + + public static DummyLanguageModule getInstance() { + return (DummyLanguageModule) LanguageRegistry.getLanguage(NAME); } public static DummyRootNode parse(String code) { @@ -60,11 +63,6 @@ public class DummyLanguageModule extends BaseLanguageModule { return (DummyRootNode) version.getLanguageVersionHandler().getParser().parse(task); } - - public static DummyLanguageModule getInstance() { - return (DummyLanguageModule) LanguageRegistry.getLanguage(NAME); - } - public static class Handler extends AbstractPmdLanguageVersionHandler { @Override @@ -78,28 +76,6 @@ public class DummyLanguageModule extends BaseLanguageModule { } } - public static class HandlerWithParserThatThrows extends Handler { - - @Override - public Parser getParser() { - return task -> { - throw new AssertionError("test error while parsing"); - }; - } - } - - public static class HandlerWithParserThatReportsSemanticError extends Handler { - - @Override - public Parser getParser() { - return task -> { - RootNode root = super.getParser().parse(task); - task.getReporter().error(root, "An error occurred!"); - return root; - }; - } - } - /** * Creates a tree of nodes that corresponds to the nesting structures * of parentheses in the text. The image of each node is also populated. @@ -170,6 +146,5 @@ public class DummyLanguageModule extends BaseLanguageModule { } }; } - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java index bf1931f9e9..9e6c794527 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.impl.AbstractNode; @@ -93,8 +92,7 @@ public class DummyNode extends AbstractNode implements Gen public void setImage(String image) { this.image = image; if (image.startsWith("#")) { - xpathName = image.substring(1); - assert AssertionUtil.isJavaIdentifier(xpathName) : "need an ident after '#': " + image; + xpathName = image; } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporterTest.java index f798a8e066..43fe6d9c49 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/SemanticErrorReporterTest.java @@ -4,12 +4,15 @@ package net.sourceforge.pmd.lang.ast; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.contains; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; @@ -36,24 +39,29 @@ public class SemanticErrorReporterTest { @Before public void setup() { mockReporter = mock(MessageReporter.class); + when(mockReporter.isLoggable(Level.ERROR)).thenReturn(true); mockLogger = spy(NOPLogger.class); } @Test public void testErrorLogging() { - SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(mockReporter, mockLogger); + SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(mockReporter); RootNode node = parseMockNode(reporter); + assertNull(reporter.getFirstError()); + String message = "an error occurred"; reporter.error(node, message); verify(mockReporter).log(eq(Level.ERROR), contains(message)); verifyNoMoreInteractions(mockLogger); + + assertNotNull(reporter.getFirstError()); } @Test public void testEscaping() { - SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(mockReporter, mockLogger); + SemanticErrorReporter reporter = SemanticErrorReporter.reportToLogger(mockReporter); RootNode node = parseMockNode(reporter); // this is a MessageFormat string diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java index a443e93189..f4ae24306a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextDocumentTest.java @@ -10,13 +10,13 @@ import static org.junit.Assert.assertThrows; import java.io.IOException; -import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.util.IOUtil; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -198,7 +198,7 @@ public class TextDocumentTest { assertEquals("NewReader should read the text", doc.getText().toString(), - IOUtils.toString(doc.newReader()) + IOUtil.readToString(doc.newReader()) ); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java index 17cf18bfba..5f2f390b5d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java @@ -18,7 +18,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.Rule; import org.junit.Test; @@ -242,7 +241,7 @@ public class TextFilesTest { private @NonNull Path makeTmpFile(Charset charset, String content) throws IOException { Path file = tempDir.newFile().toPath(); try (BufferedWriter writer = Files.newBufferedWriter(file, charset)) { - IOUtils.write(content, writer); + writer.write(content); } return file; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java index 1d15739995..cd5d1e3c0a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java @@ -5,7 +5,11 @@ package net.sourceforge.pmd.lang.rule.xpath.internal; -import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + import org.junit.Test; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -31,28 +35,72 @@ public class ElementNodeTest { Configuration configuration = Configuration.newConfiguration(); AstTreeInfo treeInfo = new AstTreeInfo(root, configuration); - Assert.assertSame(root, treeInfo.getRootNode().getUnderlyingNode()); - Assert.assertEquals(Type.DOCUMENT, treeInfo.getRootNode().getNodeKind()); + assertSame(root, treeInfo.getRootNode().getUnderlyingNode()); + assertEquals(Type.DOCUMENT, treeInfo.getRootNode().getNodeKind()); AstElementNode rootElt = treeInfo.getRootNode().getRootElement(); - Assert.assertSame(root, rootElt.getUnderlyingNode()); - Assert.assertEquals(Type.ELEMENT, rootElt.getNodeKind()); - Assert.assertSame(rootElt, treeInfo.findWrapperFor(root)); + assertSame(root, rootElt.getUnderlyingNode()); + assertEquals(Type.ELEMENT, rootElt.getNodeKind()); + assertSame(rootElt, treeInfo.findWrapperFor(root)); AstElementNode elementFoo0 = rootElt.getChildren().get(0); - Assert.assertSame(c0, elementFoo0.getUnderlyingNode()); - Assert.assertSame(elementFoo0, treeInfo.findWrapperFor(c0)); + assertSame(c0, elementFoo0.getUnderlyingNode()); + assertSame(elementFoo0, treeInfo.findWrapperFor(c0)); AstElementNode elementFoo1 = rootElt.getChildren().get(1); - Assert.assertSame(c1, elementFoo1.getUnderlyingNode()); - Assert.assertSame(elementFoo1, treeInfo.findWrapperFor(c1)); + assertSame(c1, elementFoo1.getUnderlyingNode()); + assertSame(elementFoo1, treeInfo.findWrapperFor(c1)); - Assert.assertFalse(elementFoo0.isSameNodeInfo(elementFoo1)); - Assert.assertFalse(elementFoo1.isSameNodeInfo(elementFoo0)); - Assert.assertTrue(elementFoo0.compareOrder(elementFoo1) < 0); - Assert.assertTrue(elementFoo1.compareOrder(elementFoo0) > 0); - Assert.assertEquals(0, elementFoo0.compareOrder(elementFoo0)); - Assert.assertEquals(0, elementFoo1.compareOrder(elementFoo1)); + assertFalse(elementFoo0.isSameNodeInfo(elementFoo1)); + assertFalse(elementFoo1.isSameNodeInfo(elementFoo0)); + assertTrue(elementFoo0.compareOrder(elementFoo1) < 0); + assertTrue(elementFoo1.compareOrder(elementFoo0) > 0); + assertEquals(0, elementFoo0.compareOrder(elementFoo0)); + assertEquals(0, elementFoo1.compareOrder(elementFoo1)); } + + @Test + public void verifyTextNodeType() { + DummyRootNode root = DummyLanguageModule.parse("(foo)(#text)"); + + DummyNode c0 = root.getChild(0); + DummyNode c1 = root.getChild(1); + + Configuration configuration = Configuration.newConfiguration(); + AstTreeInfo treeInfo = new AstTreeInfo(root, configuration); + + AstElementNode rootElt = treeInfo.getRootNode().getRootElement(); + assertSame(root, rootElt.getUnderlyingNode()); + assertEquals(Type.ELEMENT, rootElt.getNodeKind()); + assertSame(rootElt, treeInfo.findWrapperFor(root)); + + AstElementNode elementFoo0 = rootElt.getChildren().get(0); + assertEquals(Type.ELEMENT, elementFoo0.getNodeKind()); + assertSame(c0, elementFoo0.getUnderlyingNode()); + assertSame(elementFoo0, treeInfo.findWrapperFor(c0)); + + AstElementNode elementText1 = rootElt.getChildren().get(1); + assertEquals(Type.TEXT, elementText1.getNodeKind()); + assertSame(c1, elementText1.getUnderlyingNode()); + assertSame(elementText1, treeInfo.findWrapperFor(c1)); + } + + @Test + public void verifyCommentNodeType() { + DummyRootNode root = DummyLanguageModule.parse("(#comment)"); + + DummyNode c1 = root.getChild(0); + + Configuration configuration = Configuration.newConfiguration(); + AstTreeInfo treeInfo = new AstTreeInfo(root, configuration); + AstElementNode rootElt = treeInfo.getRootNode().getRootElement(); + + AstElementNode elementComment = rootElt.getChildren().get(0); + assertEquals("#comment", c1.getXPathNodeName()); + assertEquals(Type.COMMENT, elementComment.getNodeKind()); + assertSame(c1, elementComment.getUnderlyingNode()); + assertSame(elementComment, treeInfo.findWrapperFor(c1)); + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java index 5847cd35ec..2108e28680 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/PmdRunnableTest.java @@ -12,9 +12,11 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.List; +import java.util.function.BiConsumer; import org.junit.Assert; import org.junit.Before; @@ -35,10 +37,13 @@ import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.internal.SystemProps; import net.sourceforge.pmd.internal.util.ContextedAssertionError; import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.DummyLanguageModule.Handler; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.processor.MonoThreadProcessor.MonothreadRunnable; @@ -46,12 +51,18 @@ import net.sourceforge.pmd.util.log.MessageReporter; public class PmdRunnableTest { + public static final String TEST_MESSAGE_SEMANTIC_ERROR = "An error occurred!"; + private static final String PARSER_REPORTS_SEMANTIC_ERROR = "1.9-semantic_error"; + private static final String THROWS_SEMANTIC_ERROR = "1.9-throws_semantic_error"; + private static final String THROWS_ASSERTION_ERROR = "1.9-throws"; + + @org.junit.Rule public TestRule restoreSystemProperties = new RestoreSystemProperties(); - private LanguageVersion dummyThrows; - private LanguageVersion dummyDefault; - private LanguageVersion dummySemanticError; + + private final DummyLanguageModule dummyLang = DummyLanguageModule.getInstance(); + private PMDConfiguration configuration; private PmdRunnable pmdRunnable; private MessageReporter reporter; @@ -60,11 +71,6 @@ public class PmdRunnableTest { @Before public void prepare() { - Language dummyLanguage = LanguageRegistry.findLanguageByTerseName(DummyLanguageModule.TERSE_NAME); - dummyDefault = dummyLanguage.getDefaultVersion(); - dummyThrows = dummyLanguage.getVersion("1.9-throws"); - dummySemanticError = dummyLanguage.getVersion("1.9-semantic_error"); - // reset data rule = spy(new RuleThatThrows()); configuration = new PMDConfiguration(); @@ -95,7 +101,7 @@ public class PmdRunnableTest { public void inErrorRecoveryModeErrorsShouldBeLoggedByParser() { System.setProperty(SystemProps.PMD_ERROR_RECOVERY, ""); - Report report = process(dummyThrows); + Report report = process(versionWithParserThatThrowsAssertionError()); Assert.assertEquals(1, report.getProcessingErrors().size()); } @@ -104,7 +110,7 @@ public class PmdRunnableTest { public void inErrorRecoveryModeErrorsShouldBeLoggedByRule() { System.setProperty(SystemProps.PMD_ERROR_RECOVERY, ""); - Report report = process(dummyDefault); + Report report = process(dummyLang.getDefaultVersion()); List errors = report.getProcessingErrors(); assertThat(errors, hasSize(1)); @@ -115,24 +121,55 @@ public class PmdRunnableTest { public void withoutErrorRecoveryModeProcessingShouldBeAbortedByParser() { Assert.assertNull(System.getProperty(SystemProps.PMD_ERROR_RECOVERY)); - Assert.assertThrows(AssertionError.class, () -> process(dummyThrows)); + Assert.assertThrows(AssertionError.class, () -> process(versionWithParserThatThrowsAssertionError())); } @Test public void withoutErrorRecoveryModeProcessingShouldBeAbortedByRule() { Assert.assertNull(System.getProperty(SystemProps.PMD_ERROR_RECOVERY)); - - Assert.assertThrows(AssertionError.class, () -> process(dummyDefault)); + Assert.assertThrows(AssertionError.class, () -> process(dummyLang.getDefaultVersion())); } @Test public void semanticErrorShouldAbortTheRun() { - process(dummySemanticError); + Report report = process(versionWithParserThatReportsSemanticError()); - verify(reporter).log(eq(Level.INFO), contains("skipping rule analysis")); + verify(reporter, times(1)) + .log(eq(Level.ERROR), eq("at !debug only! test.dummy:1:1: " + TEST_MESSAGE_SEMANTIC_ERROR)); verify(rule, never()).apply(Mockito.any(), Mockito.any()); + + Assert.assertEquals(1, report.getProcessingErrors().size()); + } + + @Test + public void semanticErrorThrownShouldAbortTheRun() { + Report report = process(getVersionWithParserThatThrowsSemanticError()); + + verify(reporter, times(1)).log(eq(Level.ERROR), contains(TEST_MESSAGE_SEMANTIC_ERROR)); + verify(rule, never()).apply(Mockito.any(), Mockito.any()); + + Assert.assertEquals(1, report.getProcessingErrors().size()); + } + + public static void registerCustomVersions(BiConsumer addVersion) { + addVersion.accept(THROWS_ASSERTION_ERROR, new HandlerWithParserThatThrows()); + addVersion.accept(PARSER_REPORTS_SEMANTIC_ERROR, new HandlerWithParserThatReportsSemanticError()); + addVersion.accept(THROWS_SEMANTIC_ERROR, new HandlerWithParserThatThrowsSemanticError()); + + } + + public LanguageVersion versionWithParserThatThrowsAssertionError() { + return dummyLang.getVersion(THROWS_ASSERTION_ERROR); + } + + public LanguageVersion getVersionWithParserThatThrowsSemanticError() { + return dummyLang.getVersion(THROWS_SEMANTIC_ERROR); + } + + public LanguageVersion versionWithParserThatReportsSemanticError() { + return dummyLang.getVersion(PARSER_REPORTS_SEMANTIC_ERROR); } private static class RuleThatThrows extends AbstractRule { @@ -147,4 +184,37 @@ public class PmdRunnableTest { throw new AssertionError("test"); } } + + public static class HandlerWithParserThatThrowsSemanticError extends Handler { + + @Override + public Parser getParser() { + return task -> { + RootNode root = super.getParser().parse(task); + throw task.getReporter().error(root, TEST_MESSAGE_SEMANTIC_ERROR); + }; + } + } + + public static class HandlerWithParserThatThrows extends Handler { + + @Override + public Parser getParser() { + return task -> { + throw new AssertionError("test error while parsing"); + }; + } + } + + public static class HandlerWithParserThatReportsSemanticError extends Handler { + + @Override + public Parser getParser() { + return task -> { + RootNode root = super.getParser().parse(task); + task.getReporter().error(root, TEST_MESSAGE_SEMANTIC_ERROR); + return root; + }; + } + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index a61b2c0066..8cd1a0b9dc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -13,8 +13,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -34,6 +32,7 @@ import net.sourceforge.pmd.lang.document.TextRange2d; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; public abstract class AbstractRendererTest { @@ -126,7 +125,7 @@ public abstract class AbstractRendererTest { */ protected String readFile(String relativePath) { try (InputStream in = getClass().getResourceAsStream(relativePath)) { - return IOUtils.toString(in, StandardCharsets.UTF_8); + return IOUtil.readToString(in, StandardCharsets.UTF_8); } catch (IOException e) { throw new RuntimeException(e); } @@ -222,7 +221,7 @@ public abstract class AbstractRendererTest { throw new AssertionError(e); } - return FileUtils.readFileToString(file, expectedEncoding); + return IOUtil.readFileToString(file, expectedEncoding); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java index 9faefb055f..858826c5a9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.reporting.FileAnalysisListener; - public class SarifRendererTest extends AbstractRendererTest { @org.junit.Rule diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java index de887c378b..f161902268 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java @@ -12,7 +12,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import javax.xml.parsers.DocumentBuilderFactory; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -32,6 +31,7 @@ import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextRange2d; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; +import net.sourceforge.pmd.util.IOUtil; public class XMLRendererTest extends AbstractRendererTest { @@ -169,7 +169,7 @@ public class XMLRendererTest extends AbstractRendererTest { renderer.flush(); try (FileInputStream input = new FileInputStream(reportFile)) { - return IOUtils.toString(input, expectedCharset); + return IOUtil.readToString(input, expectedCharset); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java index a1806aba4f..83479fc12d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java @@ -14,7 +14,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -27,6 +26,7 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; +import net.sourceforge.pmd.util.IOUtil; public class YAHTMLRendererTest extends AbstractRendererTest { @@ -74,8 +74,8 @@ public class YAHTMLRendererTest extends AbstractRendererTest { for (String file : htmlFiles) { try (FileInputStream in = new FileInputStream(new File(outputDir, file)); InputStream expectedIn = YAHTMLRendererTest.class.getResourceAsStream("yahtml/" + file)) { - String data = IOUtils.toString(in, StandardCharsets.UTF_8); - String expected = normalizeLineSeparators(IOUtils.toString(expectedIn, StandardCharsets.UTF_8)); + String data = IOUtil.readToString(in, StandardCharsets.UTF_8); + String expected = normalizeLineSeparators(IOUtil.readToString(expectedIn, StandardCharsets.UTF_8)); assertEquals("File " + file + " is different", expected, data); } @@ -83,8 +83,8 @@ public class YAHTMLRendererTest extends AbstractRendererTest { } private static String normalizeLineSeparators(String s) { - return s.replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_WINDOWS), IOUtils.LINE_SEPARATOR_UNIX) - .replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_UNIX), PMD.EOL); + return s.replaceAll(Pattern.quote("\r\n"), "\n") + .replaceAll(Pattern.quote("\n"), PMD.EOL); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java new file mode 100644 index 0000000000..b577de8f11 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java @@ -0,0 +1,299 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +public class IOUtilTest { + + @Test + public void testReadAllBytes() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(5, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testToByteArrayResize() throws IOException { + int size = 8192 + 8192 + 10; + byte[] data = new byte[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (InputStream stream = new ByteArrayInputStream(data)) { + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(size, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testSkipFully() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + Assert.assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + IOUtil.skipFully(stream, -1); + } + }); + + Assert.assertEquals(3, IOUtil.skipFully(stream, 3)); + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(2, bytes.length); + Assert.assertArrayEquals("45".getBytes(StandardCharsets.UTF_8), bytes); + } + } + + @Test + public void testSkipFully2() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + // skip more bytes than the stream contains + Assert.assertEquals(data.length, IOUtil.skipFully(stream, data.length + 1)); + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(0, bytes.length); + } + } + + @Test + public void testNormalizePath() { + if (SystemUtils.IS_OS_UNIX) { + Assert.assertEquals("ab/cd.txt", IOUtil.normalizePath("ab/ef/../cd.txt")); + Assert.assertEquals("/a.txt", IOUtil.normalizePath("/x/../../a.txt")); + Assert.assertEquals("/foo", IOUtil.normalizePath("//../foo")); + Assert.assertEquals("/foo", IOUtil.normalizePath("/foo//")); + Assert.assertEquals("/foo", IOUtil.normalizePath("/foo/./")); + Assert.assertEquals("/bar", IOUtil.normalizePath("/foo/../bar")); + Assert.assertEquals("/bar", IOUtil.normalizePath("/foo/../bar/")); + Assert.assertEquals("/baz", IOUtil.normalizePath("/foo/../bar/../baz")); + Assert.assertEquals("/foo/bar", IOUtil.normalizePath("//foo//./bar")); + Assert.assertEquals("foo", IOUtil.normalizePath("foo/bar/..")); + Assert.assertEquals("bar", IOUtil.normalizePath("foo/../bar")); + Assert.assertEquals("/foo/baz", IOUtil.normalizePath("//foo/bar/../baz")); + Assert.assertEquals("~/bar", IOUtil.normalizePath("~/foo/../bar/")); + Assert.assertEquals("/", IOUtil.normalizePath("/../")); + Assert.assertEquals("bar", IOUtil.normalizePath("~/../bar")); + Assert.assertEquals("bar", IOUtil.normalizePath("./bar")); + + Assert.assertNull(IOUtil.normalizePath("../foo")); + Assert.assertNull(IOUtil.normalizePath("foo/../../bar")); + Assert.assertNull(IOUtil.normalizePath(".")); + + Assert.assertTrue(IOUtil.equalsNormalizedPaths("foo/../bar", "bar/./")); + } + + if (SystemUtils.IS_OS_WINDOWS) { + Assert.assertEquals("ab\\cd.txt", IOUtil.normalizePath("ab\\ef\\..\\cd.txt")); + Assert.assertEquals("\\a.txt", IOUtil.normalizePath("\\x\\..\\..\\a.txt")); + Assert.assertEquals("\\foo", IOUtil.normalizePath("\\foo\\\\")); + Assert.assertEquals("\\foo", IOUtil.normalizePath("\\foo\\.\\")); + Assert.assertEquals("\\bar", IOUtil.normalizePath("\\foo\\..\\bar")); + Assert.assertEquals("\\bar", IOUtil.normalizePath("\\foo\\..\\bar\\")); + Assert.assertEquals("\\baz", IOUtil.normalizePath("\\foo\\..\\bar\\..\\baz")); + Assert.assertEquals("\\\\foo\\bar\\", IOUtil.normalizePath("\\\\foo\\bar")); + Assert.assertEquals("\\\\foo\\bar\\baz", IOUtil.normalizePath("\\\\foo\\bar\\..\\baz")); + Assert.assertEquals("foo", IOUtil.normalizePath("foo\\bar\\..")); + Assert.assertEquals("bar", IOUtil.normalizePath("foo\\..\\bar")); + Assert.assertEquals("\\foo\\baz", IOUtil.normalizePath("\\foo\\bar\\..\\baz")); + Assert.assertEquals("\\", IOUtil.normalizePath("\\..\\")); + Assert.assertEquals("bar", IOUtil.normalizePath(".\\bar")); + + Assert.assertNull(IOUtil.normalizePath("\\\\..\\foo")); + Assert.assertNull(IOUtil.normalizePath("..\\foo")); + Assert.assertNull(IOUtil.normalizePath("foo\\..\\..\\bar")); + Assert.assertNull(IOUtil.normalizePath(".")); + Assert.assertNull(IOUtil.normalizePath("\\\\foo\\\\.\\bar")); + Assert.assertNull(IOUtil.normalizePath("\\\\foo\\.\\bar")); + + Assert.assertTrue(IOUtil.equalsNormalizedPaths("foo\\..\\bar", "bar\\.\\")); + + Assert.assertEquals("C:\\bar", IOUtil.normalizePath("C:\\..\\bar")); + Assert.assertEquals("ab\\cd.txt", IOUtil.normalizePath("ab\\ef\\..\\cd.txt")); + Assert.assertEquals("C:\\ab\\cd.txt", IOUtil.normalizePath("C:\\ab\\ef\\..\\.\\cd.txt")); + Assert.assertNull(IOUtil.normalizePath("..\\foo")); + Assert.assertNull(IOUtil.normalizePath("foo\\..\\..\\bar")); + } + } + + @Test + public void testFilenameExtension() { + Assert.assertEquals("txt", IOUtil.getFilenameExtension("ab/cd.txt")); + Assert.assertEquals("txt", IOUtil.getFilenameExtension("ab.cd.txt")); + Assert.assertEquals("", IOUtil.getFilenameExtension("ab/cd")); + Assert.assertEquals("html", IOUtil.getFilenameExtension("cd.html")); + } + + @Test + public void testFilenameBase() { + Assert.assertEquals("cd", IOUtil.getFilenameBase("ab/cd.txt")); + Assert.assertEquals("ab.cd", IOUtil.getFilenameBase("ab.cd.txt")); + Assert.assertEquals("cd", IOUtil.getFilenameBase("ab/cd")); + } + + @Test + public void testBomAwareStream() throws IOException { + assertBomStream("No BOM".getBytes(StandardCharsets.UTF_8), "No BOM", null); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_8), "BOM", StandardCharsets.UTF_8.name()); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_16LE), "BOM", StandardCharsets.UTF_16LE.name()); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_16BE), "BOM", StandardCharsets.UTF_16BE.name()); + } + + private void assertBomStream(byte[] data, String expectedData, String expectedCharset) throws IOException { + try (IOUtil.BomAwareInputStream stream = new IOUtil.BomAwareInputStream(new ByteArrayInputStream(data))) { + if (expectedCharset != null) { + Assert.assertTrue(stream.hasBom()); + Assert.assertEquals(expectedCharset, stream.getBomCharsetName()); + Assert.assertEquals(expectedData, new String(IOUtil.toByteArray(stream), stream.getBomCharsetName())); + + } else { + Assert.assertFalse(stream.hasBom()); + Assert.assertNull(stream.getBomCharsetName()); + Assert.assertEquals(expectedData, new String(IOUtil.toByteArray(stream), StandardCharsets.UTF_8)); + } + } + } + + @Test + public void testOutputStreamFromWriter() throws IOException { + StringWriter writer = new StringWriter(); + try (OutputStream outputStream = IOUtil.fromWriter(writer, "UTF-8")) { + outputStream.write("abc".getBytes(StandardCharsets.UTF_8)); + } + Assert.assertEquals("abc", writer.toString()); + } + + @Test + public void testInputStreamFromReader() throws IOException { + try (InputStream inputStream = IOUtil.fromReader(new StringReader("abc"))) { + byte[] bytes = IOUtil.toByteArray(inputStream); + Assert.assertEquals("abc", new String(bytes, StandardCharsets.UTF_8)); + } + } + + @Test + public void testInputStreamFromReader2() throws IOException { + int size = 8192 + 8192 + 10; + char[] data = new char[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + data[8192] = 'ä'; // block size border - in UTF-8 these are two bytes. Decoding needs to take the bytes + // from previous block and new block + try (InputStream inputStream = IOUtil.fromReader(new StringReader(new String(data)))) { + byte[] bytes = IOUtil.toByteArray(inputStream); + Assert.assertEquals(new String(data), new String(bytes, StandardCharsets.UTF_8)); + } + } + + @Test + public void testCopyStream() throws IOException { + int size = 8192 + 8192 + 10; + byte[] data = new byte[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (InputStream stream = new ByteArrayInputStream(data); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + IOUtil.copy(stream, out); + byte[] bytes = out.toByteArray(); + Assert.assertEquals(size, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testCopyReader() throws IOException { + int size = 8192 + 8192 + 10; + char[] data = new char[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (Reader reader = new CharArrayReader(data); + StringWriter writer = new StringWriter()) { + IOUtil.copy(reader, writer); + char[] chars = writer.toString().toCharArray(); + Assert.assertEquals(size, chars.length); + Assert.assertArrayEquals(data, chars); + } + } + + @Test + public void testReadEmptyStream() throws IOException { + try (InputStream in = new ByteArrayInputStream(new byte[0])) { + byte[] bytes = IOUtil.toByteArray(in); + Assert.assertNotNull(bytes); + Assert.assertEquals(0, bytes.length); + } + } + + @Test + public void testCloseQuietly() { + class Stream extends InputStream { + private boolean closed = false; + + @Override + public int read() throws IOException { + return 0; + } + + @Override + public void close() throws IOException { + closed = true; + throw new IOException("test"); + } + + public boolean isClosed() { + return closed; + } + } + + Stream stream = new Stream(); + IOUtil.closeQuietly(stream); + Assert.assertTrue(stream.isClosed()); + } + + @Test + public void testReadFileToString() throws IOException { + String testString = "Test ABC"; + Path tempFile = Files.createTempFile("pmd", ".txt"); + Files.write(tempFile, testString.getBytes(Charset.defaultCharset())); + Assert.assertEquals(testString, IOUtil.readFileToString(tempFile.toFile())); + } + + @Test + public void testReadToString() throws IOException { + String testString = "testReadToString"; + Reader reader = new StringReader(testString); + Assert.assertEquals(testString, IOUtil.readToString(reader)); + } + + @Test + public void testReadStreamToString() throws IOException { + String testString = "testReadStreamToString"; + InputStream stream = new ByteArrayInputStream(testString.getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(testString, IOUtil.readToString(stream, StandardCharsets.UTF_8)); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java index 2282641b40..7b3aa6e48e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.util.treeexport; import static org.hamcrest.Matchers.containsString; import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -15,7 +16,6 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import org.apache.commons.io.IOUtils; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; import org.junit.Assert; @@ -67,7 +67,7 @@ public class TreeExportCliTest { } private static InputStream stdinContaining(String input) { - return IOUtils.toInputStream(input, StandardCharsets.UTF_8); + return new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); } static class IoSpy { diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 5318fb16d3..06093bc6d9 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 35e9ba91c2..d003846560 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index 84b62fe8e1..64822f1a38 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -22,6 +22,7 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; private boolean ignoreLiteralSequences = false; + private boolean ignoreAttributes = false; /** * Sets the possible options for the C# tokenizer. @@ -29,19 +30,16 @@ public class CsTokenizer extends AntlrTokenizer { * @param properties the properties * @see #IGNORE_USINGS * @see #OPTION_IGNORE_LITERAL_SEQUENCES + * @see #IGNORE_ANNOTATIONS */ public void setProperties(Properties properties) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, Boolean.FALSE.toString())); - ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, - Boolean.FALSE.toString())); + ignoreUsings = getBooleanProperty(properties, IGNORE_USINGS); + ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES); + ignoreAttributes = getBooleanProperty(properties, IGNORE_ANNOTATIONS); } - public void setIgnoreUsings(boolean ignoreUsings) { - this.ignoreUsings = ignoreUsings; - } - - public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { - this.ignoreLiteralSequences = ignoreLiteralSequences; + private boolean getBooleanProperty(final Properties properties, final String property) { + return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString())); } @Override @@ -51,7 +49,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences); + return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences, ignoreAttributes); } /** @@ -70,15 +68,18 @@ public class CsTokenizer extends AntlrTokenizer { private final boolean ignoreUsings; private final boolean ignoreLiteralSequences; + private final boolean ignoreAttributes; private boolean discardingUsings = false; private boolean discardingNL = false; + private boolean isDiscardingAttribute = false; private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { + CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { super(tokenManager); this.ignoreUsings = ignoreUsings; this.ignoreLiteralSequences = ignoreLiteralSequences; + this.ignoreAttributes = ignoreAttributes; } @Override @@ -91,6 +92,7 @@ public class CsTokenizer extends AntlrTokenizer { discardCurrent = false; skipUsingDirectives(currentToken, remainingTokens); skipLiteralSequences(currentToken, remainingTokens); + skipAttributes(currentToken); } private void skipUsingDirectives(final AntlrToken currentToken, final Iterable remainingTokens) { @@ -167,6 +169,25 @@ public class CsTokenizer extends AntlrTokenizer { discardingNL = currentToken.getKind() == CSharpLexer.NL; } + private void skipAttributes(final AntlrToken currentToken) { + if (ignoreAttributes) { + switch (currentToken.getKind()) { + case CSharpLexer.OPEN_BRACKET: + // Start of an attribute. + isDiscardingAttribute = true; + break; + case CSharpLexer.CLOSE_BRACKET: + // End of an attribute. + isDiscardingAttribute = false; + discardCurrent = true; + break; + default: + // Skip any other token. + break; + } + } + } + private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int type = currentToken.getKind(); @@ -222,7 +243,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent; + return discardingUsings || discardingNL || isDiscardingAttribute || isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 63c9cd5aee..d05797d726 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -105,18 +105,33 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("csharp7And8Additions"); } + @Test + public void testAttributesAreNotIgnored() { + doTest("attributes"); + } + + @Test + public void testAttributesAreIgnored() { + doTest("attributes", "_ignored", skipAttributes()); + } + private Properties ignoreUsings() { - return properties(true, false); + return properties(true, false, false); } private Properties skipLiteralSequences() { - return properties(false, true); + return properties(false, true, false); } - private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) { + private Properties skipAttributes() { + return properties(false, false, true); + } + + private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { Properties properties = new Properties(); properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); + properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAttributes)); return properties; } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs new file mode 100644 index 0000000000..811e151362 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs @@ -0,0 +1,42 @@ +[Serializable] +public class SampleClass +{ + // Objects of this type can be serialized. +} + +[System.Runtime.InteropServices.DllImport("user32.dll")] +extern static void SampleMethod(); + +void MethodA([In][Out] ref double x) { } +void MethodB([Out][In] ref double x) { } +void MethodC([In, Out] ref double x) { } + +[Conditional("DEBUG"), Conditional("TEST1")] +void TraceMethod() +{ + // ... +} + +[DllImport("user32.dll")] +[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)] +[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)] + +using System; +using System.Reflection; +[assembly: AssemblyTitleAttribute("Production assembly 4")] +[module: CLSCompliant(true)] + +// default: applies to method +[ValidatedContract] +int Method1() { return 0; } + +// applies to method +[method: ValidatedContract] +int Method2() { return 0; } + +// applies to parameter +int Method3([ValidatedContract] string contract) { return 0; } + +// applies to return value +[return: ValidatedContract] +int Method4() { return 0; } \ No newline at end of file diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt new file mode 100644 index 0000000000..ec85e49500 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt @@ -0,0 +1,229 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [\[] 1 2 + [Serializable] 2 14 + [\]] 14 15 +L2 + [public] 1 7 + [class] 8 13 + [SampleClass] 14 25 +L3 + [{] 1 2 +L5 + [}] 1 2 +L7 + [\[] 1 2 + [System] 2 8 + [.] 8 9 + [Runtime] 9 16 + [.] 16 17 + [InteropServices] 17 32 + [.] 32 33 + [DllImport] 33 42 + [(] 42 43 + ["user32.dll"] 43 55 + [)] 55 56 + [\]] 56 57 +L8 + [extern] 1 7 + [static] 8 14 + [void] 15 19 + [SampleMethod] 20 32 + [(] 32 33 + [)] 33 34 + [;] 34 35 +L10 + [void] 1 5 + [MethodA] 6 13 + [(] 13 14 + [\[] 14 15 + [In] 15 17 + [\]] 17 18 + [\[] 18 19 + [Out] 19 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L11 + [void] 1 5 + [MethodB] 6 13 + [(] 13 14 + [\[] 14 15 + [Out] 15 18 + [\]] 18 19 + [\[] 19 20 + [In] 20 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L12 + [void] 1 5 + [MethodC] 6 13 + [(] 13 14 + [\[] 14 15 + [In] 15 17 + [,] 17 18 + [Out] 19 22 + [\]] 22 23 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L14 + [\[] 1 2 + [Conditional] 2 13 + [(] 13 14 + ["DEBUG"] 14 21 + [)] 21 22 + [,] 22 23 + [Conditional] 24 35 + [(] 35 36 + ["TEST1"] 36 43 + [)] 43 44 + [\]] 44 45 +L15 + [void] 1 5 + [TraceMethod] 6 17 + [(] 17 18 + [)] 18 19 +L16 + [{] 1 2 +L18 + [}] 1 2 +L20 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [)] 24 25 + [\]] 25 26 +L21 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [,] 24 25 + [SetLastError] 26 38 + [=] 38 39 + [false] 39 44 + [,] 44 45 + [ExactSpelling] 46 59 + [=] 59 60 + [false] 60 65 + [)] 65 66 + [\]] 66 67 +L22 + [\[] 1 2 + [DllImport] 2 11 + [(] 11 12 + ["user32.dll"] 12 24 + [,] 24 25 + [ExactSpelling] 26 39 + [=] 39 40 + [false] 40 45 + [,] 45 46 + [SetLastError] 47 59 + [=] 59 60 + [false] 60 65 + [)] 65 66 + [\]] 66 67 +L24 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Reflection] 14 24 + [;] 24 25 +L26 + [\[] 1 2 + [assembly] 2 10 + [:] 10 11 + [AssemblyTitleAttribute] 12 34 + [(] 34 35 + ["Production assembly 4"] 35 58 + [)] 58 59 + [\]] 59 60 +L27 + [\[] 1 2 + [module] 2 8 + [:] 8 9 + [CLSCompliant] 10 22 + [(] 22 23 + [true] 23 27 + [)] 27 28 + [\]] 28 29 +L30 + [\[] 1 2 + [ValidatedContract] 2 19 + [\]] 19 20 +L31 + [int] 1 4 + [Method1] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L34 + [\[] 1 2 + [method] 2 8 + [:] 8 9 + [ValidatedContract] 10 27 + [\]] 27 28 +L35 + [int] 1 4 + [Method2] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L38 + [int] 1 4 + [Method3] 5 12 + [(] 12 13 + [\[] 13 14 + [ValidatedContract] 14 31 + [\]] 31 32 + [string] 33 39 + [contract] 40 48 + [)] 48 49 + [{] 50 51 + [return] 52 58 + [0] 59 60 + [;] 60 61 + [}] 62 63 +L41 + [\[] 1 2 + [return] 2 8 + [:] 8 9 + [ValidatedContract] 10 27 + [\]] 27 28 +L42 + [int] 1 4 + [Method4] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt new file mode 100644 index 0000000000..c2de96f13f --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt @@ -0,0 +1,109 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [public] 1 7 + [class] 8 13 + [SampleClass] 14 25 +L3 + [{] 1 2 +L5 + [}] 1 2 +L8 + [extern] 1 7 + [static] 8 14 + [void] 15 19 + [SampleMethod] 20 32 + [(] 32 33 + [)] 33 34 + [;] 34 35 +L10 + [void] 1 5 + [MethodA] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L11 + [void] 1 5 + [MethodB] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L12 + [void] 1 5 + [MethodC] 6 13 + [(] 13 14 + [ref] 24 27 + [double] 28 34 + [x] 35 36 + [)] 36 37 + [{] 38 39 + [}] 40 41 +L15 + [void] 1 5 + [TraceMethod] 6 17 + [(] 17 18 + [)] 18 19 +L16 + [{] 1 2 +L18 + [}] 1 2 +L24 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Reflection] 14 24 + [;] 24 25 +L31 + [int] 1 4 + [Method1] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L35 + [int] 1 4 + [Method2] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +L38 + [int] 1 4 + [Method3] 5 12 + [(] 12 13 + [string] 33 39 + [contract] 40 48 + [)] 48 49 + [{] 50 51 + [return] 52 58 + [0] 59 60 + [;] 60 61 + [}] 62 63 +L42 + [int] 1 4 + [Method4] 5 12 + [(] 12 13 + [)] 13 14 + [{] 15 16 + [return] 17 23 + [0] 24 25 + [;] 25 26 + [}] 27 28 +EOF diff --git a/pmd-dart/pom.xml b/pmd-dart/pom.xml index 3a75d27a4a..0c8af18fe3 100644 --- a/pmd-dart/pom.xml +++ b/pmd-dart/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index e8d3b680af..39efc28454 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -9,7 +9,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -271,10 +271,6 @@ slf4j-simple
- - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java index 6d11b7d9d7..2c840d2487 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java @@ -8,15 +8,19 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; import org.junit.Assume; import org.junit.Test; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.util.IOUtil; /** * This test calls ant in a fake terminal to make sure we have a {@link java.io.Console} connected. @@ -41,10 +45,28 @@ public class AntIT extends AbstractBinaryDistributionTest { private File prepareAntTestProjectFolder() throws IOException { - File sourceProjectFolder = new File("src/test/resources/ant-it"); - File projectFolder = folder.newFolder(); - FileUtils.copyDirectory(sourceProjectFolder, projectFolder); - return projectFolder; + final Path sourceProjectFolder = new File("src/test/resources/ant-it").toPath(); + final Path projectFolder = folder.newFolder().toPath(); + Files.walkFileTree(sourceProjectFolder, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + assert !dir.isAbsolute(); + Path target = projectFolder.resolve(sourceProjectFolder.relativize(dir)); + if (!target.toFile().exists()) { + target.toFile().mkdir(); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + assert !file.isAbsolute(); + Path target = projectFolder.resolve(sourceProjectFolder.relativize(file)); + Files.copy(file, target); + return FileVisitResult.CONTINUE; + } + }); + return projectFolder.toFile(); } @@ -64,7 +86,7 @@ public class AntIT extends AbstractBinaryDistributionTest { @Override public void run() { try (InputStream in = process.getInputStream()) { - String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + String output = IOUtil.readToString(process.getInputStream(), StandardCharsets.UTF_8); result.withOutput(output); } catch (IOException e) { result.withOutput("Exception occurred: " + e.toString()); diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 1de270c3f1..02650b99c7 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -27,9 +27,11 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { static { SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, html, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, vf, xml]"; - SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, modelica, plsql, pom, scala, swift, vf, vm, wsdl, xml, xsl"; + SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, kotlin, modelica, plsql, pom, scala, swift, vf, vm, wsdl, xml, xsl"; } + private final String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); + @Test public void testFileExistence() { assertTrue(getBinaryDistribution().exists()); @@ -69,27 +71,34 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { } @Test - public void runPMD() throws Exception { - String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath(); + public void testPmdJavaQuickstart() throws Exception { + ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "rulesets/java/quickstart.xml"); + result.assertExecutionResult(4, ""); + } - ExecutionResult result; - - result = PMDExecutor.runPMD(tempDir); // without any argument, display usage help and error - result.assertExecutionResultErrOutput(1, CliMessages.runWithHelpFlagMessage()); - - result = PMDExecutor.runPMD(tempDir, "-h"); - result.assertExecutionResult(0, SUPPORTED_LANGUAGES_PMD); - - result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); - result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:"); - - // also test XML format - result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml"); + @Test + public void testPmdXmlFormat() throws Exception { + ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml"); result.assertExecutionResult(4, "", "JumbledIncrementer.java\">"); result.assertExecutionResult(4, "", "net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -83,10 +83,6 @@ pmd-core ${project.version} - - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java index 3a7ee37eb4..499b7b67e3 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java @@ -36,10 +36,10 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.util.IOUtil; /** * Checks links to local pages for non-existing link-targets. @@ -299,7 +299,7 @@ public class DeadLinksChecker { private String fileToString(Path mdFile) { try (InputStream inputStream = Files.newInputStream(mdFile)) { - return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + return IOUtil.readToString(inputStream, StandardCharsets.UTF_8); } catch (IOException ex) { throw new RuntimeException("error reading " + mdFile, ex); } diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index ddec28206a..2b3b19c424 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -10,16 +10,16 @@ import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.io.FilenameUtils; - import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.util.IOUtil; public final class GenerateRuleDocsCmd { @@ -53,8 +53,8 @@ public final class GenerateRuleDocsCmd { try { List additionalRulesets = new ArrayList<>(); Pattern rulesetPattern = Pattern.compile("^.+" + Pattern.quote(File.separator) + "pmd-\\w+" - + Pattern.quote(FilenameUtils.normalize("/src/main/resources/rulesets/")) - + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); + + Pattern.quote(IOUtil.normalizePath(File.separator + Paths.get("src", "main", "resources", "rulesets").toString() + File.separator)) + + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); Files.walkFileTree(basePath, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 66951fa188..924f79fe18 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -28,7 +28,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; @@ -43,6 +42,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.util.IOUtil; public class RuleDocGenerator { private static final Logger LOG = LoggerFactory.getLogger(RuleDocGenerator.class); @@ -131,7 +131,7 @@ public class RuleDocGenerator { } private Path getAbsoluteOutputPath(String filename) { - return root.resolve(FilenameUtils.normalize(filename)); + return root.resolve(IOUtil.normalizePath(filename)); } private Map> sortRulesets(List rulesets) { @@ -608,17 +608,13 @@ public class RuleDocGenerator { // is replaced by a correct path. for (List rulesets : sortedRulesets.values()) { for (RuleSet ruleset : rulesets) { - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - String rulesetFilename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()), true); + String rulesetFilename = RuleSetUtils.normalizeForwardSlashes(StringUtils.chomp(ruleset.getFileName())); allRulesets.put(ruleset.getFileName(), rulesetFilename); for (Rule rule : ruleset.getRules()) { String ruleClass = rule.getRuleClass(); String relativeSourceFilename = ruleClass.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + ".java"; - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRules.put(ruleClass, FilenameUtils.normalize(relativeSourceFilename, true)); + allRules.put(ruleClass, RuleSetUtils.normalizeForwardSlashes(relativeSourceFilename)); } } } @@ -640,9 +636,7 @@ public class RuleDocGenerator { } if (foundRuleClass != null) { Path foundPath = root.relativize(file); - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRules.put(foundRuleClass, FilenameUtils.normalize(foundPath.toString(), true)); + allRules.put(foundRuleClass, RuleSetUtils.normalizeForwardSlashes(foundPath.toString())); } String foundRuleset = null; @@ -654,9 +648,7 @@ public class RuleDocGenerator { } if (foundRuleset != null) { Path foundPath = root.relativize(file); - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRulesets.put(foundRuleset, FilenameUtils.normalize(foundPath.toString(), true)); + allRulesets.put(foundRuleset, RuleSetUtils.normalizeForwardSlashes(foundPath.toString())); } } return FileVisitResult.CONTINUE; diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java index f928fe1de6..0ab05a68bc 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java @@ -4,12 +4,16 @@ package net.sourceforge.pmd.docs; -import org.apache.commons.io.FilenameUtils; +import java.io.File; +import java.util.regex.Pattern; + import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.util.IOUtil; public final class RuleSetUtils { @@ -28,7 +32,7 @@ public final class RuleSetUtils { } public static String getRuleSetFilename(String rulesetFileName) { - return FilenameUtils.getBaseName(StringUtils.chomp(rulesetFileName)); + return IOUtil.getFilenameBase(StringUtils.chomp(rulesetFileName)); } /** @@ -51,7 +55,7 @@ public final class RuleSetUtils { public static String getRuleSetClasspath(RuleSet ruleset) { final String RESOURCES_PATH = "/resources/"; - String filename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()), true); + String filename = normalizeForwardSlashes(StringUtils.chomp(ruleset.getFileName())); int startIndex = filename.lastIndexOf(RESOURCES_PATH); if (startIndex > -1) { return filename.substring(startIndex + RESOURCES_PATH.length()); @@ -60,6 +64,16 @@ public final class RuleSetUtils { } } + public static String normalizeForwardSlashes(String path) { + String normalized = IOUtil.normalizePath(path); + if (SystemUtils.IS_OS_WINDOWS) { + // Note: windows path separators are changed to forward slashes, + // so that the editme link works + normalized = normalized.replaceAll(Pattern.quote(File.separator), "/"); + } + return normalized; + } + /** * Recursively resolves rule references until the last reference. * The last reference is returned. diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java index dc5ee7e626..3f90cbe4cb 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.PMD; @@ -49,7 +48,7 @@ public class MockedFileWriter implements FileWriter { } public static String normalizeLineSeparators(String s) { - return s.replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_WINDOWS), IOUtils.LINE_SEPARATOR_UNIX) - .replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_UNIX), PMD.EOL); + return s.replaceAll(Pattern.quote("\r\n"), "\n") + .replaceAll(Pattern.quote("\n"), PMD.EOL); } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java index dc96ec33a5..f994834900 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -11,11 +11,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,6 +23,7 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; +import net.sourceforge.pmd.util.IOUtil; public class RuleDocGeneratorTest { @@ -52,7 +52,7 @@ public class RuleDocGeneratorTest { private static String loadResource(String name) throws IOException { return MockedFileWriter.normalizeLineSeparators( - IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream(name), StandardCharsets.UTF_8)); + IOUtil.readToString(RuleDocGeneratorTest.class.getResourceAsStream(name), StandardCharsets.UTF_8)); } @Test @@ -69,15 +69,15 @@ public class RuleDocGeneratorTest { assertEquals(3, writer.getData().size()); FileEntry languageIndex = writer.getData().get(0); - assertTrue(FilenameUtils.normalize(languageIndex.getFilename(), true).endsWith("docs/pages/pmd/rules/java.md")); + assertTrue(IOUtil.normalizePath(languageIndex.getFilename()).endsWith(Paths.get("docs", "pages", "pmd", "rules", "java.md").toString())); assertEquals(loadResource("/expected/java.md"), languageIndex.getContent()); FileEntry ruleSetIndex = writer.getData().get(1); - assertTrue(FilenameUtils.normalize(ruleSetIndex.getFilename(), true).endsWith("docs/pages/pmd/rules/java/sample.md")); + assertTrue(IOUtil.normalizePath(ruleSetIndex.getFilename()).endsWith(Paths.get("docs", "pages", "pmd", "rules", "java", "sample.md").toString())); assertEquals(loadResource("/expected/sample.md"), ruleSetIndex.getContent()); FileEntry sidebar = writer.getData().get(2); - assertTrue(FilenameUtils.normalize(sidebar.getFilename(), true).endsWith("docs/_data/sidebars/pmd_sidebar.yml")); + assertTrue(IOUtil.normalizePath(sidebar.getFilename()).endsWith(Paths.get("docs", "_data", "sidebars", "pmd_sidebar.yml").toString())); assertEquals(loadResource("/expected/pmd_sidebar.yml"), sidebar.getContent()); } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index 8a56f9077e..d603abccf0 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -10,15 +10,15 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.List; -import org.apache.commons.io.FilenameUtils; import org.junit.Test; import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.util.IOUtil; public class RuleSetResolverTest { private static final List EXCLUDED_RULESETS = listOf( - FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml") + IOUtil.normalizePath("pmd-test/src/main/resources/rulesets/dummy/basic.xml") ); @Test diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java index 97e562e49b..0ae7e05323 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; import org.junit.Before; import org.junit.Test; @@ -27,6 +26,7 @@ import org.yaml.snakeyaml.Yaml; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.IOUtil; public class SidebarGeneratorTest { private MockedFileWriter writer = new MockedFileWriter(); @@ -55,7 +55,7 @@ public class SidebarGeneratorTest { String yaml = new Yaml(options).dump(result); String expected = MockedFileWriter.normalizeLineSeparators( - IOUtils.toString(SidebarGeneratorTest.class.getResourceAsStream("sidebar.yml"), StandardCharsets.UTF_8)); + IOUtil.readToString(SidebarGeneratorTest.class.getResourceAsStream("sidebar.yml"), StandardCharsets.UTF_8)); assertEquals(expected, yaml); } } diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index aafc0e252f..4c3371d21c 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index c0ce17de4e..f4faa32bd4 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-go/src/main/antlr4/net/sourceforge/pmd/lang/go/ast/Golang.g4 b/pmd-go/src/main/antlr4/net/sourceforge/pmd/lang/go/ast/Golang.g4 index 673c0b6682..c54605fc85 100644 --- a/pmd-go/src/main/antlr4/net/sourceforge/pmd/lang/go/ast/Golang.g4 +++ b/pmd-go/src/main/antlr4/net/sourceforge/pmd/lang/go/ast/Golang.g4 @@ -1215,6 +1215,7 @@ fragment UNICODE_LETTER | [\u1780-\u17B3] | [\u1820-\u1877] | [\u1880-\u18A8] + | [\u1D00-\u1DFF] | [\u1E00-\u1E9B] | [\u1EA0-\u1EE0] | [\u1EE1-\u1EF9] @@ -1264,11 +1265,10 @@ fragment UNICODE_LETTER | [\u31A0-\u31B7] | [\u3400] | [\u4DB5] - | [\u4E00] - | [\u9FA5] + | [\u4E00-\u9EA5] | [\uA000-\uA48C] - | [\uAC00] - | [\uD7A3] + | [\uA490-\uABFF] + | [\uAC00-\uD7AF] | [\uF900-\uFA2D] | [\uFB00-\uFB06] | [\uFB13-\uFB17] diff --git a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java b/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java index 309867e995..0a21eb2e1b 100644 --- a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java +++ b/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java @@ -40,4 +40,11 @@ public class GoTokenizerTest extends CpdTextComparisonTest { public void testIssue1751() { doTest("issue-1751"); } + + @Test + public void testUnicode() { + // https://github.com/pmd/pmd/issues/2752 + doTest("sample_unicode"); + } + } diff --git a/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.go b/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.go new file mode 100644 index 0000000000..02325f94df --- /dev/null +++ b/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.go @@ -0,0 +1,20 @@ +func main() { + //string底层是一个byte数组,因此string也可以进行切片操作 + str := "hello world"//string是不可变的 + slice := str[4:] + fmt.Println(slice) + + //若要对string进行修改需要将string修改为byte或rune的切片在操作 + //但是转为byte无法进行中文操作 + bytes := []byte(str) + bytes[2] = 'x' + str = string(bytes) + fmt.Println(str) + + //转换成rune可以对中文进行操作 + runes := []rune(str) + runes[0] = '哈' + str = string(runes) + fmt.Println(str) + +} diff --git a/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.txt b/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.txt new file mode 100644 index 0000000000..ecc27507ff --- /dev/null +++ b/pmd-go/src/test/resources/net/sourceforge/pmd/lang/go/cpd/testdata/sample_unicode.txt @@ -0,0 +1,89 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [func] 1 5 + [main] 6 10 + [(] 10 11 + [)] 11 12 + [{] 13 14 +L3 + [str] 2 5 + [:=] 6 8 + ["hello world"] 9 22 +L4 + [slice] 2 7 + [:=] 8 10 + [str] 11 14 + [\[] 14 15 + [4] 15 16 + [:] 16 17 + [\]] 17 18 +L5 + [fmt] 2 5 + [.] 5 6 + [Println] 6 13 + [(] 13 14 + [slice] 14 19 + [)] 19 20 +L9 + [bytes] 2 7 + [:=] 8 10 + [\[] 11 12 + [\]] 12 13 + [byte] 13 17 + [(] 17 18 + [str] 18 21 + [)] 21 22 +L10 + [bytes] 2 7 + [\[] 7 8 + [2] 8 9 + [\]] 9 10 + [=] 11 12 + ['x'] 13 16 +L11 + [str] 2 5 + [=] 6 7 + [string] 8 14 + [(] 14 15 + [bytes] 15 20 + [)] 20 21 +L12 + [fmt] 2 5 + [.] 5 6 + [Println] 6 13 + [(] 13 14 + [str] 14 17 + [)] 17 18 +L15 + [runes] 2 7 + [:=] 8 10 + [\[] 11 12 + [\]] 12 13 + [rune] 13 17 + [(] 17 18 + [str] 18 21 + [)] 21 22 +L16 + [runes] 2 7 + [\[] 7 8 + [0] 8 9 + [\]] 9 10 + [=] 11 12 + ['哈'] 13 16 +L17 + [str] 2 5 + [=] 6 7 + [string] 8 14 + [(] 14 15 + [runes] 15 20 + [)] 20 21 +L18 + [fmt] 2 5 + [.] 5 6 + [Println] 6 13 + [(] 13 14 + [str] 14 17 + [)] 17 18 +L20 + [}] 1 2 +EOF diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index 871d8ffaa9..f50f8a4ff9 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-html/pom.xml b/pmd-html/pom.xml index 9a51a81887..ae841490fd 100644 --- a/pmd-html/pom.xml +++ b/pmd-html/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java index d3389fe55c..e9614c1e3e 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java @@ -13,7 +13,7 @@ public final class HtmlLanguageModule extends BaseLanguageModule { public static final String TERSE_NAME = "html"; public HtmlLanguageModule() { - super(NAME, null, TERSE_NAME, "html"); + super(NAME, null, TERSE_NAME, "html", "htm", "xhtml", "xht", "shtml"); addDefaultVersion("", new HtmlHandler()); } diff --git a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlXPathRuleTest.java b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlXPathRuleTest.java index f54640b6b3..0bb5b031d8 100644 --- a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlXPathRuleTest.java +++ b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlXPathRuleTest.java @@ -11,6 +11,16 @@ import org.junit.Test; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; +import net.sourceforge.pmd.lang.html.ast.ASTHtmlComment; +import net.sourceforge.pmd.lang.html.ast.ASTHtmlDocument; +import net.sourceforge.pmd.lang.html.ast.ASTHtmlTextNode; +import net.sourceforge.pmd.lang.html.ast.HtmlParsingHelper; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; @@ -18,27 +28,51 @@ public class HtmlXPathRuleTest { // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter private static final String LIGHTNING_WEB_COMPONENT = "\n" - + ""; + + ""; @Test public void selectTextNode() { // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter // "Don’t add spaces around the property, for example, { data } is not valid HTML." - String xpath = "//*[local-name() = '#text'][contains(@Text, '{ ')]"; + String xpath = "//text()[contains(., '{ ')]"; List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); Assert.assertEquals(1, violations.size()); Assert.assertEquals(3, violations.get(0).getBeginLine()); } + @Test + public void selectTextNodeByNodeNameShouldNotWork() { + String xpath = "//*[local-name() = '#text']"; + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(0, violations.size()); + } + + @Test + public void verifyTextNodeName() { + ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("

foobar

"); + ASTHtmlTextNode textNode = document.getFirstDescendantOfType(ASTHtmlTextNode.class); + Assert.assertEquals("#text", textNode.getXPathNodeName()); + } + + @Test + public void verifyCommentNodeName() { + ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("

"); + ASTHtmlComment comment = document.getFirstDescendantOfType(ASTHtmlComment.class); + Assert.assertEquals("#comment", comment.getXPathNodeName()); + } + @Test public void selectAttributes() { // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter @@ -50,6 +84,27 @@ public class HtmlXPathRuleTest { Assert.assertEquals(4, violations.get(0).getBeginLine()); } + @Test + public void selectAttributesMultiple() { + // from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter + // "Don’t add spaces around the property, for example, { data } is not valid HTML." + String xpath = "//*[@*[local-name() = ('value', 'onchange')] = '{']"; + + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(2, violations.size()); + Assert.assertEquals(4, violations.get(0).getBeginLine()); + Assert.assertEquals(6, violations.get(1).getBeginLine()); + } + + @Test + public void selectAttributeByName() { + String xpath = "//*[@*[local-name() = 'if:true']]"; + + List violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath); + Assert.assertEquals(1, violations.size()); + Assert.assertEquals(10, violations.get(0).getBeginLine()); + } + private List runXPath(String html, String xpath) { XPathRule rule = new XPathRule(XPathVersion.DEFAULT, xpath); rule.setMessage("test"); diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index d175247c17..a75413cd0e 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -796,7 +796,7 @@ TOKEN : "\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346","\u1348"-"\u135a", "\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676","\u1681"-"\u169a","\u16a0"-"\u16ea", "\u1780"-"\u17b3","\u17db","\u1820"-"\u1877","\u1880"-"\u18a8","\u1e00"-"\u1e9b", - "\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", + "\u1ea0"-"\u1ef9","\u1d00"-"\u1eef","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", "\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4", "\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3", "\u1fd6"-"\u1fdb","\u1fe0"-"\u1fec","\u1ff2"-"\u1ff4","\u1ff6"-"\u1ffc","\u203f"-"\u2040", @@ -804,7 +804,7 @@ TOKEN : "\u2124","\u2126","\u2128","\u212a"-"\u212d","\u212f"-"\u2131","\u2133"-"\u2139", "\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u3029","\u3031"-"\u3035","\u3038"-"\u303a", "\u3041"-"\u3094","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c","\u3131"-"\u318e", - "\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\uac00"-"\ud7a3", + "\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\ua490"-"\uabff","\uac00"-"\ud7a3", "\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d","\ufb1f"-"\ufb28", "\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44", "\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb", @@ -865,7 +865,7 @@ TOKEN : "\u12f0"-"\u130e","\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346", "\u1348"-"\u135a","\u1369"-"\u1371","\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676", "\u1681"-"\u169a","\u16a0"-"\u16ea","\u1780"-"\u17d3","\u17db","\u17e0"-"\u17e9", - "\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1e00"-"\u1e9b", + "\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1d00"-"\u1d7f","\u1e00"-"\u1e9b", "\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d", "\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4", "\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3", @@ -876,6 +876,7 @@ TOKEN : "\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u302f","\u3031"-"\u3035","\u3038"-"\u303a", "\u3041"-"\u3094","\u3099"-"\u309a","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c", "\u3131"-"\u318e","\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c", + "\ua490"-"\uabff", "\uac00"-"\ud7a3","\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d"-"\ufb28", "\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44", "\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb", diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 1b25ad4b65..263b8cdc5c 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -140,10 +140,6 @@ org.ow2.asm asm
- - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInitializer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInitializer.java index ad01f55ecc..53e713335c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInitializer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInitializer.java @@ -36,4 +36,12 @@ public final class ASTInitializer extends AbstractJavaNode implements ASTBodyDec void setStatic() { isStatic = true; } + + /** + * Returns the body of this initializer. + */ + public ASTBlock getBody() { + return (ASTBlock) getChild(0); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResource.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResource.java index 29340f611d..008753c050 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResource.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResource.java @@ -91,5 +91,4 @@ public final class ASTResource extends AbstractJavaNode { return ((ASTLocalVariableDeclaration) c).iterator().next().getInitializer(); } } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSynchronizedStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSynchronizedStatement.java index f26a5286b6..b8e92e39ce 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSynchronizedStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSynchronizedStatement.java @@ -34,7 +34,7 @@ public final class ASTSynchronizedStatement extends AbstractStatement { } /** - * Returns the body of the statement. + * Returns the body of this statement. */ public ASTBlock getBody() { return (ASTBlock) getChild(1); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java index 6a4fe24939..458b52d8b7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java @@ -211,7 +211,7 @@ final class AstDisambiguationPass { JTypeDeclSymbol sym = type.getReferencedSym(); if (type.getParent() instanceof ASTAnnotation) { if (!(sym instanceof JClassSymbol && (sym.isUnresolved() || ((JClassSymbol) sym).isAnnotation()))) { - ctx.getLogger().error(type, JavaSemanticErrors.EXPECTED_ANNOTATION_TYPE); + ctx.getLogger().warning(type, JavaSemanticErrors.EXPECTED_ANNOTATION_TYPE); } return; } @@ -219,7 +219,7 @@ final class AstDisambiguationPass { int actualArity = ASTList.sizeOrZero(type.getTypeArguments()); int expectedArity = sym instanceof JClassSymbol ? ((JClassSymbol) sym).getTypeParameterCount() : 0; if (actualArity != 0 && actualArity != expectedArity) { - ctx.getLogger().error(type, JavaSemanticErrors.MALFORMED_GENERIC_TYPE, expectedArity, actualArity); + ctx.getLogger().warning(type, JavaSemanticErrors.MALFORMED_GENERIC_TYPE, expectedArity, actualArity); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java index ba568bef6b..d46e64ec4c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java @@ -93,7 +93,7 @@ public final class InternalApiBridge { try { it.getTypeMirror(); } catch (Exception e) { - processor.getLogger().error(it, "Error during type resolution of node " + it.getXPathNodeName()); + processor.getLogger().warning(it, "Error during type resolution of node " + it.getXPathNodeName()); } }); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java index d56c73255f..d0816e5d2b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateFieldRule.java @@ -37,7 +37,16 @@ public class UnusedPrivateFieldRule extends AbstractJavaRulechainRule { "lombok.Getter", "java.lang.Deprecated", "lombok.experimental.Delegate", - "javafx.fxml.FXML" + "javafx.fxml.FXML", + "javax.persistence.Id", + "javax.persistence.EmbeddedId", + "javax.persistence.Version", + "jakarta.persistence.Id", + "jakarta.persistence.EmbeddedId", + "jakarta.persistence.Version", + "org.mockito.Mock", + "org.mockito.Spy", + "org.springframework.boot.test.mock.mockito.MockBean" ); private static final PropertyDescriptor> IGNORED_FIELD_NAMES = diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyControlStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyControlStatementRule.java new file mode 100644 index 0000000000..81faf170a8 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyControlStatementRule.java @@ -0,0 +1,151 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.codestyle; + +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; +import net.sourceforge.pmd.lang.java.ast.ASTEmptyStatement; +import net.sourceforge.pmd.lang.java.ast.ASTFinallyClause; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement; +import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; +import net.sourceforge.pmd.lang.java.ast.ASTInitializer; +import net.sourceforge.pmd.lang.java.ast.ASTResource; +import net.sourceforge.pmd.lang.java.ast.ASTResourceList; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; +import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; +import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; +import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; + +public class EmptyControlStatementRule extends AbstractJavaRulechainRule { + + public EmptyControlStatementRule() { + super(ASTFinallyClause.class, ASTSynchronizedStatement.class, ASTTryStatement.class, ASTDoStatement.class, + ASTBlock.class, ASTForStatement.class, ASTForeachStatement.class, ASTWhileStatement.class, + ASTIfStatement.class, ASTSwitchStatement.class, ASTInitializer.class); + } + + @Override + public Object visit(ASTFinallyClause node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty finally clause"); + } + return null; + } + + @Override + public Object visit(ASTSynchronizedStatement node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty synchronized statement"); + } + return null; + } + + @Override + public Object visit(ASTSwitchStatement node, Object data) { + if (node.getNumChildren() == 1) { + asCtx(data).addViolationWithMessage(node, "Empty switch statement"); + } + return null; + } + + @Override + public Object visit(ASTBlock node, Object data) { + if (isEmpty(node) && node.getParent() instanceof ASTBlock) { + asCtx(data).addViolationWithMessage(node, "Empty block"); + } + return null; + } + + @Override + public Object visit(ASTIfStatement node, Object data) { + if (isEmpty(node.getThenBranch())) { + asCtx(data).addViolationWithMessage(node, "Empty if statement"); + } + if (node.hasElse() && isEmpty(node.getElseBranch())) { + asCtx(data).addViolationWithMessage(node.getElseBranch(), "Empty else statement"); + } + return null; + } + + @Override + public Object visit(ASTWhileStatement node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty while statement"); + } + return null; + } + + @Override + public Object visit(ASTForStatement node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty for statement"); + } + return null; + } + + @Override + public Object visit(ASTForeachStatement node, Object data) { + if (JavaRuleUtil.isExplicitUnusedVarName(node.getVarId().getName())) { + // allow `for (ignored : iterable) {}` + return null; + } + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty foreach statement"); + } + return null; + } + + @Override + public Object visit(ASTDoStatement node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty do..while statement"); + } + return null; + } + + @Override + public Object visit(ASTInitializer node, Object data) { + if (isEmpty(node.getBody())) { + asCtx(data).addViolationWithMessage(node, "Empty initializer statement"); + } + return null; + } + + @Override + public Object visit(ASTTryStatement node, Object data) { + if (isEmpty(node.getBody())) { + // all resources must be explicitly ignored + boolean allResourcesIgnored = true; + boolean hasResource = false; + ASTResourceList resources = node.getResources(); + if (resources != null) { + for (ASTResource resource : resources) { + hasResource = true; + String name = resource.getStableName(); + if (!JavaRuleUtil.isExplicitUnusedVarName(name)) { + allResourcesIgnored = false; + break; + } + } + } + + if (hasResource && !allResourcesIgnored) { + asCtx(data).addViolationWithMessage(node, "Empty try body - you could rename the resource to ''ignored''"); + } else if (!hasResource) { + asCtx(data).addViolationWithMessage(node, "Empty try body"); + } + } + return null; + } + + private boolean isEmpty(JavaNode node) { + return node instanceof ASTBlock && node.getNumChildren() == 0 + || node instanceof ASTEmptyStatement; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java index cdeab9b9b7..ecd65ef8b6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java @@ -43,9 +43,6 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule { "lombok.Value" ); - private static final Set INVALIDATING_FIELD_ANNOTS = - setOf("lombok.Setter"); - public ImmutableFieldRule() { super(ASTFieldDeclaration.class); definePropertyDescriptor(IGNORED_ANNOTS); @@ -54,11 +51,9 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTFieldDeclaration field, Object data) { - ASTAnyTypeDeclaration enclosingType = field.getEnclosingType(); if (field.getEffectiveVisibility().isAtMost(Visibility.V_PRIVATE) && !field.getModifiers().hasAny(JModifier.VOLATILE, JModifier.STATIC, JModifier.FINAL) - && !JavaAstUtils.hasAnyAnnotation(field, INVALIDATING_FIELD_ANNOTS) && !JavaAstUtils.hasAnyAnnotation(enclosingType, INVALIDATING_CLASS_ANNOT) && !JavaAstUtils.hasAnyAnnotation(field, getProperty(IGNORED_ANNOTS))) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java index 800b67c249..b949c7973c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java @@ -90,7 +90,7 @@ public final class ReferenceCtx { if (distinct.size() == 1) { return distinct.iterator().next(); } - processor.getLogger().error( + processor.getLogger().warning( errorLocation, AMBIGUOUS_NAME_REFERENCE, name, diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/LazyTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/LazyTypeResolver.java index c2d28ceb14..c1df639d18 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/LazyTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/ast/LazyTypeResolver.java @@ -564,7 +564,7 @@ public final class LazyTypeResolver extends JavaVisitorBase
+ + + + + + + 3 + + + + + + + + Reports unnecessary semicolons (so called "empty statements" and "empty declarations"). + These can be removed without changing the program. The Java grammar + allows them for historical reasons, but they should be avoided. + + This rule will not report empty statements that are syntactically + required, for instance, because they are the body of a control statement. + + This rule replaces EmptyStatementNotInLoop. + + 3 + + + + + + + + + + + + + Empty finally blocks serve no purpose and should be removed. + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1560,6 +1564,7 @@ public class Foo { Empty If Statement finds instances where a condition is checked but nothing is done about it. + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1592,6 +1600,7 @@ public class Foo { Empty initializers serve no purpose and should be removed. + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1620,6 +1632,7 @@ public class Foo { Empty block statements serve no purpose and should be removed. + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1691,13 +1707,17 @@ public void doit() { -Empty switch statements serve no purpose and should be removed. +Empty switch statements serve no purpose and should be removed.# + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1719,6 +1739,7 @@ public void bar() { Empty synchronized blocks serve no purpose and should be removed. + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1747,6 +1771,7 @@ public class Foo { Avoid empty try blocks - what's the point? + +This rule is deprecated since PMD 6.46.0. Use the rule {% rule "java/codestyle/EmptyControlStatement" %} +from category codestyle instead. 3 @@ -1780,6 +1808,7 @@ public class Foo { 3 diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 26ac3a194a..3b3be08128 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -671,8 +671,8 @@ You must use `new ArrayList<>(Arrays.asList(...))` if that is inconvenient for y [VariableDeclarator[NumericLiteral[@IntLiteral][@Image = '0']]] ] ] - [not(.//IfStatement)] - /*[last()]// + [*[2]//FieldAccess[@Name = 'length']/VariableAccess[pmd-java:typeIs("java.lang.Object[]")]] + /*[last()]/ExpressionStatement/ MethodCall [pmd-java:matchesSig('java.util.Collection#add(_)')] [ArgumentList/ArrayAccess @@ -680,9 +680,8 @@ You must use `new ArrayList<>(Arrays.asList(...))` if that is inconvenient for y ] | //ForeachStatement - [not(.//IfStatement)] [VariableAccess[pmd-java:typeIs("java.lang.Object[]")]] - /*[last()]//MethodCall + /*[last()]/ExpressionStatement/MethodCall [pmd-java:matchesSig('java.util.Collection#add(_)')] [ArgumentList [VariableAccess[@Name = ancestor::ForeachStatement/LocalVariableDeclaration/VariableDeclarator/VariableDeclaratorId/@Name]] @@ -697,12 +696,12 @@ public class Test { public void foo(Integer[] ints) { // could just use Arrays.asList(ints) List l = new ArrayList<>(100); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < ints.length; i++) { l.add(ints[i]); } List anotherList = new ArrayList<>(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < ints.length; i++) { anotherList.add(ints[i].toString()); // won't trigger the rule } } diff --git a/pmd-java/src/main/resources/rulesets/java/quickstart.xml b/pmd-java/src/main/resources/rulesets/java/quickstart.xml index 9b703912e6..3c13375a8f 100644 --- a/pmd-java/src/main/resources/rulesets/java/quickstart.xml +++ b/pmd-java/src/main/resources/rulesets/java/quickstart.xml @@ -264,17 +264,10 @@ + + - - - - - - - - - diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 65acf4b157..db07462b49 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -9,9 +9,9 @@ import static org.junit.Assert.assertTrue; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Locale; -import org.apache.commons.io.FileUtils; import org.junit.AfterClass; import org.junit.Rule; import org.junit.Test; @@ -20,6 +20,7 @@ import org.junit.rules.ExternalResource; import org.junit.rules.TestRule; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.util.IOUtil; public class PMDTaskTest extends AbstractAntTestHelper { @@ -142,7 +143,7 @@ public class PMDTaskTest extends AbstractAntTestHelper { setDefaultCharset("cp1252"); executeTarget("testFormatterEncodingWithXML"); - String report = FileUtils.readFileToString(currentTempFile(), "UTF-8"); + String report = IOUtil.readFileToString(currentTempFile(), StandardCharsets.UTF_8); assertTrue(report.contains("someVariableWithÜmlaut")); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java index 2c04b311db..fb66b5c51d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -101,8 +101,8 @@ public class CLITest extends BaseCLITest { public void testWrongRuleset() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml", }; String log = runTest(StatusCode.ERROR, args); - assertThat(log, containsString("Can't find resource 'category/java/designn.xml' for rule 'null'." - + " Make sure the resource is a valid file")); + assertThat(log, containsString("Cannot resolve rule/ruleset reference " + + "'category/java/designn.xml'")); } /** @@ -112,8 +112,8 @@ public class CLITest extends BaseCLITest { public void testWrongRulesetWithRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml/UseCollectionIsEmpty", }; String log = runTest(StatusCode.ERROR, args); - assertThat(log, containsString("Can't find resource 'category/java/designn.xml' for rule " - + "'UseCollectionIsEmpty'.")); + assertThat(log, containsString("Cannot resolve rule/ruleset reference" + + " 'category/java/designn.xml/UseCollectionIsEmpty'")); } /** diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java index 99f534b543..c7cab54408 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java @@ -14,7 +14,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Rule; @@ -27,6 +26,7 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.java.JavaLanguageModule; +import net.sourceforge.pmd.util.IOUtil; public class PMDCoverageTest { @@ -80,7 +80,7 @@ public class PMDCoverageTest { System.err.println("Running PMD with: " + Arrays.toString(args)); PMD.runPmd(args); - report = FileUtils.readFileToString(f, StandardCharsets.UTF_8); + report = IOUtil.readFileToString(f, StandardCharsets.UTF_8); assertEquals("Nothing should be output to stdout", 0, output.getLog().length()); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 6072dd218a..b15c594cad 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -25,54 +25,42 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { */ @Test public void testIgnoreIdentifiers() { - runCPD("--minimum-tokens", "34", "--language", "java", "--files", + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers"); - - String out = getOutput(); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); - Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** * Test ignore identifiers argument with failOnViolation=false */ @Test - public void testIgnoreIdentifiersFailOnViolationFalse() { - runCPD("--minimum-tokens", "34", "--language", "java", "--files", + public void testIgnoreIdentifiersFailOnViolationFalse() throws Exception { + String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--failOnViolation", "false"); - - String out = getOutput(); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); - Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** * Test ignore identifiers argument with failOnViolation=false with changed long options */ @Test - public void testIgnoreIdentifiersFailOnViolationFalseLongOption() { - runCPD("--minimum-tokens", "34", "--language", "java", "--files", + public void testIgnoreIdentifiersFailOnViolationFalseLongOption() throws Exception { + String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--fail-on-violation", "false"); - - String out = getOutput(); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); - Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** * Test excludes option. */ @Test - public void testExcludes() { - runCPD("--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files", + public void testExcludes() throws Exception { + String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--exclude", "src/test/resources/net/sourceforge/pmd/cpd/clitest/File2.java"); - - String out = getOutput(); Assert.assertFalse(out.contains("Found a 7 line (34 tokens) duplication")); - Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** @@ -85,17 +73,15 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { // set the default encoding under Windows System.setProperty("file.encoding", "Cp1252"); - runCPD("--minimum-tokens", "34", "--language", "java", "--files", + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--format", "xml", // request UTF-8 for CPD "--encoding", "UTF-8"); // reset default encoding System.setProperty("file.encoding", origEncoding); - String out = getOutput(); assertThat(out, startsWith("")); assertThat(out, containsPattern("System\\.out\\.println\\([ij] \\+ \"ä\"\\);")); - Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** @@ -103,31 +89,25 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { * */ @Test - public void testBrokenAndValidFile() { - runCPD("--minimum-tokens", "10", "--language", "java", "--files", + public void testBrokenAndValidFile() throws IOException { + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--format", "text", "--skip-lexical-errors"); - String out = getOutput(); Assert.assertTrue( Pattern.compile("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file").matcher(out).find()); Assert.assertTrue(out.contains("Found a 5 line (13 tokens) duplication")); - Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } @Test - public void testFormatXmlWithoutEncoding() { - runCPD("--minimum-tokens", "10", "--language", "java", "--files", + public void testFormatXmlWithoutEncoding() throws Exception { + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--format", "xml"); - String out = getOutput(); Assert.assertTrue(out.contains("")); - Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } @Test - public void testCSVFormat() { - runCPD("--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", + public void testCSVFormat() throws Exception { + String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--language", "c", "--format", "csv"); - String out = getOutput(); Assert.assertFalse(out.contains("Couldn't instantiate renderer")); - Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java index f6df0c19c9..0643500672 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java @@ -15,6 +15,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -125,7 +126,7 @@ public class JavaParsingHelper extends BaseParsingHelper + + + + #3824 #3825 Do not flag fields annotated with @Id, @EmbeddedId, or @Version + 0 + + + + + Do not flag Mockito fields that are injected into test target + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index 9bb180e1a3..fd77707ff7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -516,4 +516,31 @@ public class Foo { } ]]> + + With records + 0 + stringSet) { + + public boolean hasMoreThanOneItem() { + return this.stringSet.size() > 1; + } +} ]]> + + + With records and size check + 1 + 6 + stringSet) { + + public boolean hasMoreThanOneItem() { + return this.stringSet.size() == 0; + } +} ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyControlStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyControlStatement.xml new file mode 100644 index 0000000000..b0a5134918 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyControlStatement.xml @@ -0,0 +1,520 @@ + + + + + pos, empty try block + 1 + 3 + + Empty try body + + + + + + pos, empty try block + 1 + 3 + + Empty try body + + + + + pos, empty finally block + 1 + 5 + + Empty finally clause + + + + + + pos, empty finally block + 0 + + + + pos, empty try and finally block + 2 + + Empty try body + Empty finally clause + + + + + + #432 empty try-with-resource - not ok + 1 + + Empty try body - you could rename the resource to 'ignored' + + target.request(mediaTypes).delete(), DELETE, new ExpectedResponse(status, required))) { + // was false positive + // EmptyTryBlock was fixed to ignore empty try-with-resources. + // This new rule will by default report also empty try-with-resource blocks, + // if the resource name is not "ignored", see next test case. + } + } + } + ]]> + + + + #432 empty try-with-resource - ok with unused var name + 0 + target.request(mediaTypes).delete(), DELETE, new ExpectedResponse(status, required))) { + } + } + } + ]]> + + + + empty concise try-with-resource - not ok + 1 + 4 + + Empty try body - you could rename the resource to 'ignored' + + + + + + pos, empty synchronized stmt + 1 + + Empty synchronized statement + + + + + + neg, nonempty synchronized stmt + 0 + + + + + pos, empty switch stmt + 1 + + Empty switch statement + + + + + + neg, nonempty switch stmt + 0 + + + + + + pos, empty block + 1 + + Empty block + + + + + + statement block not empty + 0 + + + + + empty initializer failure case (non static) + 1 + + Empty initializer statement + + + + + + empty initializer failure case (static) + 1 + + Empty initializer statement + + + + + + not an initializer - empty statement block + 1 + + Empty block + + + + + + initializer not empty + 0 + + + + + static initializer not empty + 0 + + + + pos, empty for + 2 + 3,5 + + Empty for statement + Empty for statement + + + + + pos, empty do..while + 2 + 4,6 + + Empty do..while statement + Empty do..while statement + + + + + pos, empty foreach + 2 + 6,8 + + Empty foreach statement + Empty foreach statement + + list) { + for (int i : list) { + } + for (int i : list) ; + for (int i : list) { // neg, nonempty + System.out.println(i); + } + } + } + ]]> + + + neg, empty foreach with unused var name + 0 + list) { + for (int ignored : list) { + } + for (int ignored2 : list) ; + for (int i : list) { // neg, nonempty + System.out.println(i); + } + } + } + ]]> + + + + pos, empty while + 1 + 3 + + Empty while statement + + + + + + while(true); + 1 + + Empty while statement + + + + + + one empty if statement + 1 + 3 + + Empty if statement + + 2) { + } + } +} + ]]> + + + + empty if with else statement + 2 + 3,4 + + Empty if statement + Empty else statement + + 2) { + } else { + } + } +} + ]]> + + + + empty if with else and else if statement + 3 + 3,4,5 + + Empty if statement + Empty if statement + Empty else statement + + 2) { + } else if (x > 3) { + } else { + } + } +} + ]]> + + + + one not empty if statement + 0 + 2) { + x = 1; + } + } +} + ]]> + + + + empty if statement + 1 + + Empty if statement + + 2); + } +} + ]]> + + + + empty if statement with comment + 1 + + Empty if statement + + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessarySemicolon.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessarySemicolon.xml new file mode 100644 index 0000000000..1736521e76 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessarySemicolon.xml @@ -0,0 +1,72 @@ + + + + + + Neg, necessary empty statements + 0 + + + + + Pos, empty stmt in ctor + 1 + 3 + + + + + Pos, empty stmt in method + 1 + 3 + + + + + Pos, empty decl in toplevel + 2 + 1,3 + + + + + Pos, empty decl in inner class + 1 + 3 + + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml index dae43dfb98..ccb0385278 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ImmutableField.xml @@ -657,4 +657,157 @@ public class ExampleImmutableField { } ]]> + + + #3874 [java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito) + 0 + + + + #3981 #3998 [java] ImmutableField reports fields annotated with @Value (Spring) and @Captor (Mockito) + 0 + baz2; +} + ]]> + + + #4008 FN with field assigned only in initializer + 1 + + + + #4008 FN with field assigned only in initializer (no ctor) + 1 + + + + [java] ImmutableField: Do not flag fields annotated with @Inject #4011 + 0 + + + + [java] ImmutableField reports fields annotated with @FindBy and @FindBys (Selenium) #4020 + 0 + + + + [java] ImmutableField reports fields annotated with @GwtMock (GwtMockito) and @Spy (Mockito) #4004 + 0 + + + + [java] ImmutableField: Do not flag fields in @Entity #3823 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDuplicateLiterals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDuplicateLiterals.xml index b767b87aa9..799c7111b0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDuplicateLiterals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDuplicateLiterals.xml @@ -16,6 +16,7 @@ public class Foo { buz("Howdy"); buz("Howdy"); } + private void buz(String x) {} } ]]> @@ -28,6 +29,7 @@ public class Foo { private void bar() { buz(2); } + private void buz(int x) {} } ]]> @@ -90,6 +92,7 @@ public class Foo { buz("Howdy"); buz("Howdy"); } + private void buz(String x) {} } ]]> @@ -104,6 +107,7 @@ public class Foo { buz("Howdy"); buz("Howdy"); buz("Howdy"); buz("Howdy"); buz("foo"); buz("foo"); buz("foo"); buz("foo"); } + private void buz(String x) {} } ]]> @@ -119,6 +123,7 @@ public class Foo { buz("Howdy,foo"); buz("Howdy,foo"); } + private void buz(String x) {} } ]]> @@ -133,6 +138,7 @@ public class Foo { private void bar() { buz("Howdy"); buz("Howdy"); buz("Howdy"); buz("Howdy"); } + private void buz(String x) {} } ]]> @@ -146,6 +152,7 @@ public class Foo { private void bar() { buz("foo"); buz("foo"); buz("foo"); buz("foo"); } + private void buz(String x) {} } ]]> @@ -160,6 +167,7 @@ public class Foo { buz("foo"); buz("foo"); buz("foo"); buz("foo"); buz("fo"); buz("fo"); buz("fo"); buz("fo"); } + private void buz(String x) {} } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidFieldNameMatchingMethodName.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidFieldNameMatchingMethodName.xml index 53236a6a78..6c444e243f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidFieldNameMatchingMethodName.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidFieldNameMatchingMethodName.xml @@ -101,6 +101,48 @@ public class Foo { + + + + Test1 in Enum #3936 + 1 + + + + + Test2 in Enum #3936 + 0 + + + + + Test3 in Enum #3936 + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArraysAsList.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArraysAsList.xml index df5c9d6c0a..a3ad5af636 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArraysAsList.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArraysAsList.xml @@ -14,9 +14,9 @@ import java.util.List; public class Bar { void foo() { - Integer[] ints = new Integer(10); - List l = new ArrayList(10); - for (int i = 0; i < 100; i++) { + Integer[] ints = new Integer[10]; + List l = new ArrayList(ints.length); + for (int i = 0; i < ints.length; i++) { l.add(ints[i]); } } @@ -114,11 +114,13 @@ public class Test { public void foo(Integer[] ints) { // could just use Arrays.asList(ints) List l = new ArrayList(10); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < ints.length; i++) { l.add(ints[i]); } - for (int i = 0; i < 100; i++) { - l.add(a[i].toString()); // won't trigger the rule + + List l2 = new ArrayList(10); + for (int i = 0; i < ints.length; i++) { + l2.add(ints[i].toString()); // won't trigger the rule } } } @@ -179,15 +181,77 @@ public class Test { public void foo(Integer[] ints) { // could just use Arrays.asList(ints) List l = new ArrayList<>(100); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < ints.length; i++) { l.add(ints[i]); // line 9, here is the violation } List anotherList = new ArrayList<>(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < ints.length; i++) { anotherList.add(ints[i].toString()); // line 13 - false positive } } } ]]> + + + [java] UseArraysAsList false positive with non-trivial loops #3965 + 0 + l = new ArrayList<>(100); + for (int i = 0; i < 100; i++) { + switch (lookup(ints[i])) { + case 1: l.add(ints[i]); break; // line 10 - false positive + case 2: l.addAll(getInts(i)); break; + } + } + + List anotherList = new ArrayList<>(); + for (int i : ints) { + switch (lookup(i)) { + case 1: anotherList.add(i); break; // line 18 - false positive + case 2: anotherList.addAll(getInts(i)); break; + } + } + } + + int lookup(int a) { + return a; + } + + List getInts(int a) { + return Arrays.asList(a); + } +} +]]> + + + + [java] UseArraysAsList must ignore primitive arrays #3379 + 0 + arrayList = new ArrayList<>(array.length); + for (short v : array) { + arrayList.add(v); // line 9 - false positive + } + + List arrayList2 = new ArrayList<>(array.length); + for (int i = 0; i < array.length; i++) { + arrayList2.add(array[i]); // line 14 - false positive + } + } +} +]]> + diff --git a/pmd-javascript/etc/grammar/Ecmascript5.jj b/pmd-javascript/etc/grammar/Ecmascript5.jj index e180c2f760..30d8800119 100644 --- a/pmd-javascript/etc/grammar/Ecmascript5.jj +++ b/pmd-javascript/etc/grammar/Ecmascript5.jj @@ -488,6 +488,7 @@ TOKEN : "\u1E00"-"\u1E9B", "\u1EA0"-"\u1EE0", "\u1EE1"-"\u1EF9", + "\u1EE1"-"\u1EEF", "\u1F00"-"\u1F15", "\u1F18"-"\u1F1D", "\u1F20"-"\u1F39", @@ -534,11 +535,10 @@ TOKEN : "\u31A0"-"\u31B7", "\u3400", "\u4DB5", - "\u4E00", - "\u9FA5", + "\u4E00"-"\u9EA5", "\uA000"-"\uA48C", - "\uAC00", - "\uD7A3", + "\uA490"-"\uABFF", + "\uAC00"-"\ud7AF", "\uF900"-"\uFA2D", "\uFB00"-"\uFB06", "\uFB13"-"\uFB17", diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 506dd0234e..af760b1a97 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -86,10 +86,6 @@ rhino 1.7.14 - - commons-io - commons-io - junit diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 4b92f726e5..c6b4df8f27 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -15,18 +15,18 @@ import net.sourceforge.pmd.cli.BaseCPDCLITest; public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { @Test public void shouldFindDuplicatesWithDifferentFileExtensions() { - runCPD("--minimum-tokens", "5", "--language", "js", "--files", + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "5", "--language", "js", "--files", "src/test/resources/net/sourceforge/pmd/cpd/ts/File1.ts", "src/test/resources/net/sourceforge/pmd/cpd/ts/File2.ts"); - assertThat(getOutput(), containsString("Found a 9 line (32 tokens) duplication in the following files")); + assertThat(out, containsString("Found a 9 line (32 tokens) duplication in the following files")); } @Test public void shouldFindNoDuplicatesWithDifferentFileExtensions() { - runCPD("--minimum-tokens", "5", "--language", "js", "--files", + String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "5", "--language", "js", "--files", "src/test/resources/net/sourceforge/pmd/cpd/ts/"); - assertThat(getOutput().trim(), emptyString()); + assertThat(out.trim(), emptyString()); } } diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java index d51f844aff..68b6620c0d 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java @@ -180,6 +180,16 @@ public class EcmascriptParserTest extends EcmascriptParserTestBase { assertEquals("^=", infix.getImage()); } + @Test + public void testUnicodeCjk() { + // the first is u+4F60 + js.parse("import { Test } from 'test2'\n" + + "define('element', class extends Test {\n" + + " \n" + + " }\n" + + "})"); + } + /** * [javascript] Failing with OutOfMemoryError parsing a Javascript file #2081 */ diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index d2956cb7a1..63505ded97 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index cc2edc2f2b..088fbe41a4 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -8,14 +8,27 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml + + true + + + + + ${project.basedir}/src/main/resources + true + + org.antlr antlr4-maven-plugin + + src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast + org.apache.maven.plugins @@ -29,9 +42,11 @@ - + + + diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/Kotlin.g4 b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/Kotlin.g4 new file mode 100644 index 0000000000..c905ec427d --- /dev/null +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/Kotlin.g4 @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: Apache-2.0 +/** + * Kotlin syntax grammar in ANTLR4 notation + */ + +parser grammar Kotlin; + +@header { +import net.sourceforge.pmd.lang.ast.impl.antlr4.*; +import net.sourceforge.pmd.lang.ast.AstVisitor; +} + +@parser::members { + + static final AntlrNameDictionary DICO = new KotlinNameDictionary(VOCABULARY, ruleNames); + + @Override + public KotlinTerminalNode createPmdTerminal(ParserRuleContext parent, Token t) { + return new KotlinTerminalNode(t); + } + + @Override + public KotlinErrorNode createPmdError(ParserRuleContext parent, Token t) { + return new KotlinErrorNode(t); + } +} + +options { + tokenVocab = KotlinLexer; + contextSuperClass = 'KotlinInnerNode'; + superClass = 'AntlrGeneratedParserBase'; +} + +// SECTION: general + +kotlinFile + : shebangLine? NL* fileAnnotation* packageHeader importList topLevelObject* EOF + ; + +script + : shebangLine? NL* fileAnnotation* packageHeader importList (statement semi)* EOF + ; + +shebangLine + : ShebangLine NL+ + ; + +fileAnnotation + : (AT_NO_WS | AT_PRE_WS) FILE NL* COLON NL* (LSQUARE unescapedAnnotation+ RSQUARE | unescapedAnnotation) NL* + ; + +packageHeader + : (PACKAGE identifier semi?)? + ; + +importList + : importHeader* + ; + +importHeader + : IMPORT identifier (DOT MULT | importAlias)? semi? + ; + +importAlias + : AS simpleIdentifier + ; + +topLevelObject + : declaration semis? + ; + +typeAlias + : modifiers? TYPE_ALIAS NL* simpleIdentifier (NL* typeParameters)? NL* ASSIGNMENT NL* type + ; + +declaration + : classDeclaration + | objectDeclaration + | functionDeclaration + | propertyDeclaration + | typeAlias + ; + +// SECTION: classes + +classDeclaration + : modifiers? (CLASS | (FUN NL*)? INTERFACE) NL* simpleIdentifier + (NL* typeParameters)? (NL* primaryConstructor)? + (NL* COLON NL* delegationSpecifiers)? + (NL* typeConstraints)? + (NL* classBody | NL* enumClassBody)? + ; + +primaryConstructor + : (modifiers? CONSTRUCTOR NL*)? classParameters + ; + +classBody + : LCURL NL* classMemberDeclarations NL* RCURL + ; + +classParameters + : LPAREN NL* (classParameter (NL* COMMA NL* classParameter)* (NL* COMMA)?)? NL* RPAREN + ; + +classParameter + : modifiers? (VAL | VAR)? NL* simpleIdentifier COLON NL* type (NL* ASSIGNMENT NL* expression)? + ; + +delegationSpecifiers + : annotatedDelegationSpecifier (NL* COMMA NL* annotatedDelegationSpecifier)* + ; + +delegationSpecifier + : constructorInvocation + | explicitDelegation + | userType + | functionType + ; + +constructorInvocation + : userType valueArguments + ; + +annotatedDelegationSpecifier + : annotation* NL* delegationSpecifier + ; + +explicitDelegation + : (userType | functionType) NL* BY NL* expression + ; + +typeParameters + : LANGLE NL* typeParameter (NL* COMMA NL* typeParameter)* (NL* COMMA)? NL* RANGLE + ; + +typeParameter + : typeParameterModifiers? NL* simpleIdentifier (NL* COLON NL* type)? + ; + +typeConstraints + : WHERE NL* typeConstraint (NL* COMMA NL* typeConstraint)* + ; + +typeConstraint + : annotation* simpleIdentifier NL* COLON NL* type + ; + +// SECTION: classMembers + +classMemberDeclarations + : (classMemberDeclaration semis?)* + ; + +classMemberDeclaration + : declaration + | companionObject + | anonymousInitializer + | secondaryConstructor + ; + +anonymousInitializer + : INIT NL* block + ; + +companionObject + : modifiers? COMPANION NL* OBJECT + (NL* simpleIdentifier)? + (NL* COLON NL* delegationSpecifiers)? + (NL* classBody)? + ; + +functionValueParameters + : LPAREN NL* (functionValueParameter (NL* COMMA NL* functionValueParameter)* (NL* COMMA)?)? NL* RPAREN + ; + +functionValueParameter + : parameterModifiers? parameter (NL* ASSIGNMENT NL* expression)? + ; + +functionDeclaration + : modifiers? + FUN (NL* typeParameters)? (NL* receiverType NL* DOT)? NL* simpleIdentifier + NL* functionValueParameters + (NL* COLON NL* type)? + (NL* typeConstraints)? + (NL* functionBody)? + ; + +functionBody + : block + | ASSIGNMENT NL* expression + ; + +variableDeclaration + : annotation* NL* simpleIdentifier (NL* COLON NL* type)? + ; + +multiVariableDeclaration + : LPAREN NL* variableDeclaration (NL* COMMA NL* variableDeclaration)* (NL* COMMA)? NL* RPAREN + ; + +propertyDeclaration + : modifiers? (VAL | VAR) + (NL* typeParameters)? + (NL* receiverType NL* DOT)? + (NL* (multiVariableDeclaration | variableDeclaration)) + (NL* typeConstraints)? + (NL* (ASSIGNMENT NL* expression | propertyDelegate))? + (NL+ SEMICOLON)? NL* (getter? (NL* semi? setter)? | setter? (NL* semi? getter)?) + ; + +propertyDelegate + : BY NL* expression + ; + +getter + : modifiers? GET + (NL* LPAREN NL* RPAREN (NL* COLON NL* type)? NL* functionBody)? + ; + +setter + : modifiers? SET + (NL* LPAREN NL* functionValueParameterWithOptionalType (NL* COMMA)? NL* RPAREN (NL* COLON NL* type)? NL* functionBody)? + ; + +parametersWithOptionalType + : LPAREN NL* (functionValueParameterWithOptionalType (NL* COMMA NL* functionValueParameterWithOptionalType)* (NL* COMMA)?)? NL* RPAREN + ; + +functionValueParameterWithOptionalType + : parameterModifiers? parameterWithOptionalType (NL* ASSIGNMENT NL* expression)? + ; + +parameterWithOptionalType + : simpleIdentifier NL* (COLON NL* type)? + ; + +parameter + : simpleIdentifier NL* COLON NL* type + ; + +objectDeclaration + : modifiers? OBJECT + NL* simpleIdentifier + (NL* COLON NL* delegationSpecifiers)? + (NL* classBody)? + ; + +secondaryConstructor + : modifiers? CONSTRUCTOR NL* functionValueParameters (NL* COLON NL* constructorDelegationCall)? NL* block? + ; + +constructorDelegationCall + : (THIS | SUPER) NL* valueArguments + ; + +// SECTION: enumClasses + +enumClassBody + : LCURL NL* enumEntries? (NL* SEMICOLON NL* classMemberDeclarations)? NL* RCURL + ; + +enumEntries + : enumEntry (NL* COMMA NL* enumEntry)* NL* COMMA? + ; + +enumEntry + : (modifiers NL*)? simpleIdentifier (NL* valueArguments)? (NL* classBody)? + ; + +// SECTION: types + +type + : typeModifiers? (parenthesizedType | nullableType | typeReference | functionType) + ; + +typeReference + : userType + | DYNAMIC + ; + +nullableType + : (typeReference | parenthesizedType) NL* quest+ + ; + +quest + : QUEST_NO_WS + | QUEST_WS + ; + +userType + : simpleUserType (NL* DOT NL* simpleUserType)* + ; + +simpleUserType + : simpleIdentifier (NL* typeArguments)? + ; + +typeProjection + : typeProjectionModifiers? type + | MULT + ; + +typeProjectionModifiers + : typeProjectionModifier+ + ; + +typeProjectionModifier + : varianceModifier NL* + | annotation + ; + +functionType + : (receiverType NL* DOT NL*)? functionTypeParameters NL* ARROW NL* type + ; + +functionTypeParameters + : LPAREN NL* (parameter | type)? (NL* COMMA NL* (parameter | type))* (NL* COMMA)? NL* RPAREN + ; + +parenthesizedType + : LPAREN NL* type NL* RPAREN + ; + +receiverType + : typeModifiers? (parenthesizedType | nullableType | typeReference) + ; + +parenthesizedUserType + : LPAREN NL* (userType | parenthesizedUserType) NL* RPAREN + ; + +// SECTION: statements + +statements + : (statement (semis statement)*)? semis? + ; + +statement + : (label | annotation)* ( declaration | assignment | loopStatement | expression) + ; + +label + : simpleIdentifier (AT_NO_WS | AT_POST_WS) NL* + ; + +controlStructureBody + : block + | statement + ; + +block + : LCURL NL* statements NL* RCURL + ; + +loopStatement + : forStatement + | whileStatement + | doWhileStatement + ; + +forStatement + : FOR NL* LPAREN annotation* (variableDeclaration | multiVariableDeclaration) + IN expression RPAREN NL* controlStructureBody? + ; + +whileStatement + : WHILE NL* LPAREN expression RPAREN NL* (controlStructureBody | SEMICOLON) + ; + +doWhileStatement + : DO NL* controlStructureBody? NL* WHILE NL* LPAREN expression RPAREN + ; + +assignment + : (directlyAssignableExpression ASSIGNMENT | assignableExpression assignmentAndOperator) NL* expression + ; + +semi + : (SEMICOLON | NL) NL* + | EOF + ; + +semis + : (SEMICOLON | NL)+ + | EOF + ; + +// SECTION: expressions + +expression + : disjunction + ; + +disjunction + : conjunction (NL* DISJ NL* conjunction)* + ; + +conjunction + : equality (NL* CONJ NL* equality)* + ; + +equality + : comparison (equalityOperator NL* comparison)* + ; + +comparison + : genericCallLikeComparison (comparisonOperator NL* genericCallLikeComparison)* + ; + +genericCallLikeComparison + : infixOperation callSuffix* + ; + +infixOperation + : elvisExpression (inOperator NL* elvisExpression | isOperator NL* type)* + ; + +elvisExpression + : infixFunctionCall (NL* elvis NL* infixFunctionCall)* + ; + +elvis + : QUEST_NO_WS COLON + ; + +infixFunctionCall + : rangeExpression (simpleIdentifier NL* rangeExpression)* + ; + +rangeExpression + : additiveExpression (RANGE NL* additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (additiveOperator NL* multiplicativeExpression)* + ; + +multiplicativeExpression + : asExpression (multiplicativeOperator NL* asExpression)* + ; + +asExpression + : prefixUnaryExpression (NL* asOperator NL* type)* + ; + +prefixUnaryExpression + : unaryPrefix* postfixUnaryExpression + ; + +unaryPrefix + : annotation + | label + | prefixUnaryOperator NL* + ; + +postfixUnaryExpression + : primaryExpression postfixUnarySuffix* + ; + +postfixUnarySuffix + : postfixUnaryOperator + | typeArguments + | callSuffix + | indexingSuffix + | navigationSuffix + ; + +directlyAssignableExpression + : postfixUnaryExpression assignableSuffix + | simpleIdentifier + | parenthesizedDirectlyAssignableExpression + ; + +parenthesizedDirectlyAssignableExpression + : LPAREN NL* directlyAssignableExpression NL* RPAREN + ; + +assignableExpression + : prefixUnaryExpression + | parenthesizedAssignableExpression + ; + +parenthesizedAssignableExpression + : LPAREN NL* assignableExpression NL* RPAREN + ; + +assignableSuffix + : typeArguments + | indexingSuffix + | navigationSuffix + ; + +indexingSuffix + : LSQUARE NL* expression (NL* COMMA NL* expression)* (NL* COMMA)? NL* RSQUARE + ; + +navigationSuffix + : memberAccessOperator NL* (simpleIdentifier | parenthesizedExpression | CLASS) + ; + +callSuffix + : typeArguments? (valueArguments? annotatedLambda | valueArguments) + ; + +annotatedLambda + : annotation* label? NL* lambdaLiteral + ; + +typeArguments + : LANGLE NL* typeProjection (NL* COMMA NL* typeProjection)* (NL* COMMA)? NL* RANGLE + ; + +valueArguments + : LPAREN NL* (valueArgument (NL* COMMA NL* valueArgument)* (NL* COMMA)? NL*)? RPAREN + ; + +valueArgument + : annotation? NL* (simpleIdentifier NL* ASSIGNMENT NL*)? MULT? NL* expression + ; + +primaryExpression + : parenthesizedExpression + | simpleIdentifier + | literalConstant + | stringLiteral + | callableReference + | functionLiteral + | objectLiteral + | collectionLiteral + | thisExpression + | superExpression + | ifExpression + | whenExpression + | tryExpression + | jumpExpression + ; + +parenthesizedExpression + : LPAREN NL* expression NL* RPAREN + ; + +collectionLiteral + : LSQUARE NL* (expression (NL* COMMA NL* expression)* (NL* COMMA)? NL*)? RSQUARE + ; + +literalConstant + : BooleanLiteral + | IntegerLiteral + | HexLiteral + | BinLiteral + | CharacterLiteral + | RealLiteral + | NullLiteral + | LongLiteral + | UnsignedLiteral + ; + +stringLiteral + : lineStringLiteral + | multiLineStringLiteral + ; + +lineStringLiteral + : QUOTE_OPEN (lineStringContent | lineStringExpression)* QUOTE_CLOSE + ; + +multiLineStringLiteral + : TRIPLE_QUOTE_OPEN (multiLineStringContent | multiLineStringExpression | MultiLineStringQuote)* TRIPLE_QUOTE_CLOSE + ; + +lineStringContent + : LineStrText + | LineStrEscapedChar + | LineStrRef + ; + +lineStringExpression + : LineStrExprStart NL* expression NL* RCURL + ; + +multiLineStringContent + : MultiLineStrText + | MultiLineStringQuote + | MultiLineStrRef + ; + +multiLineStringExpression + : MultiLineStrExprStart NL* expression NL* RCURL + ; + +lambdaLiteral + : LCURL NL* (lambdaParameters? NL* ARROW NL*)? statements NL* RCURL + ; + +lambdaParameters + : lambdaParameter (NL* COMMA NL* lambdaParameter)* (NL* COMMA)? + ; + +lambdaParameter + : variableDeclaration + | multiVariableDeclaration (NL* COLON NL* type)? + ; + +anonymousFunction + : FUN + (NL* type NL* DOT)? + NL* parametersWithOptionalType + (NL* COLON NL* type)? + (NL* typeConstraints)? + (NL* functionBody)? + ; + +functionLiteral + : lambdaLiteral + | anonymousFunction + ; + +objectLiteral + : OBJECT (NL* COLON NL* delegationSpecifiers NL*)? (NL* classBody)? + ; + +thisExpression + : THIS + | THIS_AT + ; + +superExpression + : SUPER (LANGLE NL* type NL* RANGLE)? (AT_NO_WS simpleIdentifier)? + | SUPER_AT + ; + +ifExpression + : IF NL* LPAREN NL* expression NL* RPAREN NL* + ( controlStructureBody + | controlStructureBody? NL* SEMICOLON? NL* ELSE NL* (controlStructureBody | SEMICOLON) + | SEMICOLON) + ; + +whenSubject + : LPAREN (annotation* NL* VAL NL* variableDeclaration NL* ASSIGNMENT NL*)? expression RPAREN + ; + +whenExpression + : WHEN NL* whenSubject? NL* LCURL NL* (whenEntry NL*)* NL* RCURL + ; + +whenEntry + : whenCondition (NL* COMMA NL* whenCondition)* (NL* COMMA)? NL* ARROW NL* controlStructureBody semi? + | ELSE NL* ARROW NL* controlStructureBody semi? + ; + +whenCondition + : expression + | rangeTest + | typeTest + ; + +rangeTest + : inOperator NL* expression + ; + +typeTest + : isOperator NL* type + ; + +tryExpression + : TRY NL* block ((NL* catchBlock)+ (NL* finallyBlock)? | NL* finallyBlock) + ; + +catchBlock + : CATCH NL* LPAREN annotation* simpleIdentifier COLON type (NL* COMMA)? RPAREN NL* block + ; + +finallyBlock + : FINALLY NL* block + ; + +jumpExpression + : THROW NL* expression + | (RETURN | RETURN_AT) expression? + | CONTINUE + | CONTINUE_AT + | BREAK + | BREAK_AT + ; + +callableReference + : receiverType? COLONCOLON NL* (simpleIdentifier | CLASS) + ; + +assignmentAndOperator + : ADD_ASSIGNMENT + | SUB_ASSIGNMENT + | MULT_ASSIGNMENT + | DIV_ASSIGNMENT + | MOD_ASSIGNMENT + ; + +equalityOperator + : EXCL_EQ + | EXCL_EQEQ + | EQEQ + | EQEQEQ + ; + +comparisonOperator + : LANGLE + | RANGLE + | LE + | GE + ; + +inOperator + : IN + | NOT_IN + ; + +isOperator + : IS + | NOT_IS + ; + +additiveOperator + : ADD + | SUB + ; + +multiplicativeOperator + : MULT + | DIV + | MOD + ; + +asOperator + : AS + | AS_SAFE + ; + +prefixUnaryOperator + : INCR + | DECR + | SUB + | ADD + | excl + ; + +postfixUnaryOperator + : INCR + | DECR + | EXCL_NO_WS excl + ; + +excl + : EXCL_NO_WS + | EXCL_WS + ; + +memberAccessOperator + : NL* DOT + | NL* safeNav + | COLONCOLON + ; + +safeNav + : QUEST_NO_WS DOT + ; + +// SECTION: modifiers + +modifiers + : (annotation | modifier)+ + ; + +parameterModifiers + : (annotation | parameterModifier)+ + ; + +modifier + : (classModifier + | memberModifier + | visibilityModifier + | functionModifier + | propertyModifier + | inheritanceModifier + | parameterModifier + | platformModifier) NL* + ; + +typeModifiers + : typeModifier+ + ; + +typeModifier + : annotation + | SUSPEND NL* + ; + +classModifier + : ENUM + | SEALED + | ANNOTATION + | DATA + | INNER + | VALUE + ; + +memberModifier + : OVERRIDE + | LATEINIT + ; + +visibilityModifier + : PUBLIC + | PRIVATE + | INTERNAL + | PROTECTED + ; + +varianceModifier + : IN + | OUT + ; + +typeParameterModifiers + : typeParameterModifier+ + ; + +typeParameterModifier + : reificationModifier NL* + | varianceModifier NL* + | annotation + ; + +functionModifier + : TAILREC + | OPERATOR + | INFIX + | INLINE + | EXTERNAL + | SUSPEND + ; + +propertyModifier + : CONST + ; + +inheritanceModifier + : ABSTRACT + | FINAL + | OPEN + ; + +parameterModifier + : VARARG + | NOINLINE + | CROSSINLINE + ; + +reificationModifier + : REIFIED + ; + +platformModifier + : EXPECT + | ACTUAL + ; + +// SECTION: annotations + +annotation + : (singleAnnotation | multiAnnotation) NL* + ; + +singleAnnotation + : (annotationUseSiteTarget NL* | AT_NO_WS | AT_PRE_WS) unescapedAnnotation + ; + +multiAnnotation + : (annotationUseSiteTarget NL* | AT_NO_WS | AT_PRE_WS) LSQUARE unescapedAnnotation+ RSQUARE + ; + +annotationUseSiteTarget + : (AT_NO_WS | AT_PRE_WS) (FIELD | PROPERTY | GET | SET | RECEIVER | PARAM | SETPARAM | DELEGATE) NL* COLON + ; + +unescapedAnnotation + : constructorInvocation + | userType + ; + +// SECTION: identifiers + +simpleIdentifier + : Identifier + | ABSTRACT + | ANNOTATION + | BY + | CATCH + | COMPANION + | CONSTRUCTOR + | CROSSINLINE + | DATA + | DYNAMIC + | ENUM + | EXTERNAL + | FINAL + | FINALLY + | GET + | IMPORT + | INFIX + | INIT + | INLINE + | INNER + | INTERNAL + | LATEINIT + | NOINLINE + | OPEN + | OPERATOR + | OUT + | OVERRIDE + | PRIVATE + | PROTECTED + | PUBLIC + | REIFIED + | SEALED + | TAILREC + | SET + | VARARG + | WHERE + | FIELD + | PROPERTY + | RECEIVER + | PARAM + | SETPARAM + | DELEGATE + | FILE + | EXPECT + | ACTUAL + | CONST + | SUSPEND + | VALUE + ; + +identifier + : simpleIdentifier (NL* DOT simpleIdentifier)* + ; diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.g4 b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.g4 index c790e314f1..2a33305a1d 100644 --- a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.g4 +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.g4 @@ -1,1668 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 /** - * Kotlin Grammar for ANTLR v4 - * - * Based on: - * http://jetbrains.github.io/kotlin-spec/#_grammars_and_parsing - * and - * http://kotlinlang.org/docs/reference/grammar.html - * - * Tested on - * https://github.com/JetBrains/kotlin/tree/master/compiler/testData/psi + * Kotlin lexical grammar in ANTLR4 notation */ lexer grammar KotlinLexer; -/** - * Taken from http://www.antlr3.org/grammar/1345144569663/AntlrUnicode.txt - */ +import UnicodeClasses; -//lexer grammar UnicodeClasses; - -UNICODE_CLASS_LL: - '\u0061'..'\u007A' | - '\u00B5' | - '\u00DF'..'\u00F6' | - '\u00F8'..'\u00FF' | - '\u0101' | - '\u0103' | - '\u0105' | - '\u0107' | - '\u0109' | - '\u010B' | - '\u010D' | - '\u010F' | - '\u0111' | - '\u0113' | - '\u0115' | - '\u0117' | - '\u0119' | - '\u011B' | - '\u011D' | - '\u011F' | - '\u0121' | - '\u0123' | - '\u0125' | - '\u0127' | - '\u0129' | - '\u012B' | - '\u012D' | - '\u012F' | - '\u0131' | - '\u0133' | - '\u0135' | - '\u0137' | - '\u0138' | - '\u013A' | - '\u013C' | - '\u013E' | - '\u0140' | - '\u0142' | - '\u0144' | - '\u0146' | - '\u0148' | - '\u0149' | - '\u014B' | - '\u014D' | - '\u014F' | - '\u0151' | - '\u0153' | - '\u0155' | - '\u0157' | - '\u0159' | - '\u015B' | - '\u015D' | - '\u015F' | - '\u0161' | - '\u0163' | - '\u0165' | - '\u0167' | - '\u0169' | - '\u016B' | - '\u016D' | - '\u016F' | - '\u0171' | - '\u0173' | - '\u0175' | - '\u0177' | - '\u017A' | - '\u017C' | - '\u017E'..'\u0180' | - '\u0183' | - '\u0185' | - '\u0188' | - '\u018C' | - '\u018D' | - '\u0192' | - '\u0195' | - '\u0199'..'\u019B' | - '\u019E' | - '\u01A1' | - '\u01A3' | - '\u01A5' | - '\u01A8' | - '\u01AA' | - '\u01AB' | - '\u01AD' | - '\u01B0' | - '\u01B4' | - '\u01B6' | - '\u01B9' | - '\u01BA' | - '\u01BD'..'\u01BF' | - '\u01C6' | - '\u01C9' | - '\u01CC' | - '\u01CE' | - '\u01D0' | - '\u01D2' | - '\u01D4' | - '\u01D6' | - '\u01D8' | - '\u01DA' | - '\u01DC' | - '\u01DD' | - '\u01DF' | - '\u01E1' | - '\u01E3' | - '\u01E5' | - '\u01E7' | - '\u01E9' | - '\u01EB' | - '\u01ED' | - '\u01EF' | - '\u01F0' | - '\u01F3' | - '\u01F5' | - '\u01F9' | - '\u01FB' | - '\u01FD' | - '\u01FF' | - '\u0201' | - '\u0203' | - '\u0205' | - '\u0207' | - '\u0209' | - '\u020B' | - '\u020D' | - '\u020F' | - '\u0211' | - '\u0213' | - '\u0215' | - '\u0217' | - '\u0219' | - '\u021B' | - '\u021D' | - '\u021F' | - '\u0221' | - '\u0223' | - '\u0225' | - '\u0227' | - '\u0229' | - '\u022B' | - '\u022D' | - '\u022F' | - '\u0231' | - '\u0233'..'\u0239' | - '\u023C' | - '\u023F' | - '\u0240' | - '\u0242' | - '\u0247' | - '\u0249' | - '\u024B' | - '\u024D' | - '\u024F'..'\u0293' | - '\u0295'..'\u02AF' | - '\u0371' | - '\u0373' | - '\u0377' | - '\u037B'..'\u037D' | - '\u0390' | - '\u03AC'..'\u03CE' | - '\u03D0' | - '\u03D1' | - '\u03D5'..'\u03D7' | - '\u03D9' | - '\u03DB' | - '\u03DD' | - '\u03DF' | - '\u03E1' | - '\u03E3' | - '\u03E5' | - '\u03E7' | - '\u03E9' | - '\u03EB' | - '\u03ED' | - '\u03EF'..'\u03F3' | - '\u03F5' | - '\u03F8' | - '\u03FB' | - '\u03FC' | - '\u0430'..'\u045F' | - '\u0461' | - '\u0463' | - '\u0465' | - '\u0467' | - '\u0469' | - '\u046B' | - '\u046D' | - '\u046F' | - '\u0471' | - '\u0473' | - '\u0475' | - '\u0477' | - '\u0479' | - '\u047B' | - '\u047D' | - '\u047F' | - '\u0481' | - '\u048B' | - '\u048D' | - '\u048F' | - '\u0491' | - '\u0493' | - '\u0495' | - '\u0497' | - '\u0499' | - '\u049B' | - '\u049D' | - '\u049F' | - '\u04A1' | - '\u04A3' | - '\u04A5' | - '\u04A7' | - '\u04A9' | - '\u04AB' | - '\u04AD' | - '\u04AF' | - '\u04B1' | - '\u04B3' | - '\u04B5' | - '\u04B7' | - '\u04B9' | - '\u04BB' | - '\u04BD' | - '\u04BF' | - '\u04C2' | - '\u04C4' | - '\u04C6' | - '\u04C8' | - '\u04CA' | - '\u04CC' | - '\u04CE' | - '\u04CF' | - '\u04D1' | - '\u04D3' | - '\u04D5' | - '\u04D7' | - '\u04D9' | - '\u04DB' | - '\u04DD' | - '\u04DF' | - '\u04E1' | - '\u04E3' | - '\u04E5' | - '\u04E7' | - '\u04E9' | - '\u04EB' | - '\u04ED' | - '\u04EF' | - '\u04F1' | - '\u04F3' | - '\u04F5' | - '\u04F7' | - '\u04F9' | - '\u04FB' | - '\u04FD' | - '\u04FF' | - '\u0501' | - '\u0503' | - '\u0505' | - '\u0507' | - '\u0509' | - '\u050B' | - '\u050D' | - '\u050F' | - '\u0511' | - '\u0513' | - '\u0515' | - '\u0517' | - '\u0519' | - '\u051B' | - '\u051D' | - '\u051F' | - '\u0521' | - '\u0523' | - '\u0525' | - '\u0527' | - '\u0561'..'\u0587' | - '\u1D00'..'\u1D2B' | - '\u1D6B'..'\u1D77' | - '\u1D79'..'\u1D9A' | - '\u1E01' | - '\u1E03' | - '\u1E05' | - '\u1E07' | - '\u1E09' | - '\u1E0B' | - '\u1E0D' | - '\u1E0F' | - '\u1E11' | - '\u1E13' | - '\u1E15' | - '\u1E17' | - '\u1E19' | - '\u1E1B' | - '\u1E1D' | - '\u1E1F' | - '\u1E21' | - '\u1E23' | - '\u1E25' | - '\u1E27' | - '\u1E29' | - '\u1E2B' | - '\u1E2D' | - '\u1E2F' | - '\u1E31' | - '\u1E33' | - '\u1E35' | - '\u1E37' | - '\u1E39' | - '\u1E3B' | - '\u1E3D' | - '\u1E3F' | - '\u1E41' | - '\u1E43' | - '\u1E45' | - '\u1E47' | - '\u1E49' | - '\u1E4B' | - '\u1E4D' | - '\u1E4F' | - '\u1E51' | - '\u1E53' | - '\u1E55' | - '\u1E57' | - '\u1E59' | - '\u1E5B' | - '\u1E5D' | - '\u1E5F' | - '\u1E61' | - '\u1E63' | - '\u1E65' | - '\u1E67' | - '\u1E69' | - '\u1E6B' | - '\u1E6D' | - '\u1E6F' | - '\u1E71' | - '\u1E73' | - '\u1E75' | - '\u1E77' | - '\u1E79' | - '\u1E7B' | - '\u1E7D' | - '\u1E7F' | - '\u1E81' | - '\u1E83' | - '\u1E85' | - '\u1E87' | - '\u1E89' | - '\u1E8B' | - '\u1E8D' | - '\u1E8F' | - '\u1E91' | - '\u1E93' | - '\u1E95'..'\u1E9D' | - '\u1E9F' | - '\u1EA1' | - '\u1EA3' | - '\u1EA5' | - '\u1EA7' | - '\u1EA9' | - '\u1EAB' | - '\u1EAD' | - '\u1EAF' | - '\u1EB1' | - '\u1EB3' | - '\u1EB5' | - '\u1EB7' | - '\u1EB9' | - '\u1EBB' | - '\u1EBD' | - '\u1EBF' | - '\u1EC1' | - '\u1EC3' | - '\u1EC5' | - '\u1EC7' | - '\u1EC9' | - '\u1ECB' | - '\u1ECD' | - '\u1ECF' | - '\u1ED1' | - '\u1ED3' | - '\u1ED5' | - '\u1ED7' | - '\u1ED9' | - '\u1EDB' | - '\u1EDD' | - '\u1EDF' | - '\u1EE1' | - '\u1EE3' | - '\u1EE5' | - '\u1EE7' | - '\u1EE9' | - '\u1EEB' | - '\u1EED' | - '\u1EEF' | - '\u1EF1' | - '\u1EF3' | - '\u1EF5' | - '\u1EF7' | - '\u1EF9' | - '\u1EFB' | - '\u1EFD' | - '\u1EFF'..'\u1F07' | - '\u1F10'..'\u1F15' | - '\u1F20'..'\u1F27' | - '\u1F30'..'\u1F37' | - '\u1F40'..'\u1F45' | - '\u1F50'..'\u1F57' | - '\u1F60'..'\u1F67' | - '\u1F70'..'\u1F7D' | - '\u1F80'..'\u1F87' | - '\u1F90'..'\u1F97' | - '\u1FA0'..'\u1FA7' | - '\u1FB0'..'\u1FB4' | - '\u1FB6' | - '\u1FB7' | - '\u1FBE' | - '\u1FC2'..'\u1FC4' | - '\u1FC6' | - '\u1FC7' | - '\u1FD0'..'\u1FD3' | - '\u1FD6' | - '\u1FD7' | - '\u1FE0'..'\u1FE7' | - '\u1FF2'..'\u1FF4' | - '\u1FF6' | - '\u1FF7' | - '\u210A' | - '\u210E' | - '\u210F' | - '\u2113' | - '\u212F' | - '\u2134' | - '\u2139' | - '\u213C' | - '\u213D' | - '\u2146'..'\u2149' | - '\u214E' | - '\u2184' | - '\u2C30'..'\u2C5E' | - '\u2C61' | - '\u2C65' | - '\u2C66' | - '\u2C68' | - '\u2C6A' | - '\u2C6C' | - '\u2C71' | - '\u2C73' | - '\u2C74' | - '\u2C76'..'\u2C7B' | - '\u2C81' | - '\u2C83' | - '\u2C85' | - '\u2C87' | - '\u2C89' | - '\u2C8B' | - '\u2C8D' | - '\u2C8F' | - '\u2C91' | - '\u2C93' | - '\u2C95' | - '\u2C97' | - '\u2C99' | - '\u2C9B' | - '\u2C9D' | - '\u2C9F' | - '\u2CA1' | - '\u2CA3' | - '\u2CA5' | - '\u2CA7' | - '\u2CA9' | - '\u2CAB' | - '\u2CAD' | - '\u2CAF' | - '\u2CB1' | - '\u2CB3' | - '\u2CB5' | - '\u2CB7' | - '\u2CB9' | - '\u2CBB' | - '\u2CBD' | - '\u2CBF' | - '\u2CC1' | - '\u2CC3' | - '\u2CC5' | - '\u2CC7' | - '\u2CC9' | - '\u2CCB' | - '\u2CCD' | - '\u2CCF' | - '\u2CD1' | - '\u2CD3' | - '\u2CD5' | - '\u2CD7' | - '\u2CD9' | - '\u2CDB' | - '\u2CDD' | - '\u2CDF' | - '\u2CE1' | - '\u2CE3' | - '\u2CE4' | - '\u2CEC' | - '\u2CEE' | - '\u2CF3' | - '\u2D00'..'\u2D25' | - '\u2D27' | - '\u2D2D' | - '\uA641' | - '\uA643' | - '\uA645' | - '\uA647' | - '\uA649' | - '\uA64B' | - '\uA64D' | - '\uA64F' | - '\uA651' | - '\uA653' | - '\uA655' | - '\uA657' | - '\uA659' | - '\uA65B' | - '\uA65D' | - '\uA65F' | - '\uA661' | - '\uA663' | - '\uA665' | - '\uA667' | - '\uA669' | - '\uA66B' | - '\uA66D' | - '\uA681' | - '\uA683' | - '\uA685' | - '\uA687' | - '\uA689' | - '\uA68B' | - '\uA68D' | - '\uA68F' | - '\uA691' | - '\uA693' | - '\uA695' | - '\uA697' | - '\uA723' | - '\uA725' | - '\uA727' | - '\uA729' | - '\uA72B' | - '\uA72D' | - '\uA72F'..'\uA731' | - '\uA733' | - '\uA735' | - '\uA737' | - '\uA739' | - '\uA73B' | - '\uA73D' | - '\uA73F' | - '\uA741' | - '\uA743' | - '\uA745' | - '\uA747' | - '\uA749' | - '\uA74B' | - '\uA74D' | - '\uA74F' | - '\uA751' | - '\uA753' | - '\uA755' | - '\uA757' | - '\uA759' | - '\uA75B' | - '\uA75D' | - '\uA75F' | - '\uA761' | - '\uA763' | - '\uA765' | - '\uA767' | - '\uA769' | - '\uA76B' | - '\uA76D' | - '\uA76F' | - '\uA771'..'\uA778' | - '\uA77A' | - '\uA77C' | - '\uA77F' | - '\uA781' | - '\uA783' | - '\uA785' | - '\uA787' | - '\uA78C' | - '\uA78E' | - '\uA791' | - '\uA793' | - '\uA7A1' | - '\uA7A3' | - '\uA7A5' | - '\uA7A7' | - '\uA7A9' | - '\uA7FA' | - '\uFB00'..'\uFB06' | - '\uFB13'..'\uFB17' | - '\uFF41'..'\uFF5A'; - -UNICODE_CLASS_LM: - '\u02B0'..'\u02C1' | - '\u02C6'..'\u02D1' | - '\u02E0'..'\u02E4' | - '\u02EC' | - '\u02EE' | - '\u0374' | - '\u037A' | - '\u0559' | - '\u0640' | - '\u06E5' | - '\u06E6' | - '\u07F4' | - '\u07F5' | - '\u07FA' | - '\u081A' | - '\u0824' | - '\u0828' | - '\u0971' | - '\u0E46' | - '\u0EC6' | - '\u10FC' | - '\u17D7' | - '\u1843' | - '\u1AA7' | - '\u1C78'..'\u1C7D' | - '\u1D2C'..'\u1D6A' | - '\u1D78' | - '\u1D9B'..'\u1DBF' | - '\u2071' | - '\u207F' | - '\u2090'..'\u209C' | - '\u2C7C' | - '\u2C7D' | - '\u2D6F' | - '\u2E2F' | - '\u3005' | - '\u3031'..'\u3035' | - '\u303B' | - '\u309D' | - '\u309E' | - '\u30FC'..'\u30FE' | - '\uA015' | - '\uA4F8'..'\uA4FD' | - '\uA60C' | - '\uA67F' | - '\uA717'..'\uA71F' | - '\uA770' | - '\uA788' | - '\uA7F8' | - '\uA7F9' | - '\uA9CF' | - '\uAA70' | - '\uAADD' | - '\uAAF3' | - '\uAAF4' | - '\uFF70' | - '\uFF9E' | - '\uFF9F'; - -UNICODE_CLASS_LO: - '\u00AA' | - '\u00BA' | - '\u01BB' | - '\u01C0'..'\u01C3' | - '\u0294' | - '\u05D0'..'\u05EA' | - '\u05F0'..'\u05F2' | - '\u0620'..'\u063F' | - '\u0641'..'\u064A' | - '\u066E' | - '\u066F' | - '\u0671'..'\u06D3' | - '\u06D5' | - '\u06EE' | - '\u06EF' | - '\u06FA'..'\u06FC' | - '\u06FF' | - '\u0710' | - '\u0712'..'\u072F' | - '\u074D'..'\u07A5' | - '\u07B1' | - '\u07CA'..'\u07EA' | - '\u0800'..'\u0815' | - '\u0840'..'\u0858' | - '\u08A0' | - '\u08A2'..'\u08AC' | - '\u0904'..'\u0939' | - '\u093D' | - '\u0950' | - '\u0958'..'\u0961' | - '\u0972'..'\u0977' | - '\u0979'..'\u097F' | - '\u0985'..'\u098C' | - '\u098F' | - '\u0990' | - '\u0993'..'\u09A8' | - '\u09AA'..'\u09B0' | - '\u09B2' | - '\u09B6'..'\u09B9' | - '\u09BD' | - '\u09CE' | - '\u09DC' | - '\u09DD' | - '\u09DF'..'\u09E1' | - '\u09F0' | - '\u09F1' | - '\u0A05'..'\u0A0A' | - '\u0A0F' | - '\u0A10' | - '\u0A13'..'\u0A28' | - '\u0A2A'..'\u0A30' | - '\u0A32' | - '\u0A33' | - '\u0A35' | - '\u0A36' | - '\u0A38' | - '\u0A39' | - '\u0A59'..'\u0A5C' | - '\u0A5E' | - '\u0A72'..'\u0A74' | - '\u0A85'..'\u0A8D' | - '\u0A8F'..'\u0A91' | - '\u0A93'..'\u0AA8' | - '\u0AAA'..'\u0AB0' | - '\u0AB2' | - '\u0AB3' | - '\u0AB5'..'\u0AB9' | - '\u0ABD' | - '\u0AD0' | - '\u0AE0' | - '\u0AE1' | - '\u0B05'..'\u0B0C' | - '\u0B0F' | - '\u0B10' | - '\u0B13'..'\u0B28' | - '\u0B2A'..'\u0B30' | - '\u0B32' | - '\u0B33' | - '\u0B35'..'\u0B39' | - '\u0B3D' | - '\u0B5C' | - '\u0B5D' | - '\u0B5F'..'\u0B61' | - '\u0B71' | - '\u0B83' | - '\u0B85'..'\u0B8A' | - '\u0B8E'..'\u0B90' | - '\u0B92'..'\u0B95' | - '\u0B99' | - '\u0B9A' | - '\u0B9C' | - '\u0B9E' | - '\u0B9F' | - '\u0BA3' | - '\u0BA4' | - '\u0BA8'..'\u0BAA' | - '\u0BAE'..'\u0BB9' | - '\u0BD0' | - '\u0C05'..'\u0C0C' | - '\u0C0E'..'\u0C10' | - '\u0C12'..'\u0C28' | - '\u0C2A'..'\u0C33' | - '\u0C35'..'\u0C39' | - '\u0C3D' | - '\u0C58' | - '\u0C59' | - '\u0C60' | - '\u0C61' | - '\u0C85'..'\u0C8C' | - '\u0C8E'..'\u0C90' | - '\u0C92'..'\u0CA8' | - '\u0CAA'..'\u0CB3' | - '\u0CB5'..'\u0CB9' | - '\u0CBD' | - '\u0CDE' | - '\u0CE0' | - '\u0CE1' | - '\u0CF1' | - '\u0CF2' | - '\u0D05'..'\u0D0C' | - '\u0D0E'..'\u0D10' | - '\u0D12'..'\u0D3A' | - '\u0D3D' | - '\u0D4E' | - '\u0D60' | - '\u0D61' | - '\u0D7A'..'\u0D7F' | - '\u0D85'..'\u0D96' | - '\u0D9A'..'\u0DB1' | - '\u0DB3'..'\u0DBB' | - '\u0DBD' | - '\u0DC0'..'\u0DC6' | - '\u0E01'..'\u0E30' | - '\u0E32' | - '\u0E33' | - '\u0E40'..'\u0E45' | - '\u0E81' | - '\u0E82' | - '\u0E84' | - '\u0E87' | - '\u0E88' | - '\u0E8A' | - '\u0E8D' | - '\u0E94'..'\u0E97' | - '\u0E99'..'\u0E9F' | - '\u0EA1'..'\u0EA3' | - '\u0EA5' | - '\u0EA7' | - '\u0EAA' | - '\u0EAB' | - '\u0EAD'..'\u0EB0' | - '\u0EB2' | - '\u0EB3' | - '\u0EBD' | - '\u0EC0'..'\u0EC4' | - '\u0EDC'..'\u0EDF' | - '\u0F00' | - '\u0F40'..'\u0F47' | - '\u0F49'..'\u0F6C' | - '\u0F88'..'\u0F8C' | - '\u1000'..'\u102A' | - '\u103F' | - '\u1050'..'\u1055' | - '\u105A'..'\u105D' | - '\u1061' | - '\u1065' | - '\u1066' | - '\u106E'..'\u1070' | - '\u1075'..'\u1081' | - '\u108E' | - '\u10D0'..'\u10FA' | - '\u10FD'..'\u1248' | - '\u124A'..'\u124D' | - '\u1250'..'\u1256' | - '\u1258' | - '\u125A'..'\u125D' | - '\u1260'..'\u1288' | - '\u128A'..'\u128D' | - '\u1290'..'\u12B0' | - '\u12B2'..'\u12B5' | - '\u12B8'..'\u12BE' | - '\u12C0' | - '\u12C2'..'\u12C5' | - '\u12C8'..'\u12D6' | - '\u12D8'..'\u1310' | - '\u1312'..'\u1315' | - '\u1318'..'\u135A' | - '\u1380'..'\u138F' | - '\u13A0'..'\u13F4' | - '\u1401'..'\u166C' | - '\u166F'..'\u167F' | - '\u1681'..'\u169A' | - '\u16A0'..'\u16EA' | - '\u1700'..'\u170C' | - '\u170E'..'\u1711' | - '\u1720'..'\u1731' | - '\u1740'..'\u1751' | - '\u1760'..'\u176C' | - '\u176E'..'\u1770' | - '\u1780'..'\u17B3' | - '\u17DC' | - '\u1820'..'\u1842' | - '\u1844'..'\u1877' | - '\u1880'..'\u18A8' | - '\u18AA' | - '\u18B0'..'\u18F5' | - '\u1900'..'\u191C' | - '\u1950'..'\u196D' | - '\u1970'..'\u1974' | - '\u1980'..'\u19AB' | - '\u19C1'..'\u19C7' | - '\u1A00'..'\u1A16' | - '\u1A20'..'\u1A54' | - '\u1B05'..'\u1B33' | - '\u1B45'..'\u1B4B' | - '\u1B83'..'\u1BA0' | - '\u1BAE' | - '\u1BAF' | - '\u1BBA'..'\u1BE5' | - '\u1C00'..'\u1C23' | - '\u1C4D'..'\u1C4F' | - '\u1C5A'..'\u1C77' | - '\u1CE9'..'\u1CEC' | - '\u1CEE'..'\u1CF1' | - '\u1CF5' | - '\u1CF6' | - '\u2135'..'\u2138' | - '\u2D30'..'\u2D67' | - '\u2D80'..'\u2D96' | - '\u2DA0'..'\u2DA6' | - '\u2DA8'..'\u2DAE' | - '\u2DB0'..'\u2DB6' | - '\u2DB8'..'\u2DBE' | - '\u2DC0'..'\u2DC6' | - '\u2DC8'..'\u2DCE' | - '\u2DD0'..'\u2DD6' | - '\u2DD8'..'\u2DDE' | - '\u3006' | - '\u303C' | - '\u3041'..'\u3096' | - '\u309F' | - '\u30A1'..'\u30FA' | - '\u30FF' | - '\u3105'..'\u312D' | - '\u3131'..'\u318E' | - '\u31A0'..'\u31BA' | - '\u31F0'..'\u31FF' | - '\u3400' | - '\u4DB5' | - '\u4E00' | - '\u9FCC' | - '\uA000'..'\uA014' | - '\uA016'..'\uA48C' | - '\uA4D0'..'\uA4F7' | - '\uA500'..'\uA60B' | - '\uA610'..'\uA61F' | - '\uA62A' | - '\uA62B' | - '\uA66E' | - '\uA6A0'..'\uA6E5' | - '\uA7FB'..'\uA801' | - '\uA803'..'\uA805' | - '\uA807'..'\uA80A' | - '\uA80C'..'\uA822' | - '\uA840'..'\uA873' | - '\uA882'..'\uA8B3' | - '\uA8F2'..'\uA8F7' | - '\uA8FB' | - '\uA90A'..'\uA925' | - '\uA930'..'\uA946' | - '\uA960'..'\uA97C' | - '\uA984'..'\uA9B2' | - '\uAA00'..'\uAA28' | - '\uAA40'..'\uAA42' | - '\uAA44'..'\uAA4B' | - '\uAA60'..'\uAA6F' | - '\uAA71'..'\uAA76' | - '\uAA7A' | - '\uAA80'..'\uAAAF' | - '\uAAB1' | - '\uAAB5' | - '\uAAB6' | - '\uAAB9'..'\uAABD' | - '\uAAC0' | - '\uAAC2' | - '\uAADB' | - '\uAADC' | - '\uAAE0'..'\uAAEA' | - '\uAAF2' | - '\uAB01'..'\uAB06' | - '\uAB09'..'\uAB0E' | - '\uAB11'..'\uAB16' | - '\uAB20'..'\uAB26' | - '\uAB28'..'\uAB2E' | - '\uABC0'..'\uABE2' | - '\uAC00' | - '\uD7A3' | - '\uD7B0'..'\uD7C6' | - '\uD7CB'..'\uD7FB' | - '\uF900'..'\uFA6D' | - '\uFA70'..'\uFAD9' | - '\uFB1D' | - '\uFB1F'..'\uFB28' | - '\uFB2A'..'\uFB36' | - '\uFB38'..'\uFB3C' | - '\uFB3E' | - '\uFB40' | - '\uFB41' | - '\uFB43' | - '\uFB44' | - '\uFB46'..'\uFBB1' | - '\uFBD3'..'\uFD3D' | - '\uFD50'..'\uFD8F' | - '\uFD92'..'\uFDC7' | - '\uFDF0'..'\uFDFB' | - '\uFE70'..'\uFE74' | - '\uFE76'..'\uFEFC' | - '\uFF66'..'\uFF6F' | - '\uFF71'..'\uFF9D' | - '\uFFA0'..'\uFFBE' | - '\uFFC2'..'\uFFC7' | - '\uFFCA'..'\uFFCF' | - '\uFFD2'..'\uFFD7' | - '\uFFDA'..'\uFFDC'; - -UNICODE_CLASS_LT: - '\u01C5' | - '\u01C8' | - '\u01CB' | - '\u01F2' | - '\u1F88'..'\u1F8F' | - '\u1F98'..'\u1F9F' | - '\u1FA8'..'\u1FAF' | - '\u1FBC' | - '\u1FCC' | - '\u1FFC'; - -UNICODE_CLASS_LU: - '\u0041'..'\u005A' | - '\u00C0'..'\u00D6' | - '\u00D8'..'\u00DE' | - '\u0100' | - '\u0102' | - '\u0104' | - '\u0106' | - '\u0108' | - '\u010A' | - '\u010C' | - '\u010E' | - '\u0110' | - '\u0112' | - '\u0114' | - '\u0116' | - '\u0118' | - '\u011A' | - '\u011C' | - '\u011E' | - '\u0120' | - '\u0122' | - '\u0124' | - '\u0126' | - '\u0128' | - '\u012A' | - '\u012C' | - '\u012E' | - '\u0130' | - '\u0132' | - '\u0134' | - '\u0136' | - '\u0139' | - '\u013B' | - '\u013D' | - '\u013F' | - '\u0141' | - '\u0143' | - '\u0145' | - '\u0147' | - '\u014A' | - '\u014C' | - '\u014E' | - '\u0150' | - '\u0152' | - '\u0154' | - '\u0156' | - '\u0158' | - '\u015A' | - '\u015C' | - '\u015E' | - '\u0160' | - '\u0162' | - '\u0164' | - '\u0166' | - '\u0168' | - '\u016A' | - '\u016C' | - '\u016E' | - '\u0170' | - '\u0172' | - '\u0174' | - '\u0176' | - '\u0178' | - '\u0179' | - '\u017B' | - '\u017D' | - '\u0181' | - '\u0182' | - '\u0184' | - '\u0186' | - '\u0187' | - '\u0189'..'\u018B' | - '\u018E'..'\u0191' | - '\u0193' | - '\u0194' | - '\u0196'..'\u0198' | - '\u019C' | - '\u019D' | - '\u019F' | - '\u01A0' | - '\u01A2' | - '\u01A4' | - '\u01A6' | - '\u01A7' | - '\u01A9' | - '\u01AC' | - '\u01AE' | - '\u01AF' | - '\u01B1'..'\u01B3' | - '\u01B5' | - '\u01B7' | - '\u01B8' | - '\u01BC' | - '\u01C4' | - '\u01C7' | - '\u01CA' | - '\u01CD' | - '\u01CF' | - '\u01D1' | - '\u01D3' | - '\u01D5' | - '\u01D7' | - '\u01D9' | - '\u01DB' | - '\u01DE' | - '\u01E0' | - '\u01E2' | - '\u01E4' | - '\u01E6' | - '\u01E8' | - '\u01EA' | - '\u01EC' | - '\u01EE' | - '\u01F1' | - '\u01F4' | - '\u01F6'..'\u01F8' | - '\u01FA' | - '\u01FC' | - '\u01FE' | - '\u0200' | - '\u0202' | - '\u0204' | - '\u0206' | - '\u0208' | - '\u020A' | - '\u020C' | - '\u020E' | - '\u0210' | - '\u0212' | - '\u0214' | - '\u0216' | - '\u0218' | - '\u021A' | - '\u021C' | - '\u021E' | - '\u0220' | - '\u0222' | - '\u0224' | - '\u0226' | - '\u0228' | - '\u022A' | - '\u022C' | - '\u022E' | - '\u0230' | - '\u0232' | - '\u023A' | - '\u023B' | - '\u023D' | - '\u023E' | - '\u0241' | - '\u0243'..'\u0246' | - '\u0248' | - '\u024A' | - '\u024C' | - '\u024E' | - '\u0370' | - '\u0372' | - '\u0376' | - '\u0386' | - '\u0388'..'\u038A' | - '\u038C' | - '\u038E' | - '\u038F' | - '\u0391'..'\u03A1' | - '\u03A3'..'\u03AB' | - '\u03CF' | - '\u03D2'..'\u03D4' | - '\u03D8' | - '\u03DA' | - '\u03DC' | - '\u03DE' | - '\u03E0' | - '\u03E2' | - '\u03E4' | - '\u03E6' | - '\u03E8' | - '\u03EA' | - '\u03EC' | - '\u03EE' | - '\u03F4' | - '\u03F7' | - '\u03F9' | - '\u03FA' | - '\u03FD'..'\u042F' | - '\u0460' | - '\u0462' | - '\u0464' | - '\u0466' | - '\u0468' | - '\u046A' | - '\u046C' | - '\u046E' | - '\u0470' | - '\u0472' | - '\u0474' | - '\u0476' | - '\u0478' | - '\u047A' | - '\u047C' | - '\u047E' | - '\u0480' | - '\u048A' | - '\u048C' | - '\u048E' | - '\u0490' | - '\u0492' | - '\u0494' | - '\u0496' | - '\u0498' | - '\u049A' | - '\u049C' | - '\u049E' | - '\u04A0' | - '\u04A2' | - '\u04A4' | - '\u04A6' | - '\u04A8' | - '\u04AA' | - '\u04AC' | - '\u04AE' | - '\u04B0' | - '\u04B2' | - '\u04B4' | - '\u04B6' | - '\u04B8' | - '\u04BA' | - '\u04BC' | - '\u04BE' | - '\u04C0' | - '\u04C1' | - '\u04C3' | - '\u04C5' | - '\u04C7' | - '\u04C9' | - '\u04CB' | - '\u04CD' | - '\u04D0' | - '\u04D2' | - '\u04D4' | - '\u04D6' | - '\u04D8' | - '\u04DA' | - '\u04DC' | - '\u04DE' | - '\u04E0' | - '\u04E2' | - '\u04E4' | - '\u04E6' | - '\u04E8' | - '\u04EA' | - '\u04EC' | - '\u04EE' | - '\u04F0' | - '\u04F2' | - '\u04F4' | - '\u04F6' | - '\u04F8' | - '\u04FA' | - '\u04FC' | - '\u04FE' | - '\u0500' | - '\u0502' | - '\u0504' | - '\u0506' | - '\u0508' | - '\u050A' | - '\u050C' | - '\u050E' | - '\u0510' | - '\u0512' | - '\u0514' | - '\u0516' | - '\u0518' | - '\u051A' | - '\u051C' | - '\u051E' | - '\u0520' | - '\u0522' | - '\u0524' | - '\u0526' | - '\u0531'..'\u0556' | - '\u10A0'..'\u10C5' | - '\u10C7' | - '\u10CD' | - '\u1E00' | - '\u1E02' | - '\u1E04' | - '\u1E06' | - '\u1E08' | - '\u1E0A' | - '\u1E0C' | - '\u1E0E' | - '\u1E10' | - '\u1E12' | - '\u1E14' | - '\u1E16' | - '\u1E18' | - '\u1E1A' | - '\u1E1C' | - '\u1E1E' | - '\u1E20' | - '\u1E22' | - '\u1E24' | - '\u1E26' | - '\u1E28' | - '\u1E2A' | - '\u1E2C' | - '\u1E2E' | - '\u1E30' | - '\u1E32' | - '\u1E34' | - '\u1E36' | - '\u1E38' | - '\u1E3A' | - '\u1E3C' | - '\u1E3E' | - '\u1E40' | - '\u1E42' | - '\u1E44' | - '\u1E46' | - '\u1E48' | - '\u1E4A' | - '\u1E4C' | - '\u1E4E' | - '\u1E50' | - '\u1E52' | - '\u1E54' | - '\u1E56' | - '\u1E58' | - '\u1E5A' | - '\u1E5C' | - '\u1E5E' | - '\u1E60' | - '\u1E62' | - '\u1E64' | - '\u1E66' | - '\u1E68' | - '\u1E6A' | - '\u1E6C' | - '\u1E6E' | - '\u1E70' | - '\u1E72' | - '\u1E74' | - '\u1E76' | - '\u1E78' | - '\u1E7A' | - '\u1E7C' | - '\u1E7E' | - '\u1E80' | - '\u1E82' | - '\u1E84' | - '\u1E86' | - '\u1E88' | - '\u1E8A' | - '\u1E8C' | - '\u1E8E' | - '\u1E90' | - '\u1E92' | - '\u1E94' | - '\u1E9E' | - '\u1EA0' | - '\u1EA2' | - '\u1EA4' | - '\u1EA6' | - '\u1EA8' | - '\u1EAA' | - '\u1EAC' | - '\u1EAE' | - '\u1EB0' | - '\u1EB2' | - '\u1EB4' | - '\u1EB6' | - '\u1EB8' | - '\u1EBA' | - '\u1EBC' | - '\u1EBE' | - '\u1EC0' | - '\u1EC2' | - '\u1EC4' | - '\u1EC6' | - '\u1EC8' | - '\u1ECA' | - '\u1ECC' | - '\u1ECE' | - '\u1ED0' | - '\u1ED2' | - '\u1ED4' | - '\u1ED6' | - '\u1ED8' | - '\u1EDA' | - '\u1EDC' | - '\u1EDE' | - '\u1EE0' | - '\u1EE2' | - '\u1EE4' | - '\u1EE6' | - '\u1EE8' | - '\u1EEA' | - '\u1EEC' | - '\u1EEE' | - '\u1EF0' | - '\u1EF2' | - '\u1EF4' | - '\u1EF6' | - '\u1EF8' | - '\u1EFA' | - '\u1EFC' | - '\u1EFE' | - '\u1F08'..'\u1F0F' | - '\u1F18'..'\u1F1D' | - '\u1F28'..'\u1F2F' | - '\u1F38'..'\u1F3F' | - '\u1F48'..'\u1F4D' | - '\u1F59' | - '\u1F5B' | - '\u1F5D' | - '\u1F5F' | - '\u1F68'..'\u1F6F' | - '\u1FB8'..'\u1FBB' | - '\u1FC8'..'\u1FCB' | - '\u1FD8'..'\u1FDB' | - '\u1FE8'..'\u1FEC' | - '\u1FF8'..'\u1FFB' | - '\u2102' | - '\u2107' | - '\u210B'..'\u210D' | - '\u2110'..'\u2112' | - '\u2115' | - '\u2119'..'\u211D' | - '\u2124' | - '\u2126' | - '\u2128' | - '\u212A'..'\u212D' | - '\u2130'..'\u2133' | - '\u213E' | - '\u213F' | - '\u2145' | - '\u2183' | - '\u2C00'..'\u2C2E' | - '\u2C60' | - '\u2C62'..'\u2C64' | - '\u2C67' | - '\u2C69' | - '\u2C6B' | - '\u2C6D'..'\u2C70' | - '\u2C72' | - '\u2C75' | - '\u2C7E'..'\u2C80' | - '\u2C82' | - '\u2C84' | - '\u2C86' | - '\u2C88' | - '\u2C8A' | - '\u2C8C' | - '\u2C8E' | - '\u2C90' | - '\u2C92' | - '\u2C94' | - '\u2C96' | - '\u2C98' | - '\u2C9A' | - '\u2C9C' | - '\u2C9E' | - '\u2CA0' | - '\u2CA2' | - '\u2CA4' | - '\u2CA6' | - '\u2CA8' | - '\u2CAA' | - '\u2CAC' | - '\u2CAE' | - '\u2CB0' | - '\u2CB2' | - '\u2CB4' | - '\u2CB6' | - '\u2CB8' | - '\u2CBA' | - '\u2CBC' | - '\u2CBE' | - '\u2CC0' | - '\u2CC2' | - '\u2CC4' | - '\u2CC6' | - '\u2CC8' | - '\u2CCA' | - '\u2CCC' | - '\u2CCE' | - '\u2CD0' | - '\u2CD2' | - '\u2CD4' | - '\u2CD6' | - '\u2CD8' | - '\u2CDA' | - '\u2CDC' | - '\u2CDE' | - '\u2CE0' | - '\u2CE2' | - '\u2CEB' | - '\u2CED' | - '\u2CF2' | - '\uA640' | - '\uA642' | - '\uA644' | - '\uA646' | - '\uA648' | - '\uA64A' | - '\uA64C' | - '\uA64E' | - '\uA650' | - '\uA652' | - '\uA654' | - '\uA656' | - '\uA658' | - '\uA65A' | - '\uA65C' | - '\uA65E' | - '\uA660' | - '\uA662' | - '\uA664' | - '\uA666' | - '\uA668' | - '\uA66A' | - '\uA66C' | - '\uA680' | - '\uA682' | - '\uA684' | - '\uA686' | - '\uA688' | - '\uA68A' | - '\uA68C' | - '\uA68E' | - '\uA690' | - '\uA692' | - '\uA694' | - '\uA696' | - '\uA722' | - '\uA724' | - '\uA726' | - '\uA728' | - '\uA72A' | - '\uA72C' | - '\uA72E' | - '\uA732' | - '\uA734' | - '\uA736' | - '\uA738' | - '\uA73A' | - '\uA73C' | - '\uA73E' | - '\uA740' | - '\uA742' | - '\uA744' | - '\uA746' | - '\uA748' | - '\uA74A' | - '\uA74C' | - '\uA74E' | - '\uA750' | - '\uA752' | - '\uA754' | - '\uA756' | - '\uA758' | - '\uA75A' | - '\uA75C' | - '\uA75E' | - '\uA760' | - '\uA762' | - '\uA764' | - '\uA766' | - '\uA768' | - '\uA76A' | - '\uA76C' | - '\uA76E' | - '\uA779' | - '\uA77B' | - '\uA77D' | - '\uA77E' | - '\uA780' | - '\uA782' | - '\uA784' | - '\uA786' | - '\uA78B' | - '\uA78D' | - '\uA790' | - '\uA792' | - '\uA7A0' | - '\uA7A2' | - '\uA7A4' | - '\uA7A6' | - '\uA7A8' | - '\uA7AA' | - '\uFF21'..'\uFF3A'; - -UNICODE_CLASS_ND: - '\u0030'..'\u0039' | - '\u0660'..'\u0669' | - '\u06F0'..'\u06F9' | - '\u07C0'..'\u07C9' | - '\u0966'..'\u096F' | - '\u09E6'..'\u09EF' | - '\u0A66'..'\u0A6F' | - '\u0AE6'..'\u0AEF' | - '\u0B66'..'\u0B6F' | - '\u0BE6'..'\u0BEF' | - '\u0C66'..'\u0C6F' | - '\u0CE6'..'\u0CEF' | - '\u0D66'..'\u0D6F' | - '\u0E50'..'\u0E59' | - '\u0ED0'..'\u0ED9' | - '\u0F20'..'\u0F29' | - '\u1040'..'\u1049' | - '\u1090'..'\u1099' | - '\u17E0'..'\u17E9' | - '\u1810'..'\u1819' | - '\u1946'..'\u194F' | - '\u19D0'..'\u19D9' | - '\u1A80'..'\u1A89' | - '\u1A90'..'\u1A99' | - '\u1B50'..'\u1B59' | - '\u1BB0'..'\u1BB9' | - '\u1C40'..'\u1C49' | - '\u1C50'..'\u1C59' | - '\uA620'..'\uA629' | - '\uA8D0'..'\uA8D9' | - '\uA900'..'\uA909' | - '\uA9D0'..'\uA9D9' | - '\uAA50'..'\uAA59' | - '\uABF0'..'\uABF9' | - '\uFF10'..'\uFF19'; - -UNICODE_CLASS_NL: - '\u16EE'..'\u16F0' | - '\u2160'..'\u2182' | - '\u2185'..'\u2188' | - '\u3007' | - '\u3021'..'\u3029' | - '\u3038'..'\u303A' | - '\uA6E6'..'\uA6EF'; +// SECTION: lexicalGeneral ShebangLine - : '#!' ~[\u000A\u000D]* - -> channel(HIDDEN) + : '#!' ~[\r\n]* ; DelimitedComment @@ -1671,154 +19,176 @@ DelimitedComment ; LineComment - : '//' ~[\u000A\u000D]* + : '//' ~[\r\n]* -> channel(HIDDEN) ; WS : [\u0020\u0009\u000C] - -> skip + -> channel(HIDDEN) ; -NL: '\u000A' | '\u000D' '\u000A' ; +NL: '\n' | '\r' '\n'?; -//SEPARATORS & OPERATIONS +fragment Hidden: DelimitedComment | LineComment | WS; -RESERVED: '...' ; -DOT: '.' ; -COMMA: ',' ; -LPAREN: '(' -> pushMode(Inside) ; -RPAREN: ')' ; -LSQUARE: '[' -> pushMode(Inside) ; -RSQUARE: ']' ; -LCURL: '{' ; -RCURL: '}' ; -MULT: '*' ; -MOD: '%' ; -DIV: '/' ; -ADD: '+' ; -SUB: '-' ; -INCR: '++' ; -DECR: '--' ; -CONJ: '&&' ; -DISJ: '||' ; -EXCL: '!' ; -COLON: ':' ; -SEMICOLON: ';' ; -ASSIGNMENT: '=' ; -ADD_ASSIGNMENT: '+=' ; -SUB_ASSIGNMENT: '-=' ; -MULT_ASSIGNMENT: '*=' ; -DIV_ASSIGNMENT: '/=' ; -MOD_ASSIGNMENT: '%=' ; -ARROW: '->' ; -DOUBLE_ARROW: '=>' ; -RANGE: '..' ; -COLONCOLON: '::' ; -Q_COLONCOLON: '?::' ; -DOUBLE_SEMICOLON: ';;' ; -HASH: '#' ; -AT: '@' ; -QUEST: '?' ; -ELVIS: '?:' ; -LANGLE: '<' ; -RANGLE: '>' ; -LE: '<=' ; -GE: '>=' ; -EXCL_EQ: '!=' ; -EXCL_EQEQ: '!==' ; -AS_SAFE: 'as?' ; -EQEQ: '==' ; -EQEQEQ: '===' ; -SINGLE_QUOTE: '\'' ; +// SECTION: separatorsAndOperations -//KEYWORDS +RESERVED: '...'; +DOT: '.'; +COMMA: ','; +LPAREN: '(' -> pushMode(Inside); +RPAREN: ')'; +LSQUARE: '[' -> pushMode(Inside); +RSQUARE: ']'; +LCURL: '{' -> pushMode(DEFAULT_MODE); +/* + * When using another programming language (not Java) to generate a parser, + * please replace this code with the corresponding code of a programming language you are using. + */ +RCURL: '}' { if (!_modeStack.isEmpty()) { popMode(); } }; +MULT: '*'; +MOD: '%'; +DIV: '/'; +ADD: '+'; +SUB: '-'; +INCR: '++'; +DECR: '--'; +CONJ: '&&'; +DISJ: '||'; +EXCL_WS: '!' Hidden; +EXCL_NO_WS: '!'; +COLON: ':'; +SEMICOLON: ';'; +ASSIGNMENT: '='; +ADD_ASSIGNMENT: '+='; +SUB_ASSIGNMENT: '-='; +MULT_ASSIGNMENT: '*='; +DIV_ASSIGNMENT: '/='; +MOD_ASSIGNMENT: '%='; +ARROW: '->'; +DOUBLE_ARROW: '=>'; +RANGE: '..'; +COLONCOLON: '::'; +DOUBLE_SEMICOLON: ';;'; +HASH: '#'; +AT_NO_WS: '@'; +AT_POST_WS: '@' (Hidden | NL); +AT_PRE_WS: (Hidden | NL) '@' ; +AT_BOTH_WS: (Hidden | NL) '@' (Hidden | NL); +QUEST_WS: '?' Hidden; +QUEST_NO_WS: '?'; +LANGLE: '<'; +RANGLE: '>'; +LE: '<='; +GE: '>='; +EXCL_EQ: '!='; +EXCL_EQEQ: '!=='; +AS_SAFE: 'as?'; +EQEQ: '=='; +EQEQEQ: '==='; +SINGLE_QUOTE: '\''; -RETURN_AT: 'return@' Identifier ; -CONTINUE_AT: 'continue@' Identifier ; -BREAK_AT: 'break@' Identifier ; +// SECTION: keywords -FILE: '@file' ; -PACKAGE: 'package' ; -IMPORT: 'import' ; -CLASS: 'class' ; -INTERFACE: 'interface' ; -FUN: 'fun' ; -OBJECT: 'object' ; -VAL: 'val' ; -VAR: 'var' ; -TYPE_ALIAS: 'typealias' ; -CONSTRUCTOR: 'constructor' ; -BY: 'by' ; -COMPANION: 'companion' ; -INIT: 'init' ; -THIS: 'this' ; -SUPER: 'super' ; -TYPEOF: 'typeof' ; -WHERE: 'where' ; -IF: 'if' ; -ELSE: 'else' ; -WHEN: 'when' ; -TRY: 'try' ; -CATCH: 'catch' ; -FINALLY: 'finally' ; -FOR: 'for' ; -DO: 'do' ; -WHILE: 'while' ; -THROW: 'throw' ; -RETURN: 'return' ; -CONTINUE: 'continue' ; -BREAK: 'break' ; -AS: 'as' ; -IS: 'is' ; -IN: 'in' ; -NOT_IS: '!is' (WS | NL)+ ; -NOT_IN: '!in' (WS | NL)+ ; -OUT: 'out' ; -FIELD: '@field' ; -PROPERTY: '@property' ; -GET: '@get' ; -SET: '@set' ; -GETTER: 'get' ; -SETTER: 'set' ; -RECEIVER: '@receiver' ; -PARAM: '@param' ; -SETPARAM: '@setparam' ; -DELEGATE: '@delegate' ; -DYNAMIC: 'dynamic' ; +RETURN_AT: 'return@' Identifier; +CONTINUE_AT: 'continue@' Identifier; +BREAK_AT: 'break@' Identifier; -//MODIFIERS +THIS_AT: 'this@' Identifier; +SUPER_AT: 'super@' Identifier; -PUBLIC: 'public' ; -PRIVATE: 'private' ; -PROTECTED: 'protected' ; -INTERNAL: 'internal' ; -ENUM: 'enum' ; -SEALED: 'sealed' ; -ANNOTATION: 'annotation' ; -DATA: 'data' ; -INNER: 'inner' ; -TAILREC: 'tailrec' ; -OPERATOR: 'operator' ; -INLINE: 'inline' ; -INFIX: 'infix' ; -EXTERNAL: 'external' ; -SUSPEND: 'suspend' ; -OVERRIDE: 'override' ; -ABSTRACT: 'abstract' ; -FINAL: 'final' ; -OPEN: 'open' ; -CONST: 'const' ; -LATEINIT: 'lateinit' ; -VARARG: 'vararg' ; -NOINLINE: 'noinline' ; -CROSSINLINE: 'crossinline' ; -REIFIED: 'reified' ; +FILE: 'file'; +FIELD: 'field'; +PROPERTY: 'property'; +GET: 'get'; +SET: 'set'; +RECEIVER: 'receiver'; +PARAM: 'param'; +SETPARAM: 'setparam'; +DELEGATE: 'delegate'; -// +PACKAGE: 'package'; +IMPORT: 'import'; +CLASS: 'class'; +INTERFACE: 'interface'; +FUN: 'fun'; +OBJECT: 'object'; +VAL: 'val'; +VAR: 'var'; +TYPE_ALIAS: 'typealias'; +CONSTRUCTOR: 'constructor'; +BY: 'by'; +COMPANION: 'companion'; +INIT: 'init'; +THIS: 'this'; +SUPER: 'super'; +TYPEOF: 'typeof'; +WHERE: 'where'; +IF: 'if'; +ELSE: 'else'; +WHEN: 'when'; +TRY: 'try'; +CATCH: 'catch'; +FINALLY: 'finally'; +FOR: 'for'; +DO: 'do'; +WHILE: 'while'; +THROW: 'throw'; +RETURN: 'return'; +CONTINUE: 'continue'; +BREAK: 'break'; +AS: 'as'; +IS: 'is'; +IN: 'in'; +NOT_IS: '!is' (Hidden | NL); +NOT_IN: '!in' (Hidden | NL); +OUT: 'out'; +DYNAMIC: 'dynamic'; -QUOTE_OPEN: '"' -> pushMode(LineString) ; -TRIPLE_QUOTE_OPEN: '"""' -> pushMode(MultiLineString) ; +// SECTION: lexicalModifiers + +PUBLIC: 'public'; +PRIVATE: 'private'; +PROTECTED: 'protected'; +INTERNAL: 'internal'; +ENUM: 'enum'; +SEALED: 'sealed'; +ANNOTATION: 'annotation'; +DATA: 'data'; +INNER: 'inner'; +VALUE: 'value'; +TAILREC: 'tailrec'; +OPERATOR: 'operator'; +INLINE: 'inline'; +INFIX: 'infix'; +EXTERNAL: 'external'; +SUSPEND: 'suspend'; +OVERRIDE: 'override'; +ABSTRACT: 'abstract'; +FINAL: 'final'; +OPEN: 'open'; +CONST: 'const'; +LATEINIT: 'lateinit'; +VARARG: 'vararg'; +NOINLINE: 'noinline'; +CROSSINLINE: 'crossinline'; +REIFIED: 'reified'; +EXPECT: 'expect'; +ACTUAL: 'actual'; + +// SECTION: literals + +fragment DecDigit: '0'..'9'; +fragment DecDigitNoZero: '1'..'9'; +fragment DecDigitOrSeparator: DecDigit | '_'; + +fragment DecDigits + : DecDigit DecDigitOrSeparator* DecDigit + | DecDigit + ; + +fragment DoubleExponent: [eE] [+-]? DecDigits; RealLiteral : FloatLiteral @@ -1826,133 +196,116 @@ RealLiteral ; FloatLiteral - : (DoubleLiteral | IntegerLiteral) [fF] + : DoubleLiteral [fF] + | DecDigits [fF] ; DoubleLiteral - : ( (DecDigitNoZero DecDigit*)? '.' - | (DecDigitNoZero (DecDigit | '_')* DecDigit)? '.') - ( DecDigit+ - | DecDigit (DecDigit | '_')+ DecDigit - | DecDigit+ [eE] ('+' | '-')? DecDigit+ - | DecDigit+ [eE] ('+' | '-')? DecDigit (DecDigit | '_')+ DecDigit - | DecDigit (DecDigit | '_')+ DecDigit [eE] ('+' | '-')? DecDigit+ - | DecDigit (DecDigit | '_')+ DecDigit [eE] ('+' | '-')? DecDigit (DecDigit | '_')+ DecDigit - ) - ; - -LongLiteral - : (IntegerLiteral | HexLiteral | BinLiteral) 'L' + : DecDigits? '.' DecDigits DoubleExponent? + | DecDigits DoubleExponent ; IntegerLiteral - : ('0' - | DecDigitNoZero DecDigit* - | DecDigitNoZero (DecDigit | '_')+ DecDigit - | DecDigitNoZero DecDigit* [eE] ('+' | '-')? DecDigit+ - | DecDigitNoZero DecDigit* [eE] ('+' | '-')? DecDigit (DecDigit | '_')+ DecDigit - | DecDigitNoZero (DecDigit | '_')+ DecDigit [eE] ('+' | '-')? DecDigit+ - | DecDigitNoZero (DecDigit | '_')+ DecDigit [eE] ('+' | '-')? DecDigit (DecDigit | '_')+ DecDigit - ) + : DecDigitNoZero DecDigitOrSeparator* DecDigit + | DecDigit ; -fragment DecDigit - : UNICODE_CLASS_ND - ; - -fragment DecDigitNoZero - : UNICODE_CLASS_ND_NoZeros - ; - -fragment UNICODE_CLASS_ND_NoZeros - : '\u0031'..'\u0039' - | '\u0661'..'\u0669' - | '\u06f1'..'\u06f9' - | '\u07c1'..'\u07c9' - | '\u0967'..'\u096f' - | '\u09e7'..'\u09ef' - | '\u0a67'..'\u0a6f' - | '\u0ae7'..'\u0aef' - | '\u0b67'..'\u0b6f' - | '\u0be7'..'\u0bef' - | '\u0c67'..'\u0c6f' - | '\u0ce7'..'\u0cef' - | '\u0d67'..'\u0d6f' - | '\u0de7'..'\u0def' - | '\u0e51'..'\u0e59' - | '\u0ed1'..'\u0ed9' - | '\u0f21'..'\u0f29' - | '\u1041'..'\u1049' - | '\u1091'..'\u1099' - | '\u17e1'..'\u17e9' - | '\u1811'..'\u1819' - | '\u1947'..'\u194f' - | '\u19d1'..'\u19d9' - | '\u1a81'..'\u1a89' - | '\u1a91'..'\u1a99' - | '\u1b51'..'\u1b59' - | '\u1bb1'..'\u1bb9' - | '\u1c41'..'\u1c49' - | '\u1c51'..'\u1c59' - | '\ua621'..'\ua629' - | '\ua8d1'..'\ua8d9' - | '\ua901'..'\ua909' - | '\ua9d1'..'\ua9d9' - | '\ua9f1'..'\ua9f9' - | '\uaa51'..'\uaa59' - | '\uabf1'..'\uabf9' - | '\uff11'..'\uff19' - ; +fragment HexDigit: [0-9a-fA-F]; +fragment HexDigitOrSeparator: HexDigit | '_'; HexLiteral - : '0' [xX] HexDigit (HexDigit | '_')* + : '0' [xX] HexDigit HexDigitOrSeparator* HexDigit + | '0' [xX] HexDigit ; -fragment HexDigit - : [0-9a-fA-F] - ; +fragment BinDigit: [01]; +fragment BinDigitOrSeparator: BinDigit | '_'; BinLiteral - : '0' [bB] BinDigit (BinDigit | '_')* + : '0' [bB] BinDigit BinDigitOrSeparator* BinDigit + | '0' [bB] BinDigit ; -fragment BinDigit - : [01] +UnsignedLiteral + : (IntegerLiteral | HexLiteral | BinLiteral) [uU] [lL]? ; -BooleanLiteral - : 'true' - | 'false' +LongLiteral + : (IntegerLiteral | HexLiteral | BinLiteral) [lL] ; -NullLiteral - : 'null' +BooleanLiteral: 'true'| 'false'; + +NullLiteral: 'null'; + +CharacterLiteral + : '\'' (EscapeSeq | ~[\n\r'\\]) '\'' ; +// SECTION: lexicalIdentifiers + +fragment UnicodeDigit: UNICODE_CLASS_ND; + Identifier - : (Letter | '_') (Letter | '_' | DecDigit)* - | '`' ~('`')+ '`' + : (Letter | '_') (Letter | '_' | UnicodeDigit)* + | '`' ~([\r\n] | '`')+ '`' ; -LabelReference - : '@' Identifier - ; - -LabelDefinition - : Identifier '@' +IdentifierOrSoftKey + : Identifier + /* Soft keywords */ + | ABSTRACT + | ANNOTATION + | BY + | CATCH + | COMPANION + | CONSTRUCTOR + | CROSSINLINE + | DATA + | DYNAMIC + | ENUM + | EXTERNAL + | FINAL + | FINALLY + | IMPORT + | INFIX + | INIT + | INLINE + | INNER + | INTERNAL + | LATEINIT + | NOINLINE + | OPEN + | OPERATOR + | OUT + | OVERRIDE + | PRIVATE + | PROTECTED + | PUBLIC + | REIFIED + | SEALED + | TAILREC + | VARARG + | WHERE + | GET + | SET + | FIELD + | PROPERTY + | RECEIVER + | PARAM + | SETPARAM + | DELEGATE + | FILE + | EXPECT + | ACTUAL + | VALUE + /* Strong keywords */ + | CONST + | SUSPEND ; FieldIdentifier - : '$' Identifier - ; - -CharacterLiteral - : '\'' (EscapeSeq | .) '\'' - ; - -fragment EscapeSeq - : UniCharacterLiteral - | EscapedIdentifier + : '$' IdentifierOrSoftKey ; fragment UniCharacterLiteral @@ -1963,149 +316,28 @@ fragment EscapedIdentifier : '\\' ('t' | 'b' | 'r' | 'n' | '\'' | '"' | '\\' | '$') ; -fragment Letter - : UNICODE_CLASS_LL - | UNICODE_CLASS_LM - | UNICODE_CLASS_LO - | UNICODE_CLASS_LT - | UNICODE_CLASS_LU - | UNICODE_CLASS_NL +fragment EscapeSeq + : UniCharacterLiteral + | EscapedIdentifier ; +// SECTION: characters -mode Inside ; +fragment Letter + : UNICODE_CLASS_LU + | UNICODE_CLASS_LL + | UNICODE_CLASS_LT + | UNICODE_CLASS_LM + | UNICODE_CLASS_LO + ; -Inside_RPAREN: ')' -> popMode, type(RPAREN) ; -Inside_RSQUARE: ']' -> popMode, type(RSQUARE); +// SECTION: strings -Inside_LPAREN: LPAREN -> pushMode(Inside), type(LPAREN) ; -Inside_LSQUARE: LSQUARE -> pushMode(Inside), type(LSQUARE) ; +QUOTE_OPEN: '"' -> pushMode(LineString); -Inside_LCURL: LCURL -> type(LCURL) ; -Inside_RCURL: RCURL -> type(RCURL) ; -Inside_DOT: DOT -> type(DOT) ; -Inside_COMMA: COMMA -> type(COMMA) ; -Inside_MULT: MULT -> type(MULT) ; -Inside_MOD: MOD -> type(MOD) ; -Inside_DIV: DIV -> type(DIV) ; -Inside_ADD: ADD -> type(ADD) ; -Inside_SUB: SUB -> type(SUB) ; -Inside_INCR: INCR -> type(INCR) ; -Inside_DECR: DECR -> type(DECR) ; -Inside_CONJ: CONJ -> type(CONJ) ; -Inside_DISJ: DISJ -> type(DISJ) ; -Inside_EXCL: EXCL -> type(EXCL) ; -Inside_COLON: COLON -> type(COLON) ; -Inside_SEMICOLON: SEMICOLON -> type(SEMICOLON) ; -Inside_ASSIGNMENT: ASSIGNMENT -> type(ASSIGNMENT) ; -Inside_ADD_ASSIGNMENT: ADD_ASSIGNMENT -> type(ADD_ASSIGNMENT) ; -Inside_SUB_ASSIGNMENT: SUB_ASSIGNMENT -> type(SUB_ASSIGNMENT) ; -Inside_MULT_ASSIGNMENT: MULT_ASSIGNMENT -> type(MULT_ASSIGNMENT) ; -Inside_DIV_ASSIGNMENT: DIV_ASSIGNMENT -> type(DIV_ASSIGNMENT) ; -Inside_MOD_ASSIGNMENT: MOD_ASSIGNMENT -> type(MOD_ASSIGNMENT) ; -Inside_ARROW: ARROW -> type(ARROW) ; -Inside_DOUBLE_ARROW: DOUBLE_ARROW -> type(DOUBLE_ARROW) ; -Inside_RANGE: RANGE -> type(RANGE) ; -Inside_RESERVED: RESERVED -> type(RESERVED) ; -Inside_COLONCOLON: COLONCOLON -> type(COLONCOLON) ; -Inside_Q_COLONCOLON: Q_COLONCOLON -> type(Q_COLONCOLON) ; -Inside_DOUBLE_SEMICOLON: DOUBLE_SEMICOLON -> type(DOUBLE_SEMICOLON) ; -Inside_HASH: HASH -> type(HASH) ; -Inside_AT: AT -> type(AT) ; -Inside_QUEST: QUEST -> type(QUEST) ; -Inside_ELVIS: ELVIS -> type(ELVIS) ; -Inside_LANGLE: LANGLE -> type(LANGLE) ; -Inside_RANGLE: RANGLE -> type(RANGLE) ; -Inside_LE: LE -> type(LE) ; -Inside_GE: GE -> type(GE) ; -Inside_EXCL_EQ: EXCL_EQ -> type(EXCL_EQ) ; -Inside_EXCL_EQEQ: EXCL_EQEQ -> type(EXCL_EQEQ) ; -Inside_NOT_IS: NOT_IS -> type(NOT_IS) ; -Inside_NOT_IN: NOT_IN -> type(NOT_IN) ; -Inside_AS_SAFE: AS_SAFE -> type(AS_SAFE) ; -Inside_EQEQ: EQEQ -> type(EQEQ) ; -Inside_EQEQEQ: EQEQEQ -> type(EQEQEQ) ; -Inside_SINGLE_QUOTE: SINGLE_QUOTE -> type(SINGLE_QUOTE) ; -Inside_QUOTE_OPEN: QUOTE_OPEN -> pushMode(LineString), type(QUOTE_OPEN) ; -Inside_TRIPLE_QUOTE_OPEN: TRIPLE_QUOTE_OPEN -> pushMode(MultiLineString), type(TRIPLE_QUOTE_OPEN) ; +TRIPLE_QUOTE_OPEN: '"""' -> pushMode(MultiLineString); -Inside_VAL: VAL -> type(VAL) ; -Inside_VAR: VAR -> type(VAR) ; -Inside_OBJECT: OBJECT -> type(OBJECT) ; -Inside_SUPER: SUPER -> type(SUPER) ; -Inside_IN: IN -> type(IN) ; -Inside_OUT: OUT -> type(OUT) ; -Inside_FIELD: FIELD -> type(FIELD) ; -Inside_FILE: FILE -> type(FILE) ; -Inside_PROPERTY: PROPERTY -> type(PROPERTY) ; -Inside_GET: GET -> type(GET) ; -Inside_SET: SET -> type(SET) ; -Inside_RECEIVER: RECEIVER -> type(RECEIVER) ; -Inside_PARAM: PARAM -> type(PARAM) ; -Inside_SETPARAM: SETPARAM -> type(SETPARAM) ; -Inside_DELEGATE: DELEGATE -> type(DELEGATE) ; -Inside_THROW: THROW -> type(THROW) ; -Inside_RETURN: RETURN -> type(RETURN) ; -Inside_CONTINUE: CONTINUE -> type(CONTINUE) ; -Inside_BREAK: BREAK -> type(BREAK) ; -Inside_RETURN_AT: RETURN_AT -> type(RETURN_AT) ; -Inside_CONTINUE_AT: CONTINUE_AT -> type(CONTINUE_AT) ; -Inside_BREAK_AT: BREAK_AT -> type(BREAK_AT) ; -Inside_IF: IF -> type(IF) ; -Inside_ELSE: ELSE -> type(ELSE) ; -Inside_WHEN: WHEN -> type(WHEN) ; -Inside_TRY: TRY -> type(TRY) ; -Inside_CATCH: CATCH -> type(CATCH) ; -Inside_FINALLY: FINALLY -> type(FINALLY) ; -Inside_FOR: FOR -> type(FOR) ; -Inside_DO: DO -> type(DO) ; -Inside_WHILE: WHILE -> type(WHILE) ; - -Inside_PUBLIC: PUBLIC -> type(PUBLIC) ; -Inside_PRIVATE: PRIVATE -> type(PRIVATE) ; -Inside_PROTECTED: PROTECTED -> type(PROTECTED) ; -Inside_INTERNAL: INTERNAL -> type(INTERNAL) ; -Inside_ENUM: ENUM -> type(ENUM) ; -Inside_SEALED: SEALED -> type(SEALED) ; -Inside_ANNOTATION: ANNOTATION -> type(ANNOTATION) ; -Inside_DATA: DATA -> type(DATA) ; -Inside_INNER: INNER -> type(INNER) ; -Inside_TAILREC: TAILREC -> type(TAILREC) ; -Inside_OPERATOR: OPERATOR -> type(OPERATOR) ; -Inside_INLINE: INLINE -> type(INLINE) ; -Inside_INFIX: INFIX -> type(INFIX) ; -Inside_EXTERNAL: EXTERNAL -> type(EXTERNAL) ; -Inside_SUSPEND: SUSPEND -> type(SUSPEND) ; -Inside_OVERRIDE: OVERRIDE -> type(OVERRIDE) ; -Inside_ABSTRACT: ABSTRACT -> type(ABSTRACT) ; -Inside_FINAL: FINAL -> type(FINAL) ; -Inside_OPEN: OPEN -> type(OPEN) ; -Inside_CONST: CONST -> type(CONST) ; -Inside_LATEINIT: LATEINIT -> type(LATEINIT) ; -Inside_VARARG: VARARG -> type(VARARG) ; -Inside_NOINLINE: NOINLINE -> type(NOINLINE) ; -Inside_CROSSINLINE: CROSSINLINE -> type(CROSSINLINE) ; -Inside_REIFIED: REIFIED -> type(REIFIED) ; - -Inside_BooleanLiteral: BooleanLiteral -> type(BooleanLiteral) ; -Inside_IntegerLiteral: IntegerLiteral -> type(IntegerLiteral) ; -Inside_HexLiteral: HexLiteral -> type(HexLiteral) ; -Inside_BinLiteral: BinLiteral -> type(BinLiteral) ; -Inside_CharacterLiteral: CharacterLiteral -> type(CharacterLiteral) ; -Inside_RealLiteral: RealLiteral -> type(RealLiteral) ; -Inside_NullLiteral: NullLiteral -> type(NullLiteral) ; - -Inside_LongLiteral: LongLiteral -> type(LongLiteral) ; - -Inside_Identifier: Identifier -> type(Identifier) ; -Inside_LabelReference: LabelReference -> type(LabelReference) ; -Inside_LabelDefinition: LabelDefinition -> type(LabelDefinition) ; -Inside_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN) ; -Inside_WS: WS -> skip ; -Inside_NL: NL -> skip ; - - -mode LineString ; +mode LineString; QUOTE_CLOSE : '"' -> popMode @@ -2120,16 +352,15 @@ LineStrText ; LineStrEscapedChar - : '\\' . + : EscapedIdentifier | UniCharacterLiteral ; LineStrExprStart - : '${' -> pushMode(StringExpression) + : '${' -> pushMode(DEFAULT_MODE) ; - -mode MultiLineString ; +mode MultiLineString; TRIPLE_QUOTE_CLOSE : MultiLineStringQuote? '"""' -> popMode @@ -2144,90 +375,152 @@ MultiLineStrRef ; MultiLineStrText - : ~('\\' | '"' | '$')+ | '$' - ; - -MultiLineStrEscapedChar - : '\\' . + : ~('"' | '$')+ | '$' ; MultiLineStrExprStart - : '${' -> pushMode(StringExpression) + : '${' -> pushMode(DEFAULT_MODE) ; -MultiLineNL: NL -> skip ; +// SECTION: inside +mode Inside; -mode StringExpression ; +Inside_RPAREN: RPAREN -> popMode, type(RPAREN); +Inside_RSQUARE: RSQUARE -> popMode, type(RSQUARE); +Inside_LPAREN: LPAREN -> pushMode(Inside), type(LPAREN); +Inside_LSQUARE: LSQUARE -> pushMode(Inside), type(LSQUARE); +Inside_LCURL: LCURL -> pushMode(DEFAULT_MODE), type(LCURL); +Inside_RCURL: RCURL -> popMode, type(RCURL); -StrExpr_RCURL: RCURL -> popMode, type(RCURL) ; +Inside_DOT: DOT -> type(DOT); +Inside_COMMA: COMMA -> type(COMMA); +Inside_MULT: MULT -> type(MULT); +Inside_MOD: MOD -> type(MOD); +Inside_DIV: DIV -> type(DIV); +Inside_ADD: ADD -> type(ADD); +Inside_SUB: SUB -> type(SUB); +Inside_INCR: INCR -> type(INCR); +Inside_DECR: DECR -> type(DECR); +Inside_CONJ: CONJ -> type(CONJ); +Inside_DISJ: DISJ -> type(DISJ); +Inside_EXCL_WS: '!' (Hidden|NL) -> type(EXCL_WS); +Inside_EXCL_NO_WS: EXCL_NO_WS -> type(EXCL_NO_WS); +Inside_COLON: COLON -> type(COLON); +Inside_SEMICOLON: SEMICOLON -> type(SEMICOLON); +Inside_ASSIGNMENT: ASSIGNMENT -> type(ASSIGNMENT); +Inside_ADD_ASSIGNMENT: ADD_ASSIGNMENT -> type(ADD_ASSIGNMENT); +Inside_SUB_ASSIGNMENT: SUB_ASSIGNMENT -> type(SUB_ASSIGNMENT); +Inside_MULT_ASSIGNMENT: MULT_ASSIGNMENT -> type(MULT_ASSIGNMENT); +Inside_DIV_ASSIGNMENT: DIV_ASSIGNMENT -> type(DIV_ASSIGNMENT); +Inside_MOD_ASSIGNMENT: MOD_ASSIGNMENT -> type(MOD_ASSIGNMENT); +Inside_ARROW: ARROW -> type(ARROW); +Inside_DOUBLE_ARROW: DOUBLE_ARROW -> type(DOUBLE_ARROW); +Inside_RANGE: RANGE -> type(RANGE); +Inside_RESERVED: RESERVED -> type(RESERVED); +Inside_COLONCOLON: COLONCOLON -> type(COLONCOLON); +Inside_DOUBLE_SEMICOLON: DOUBLE_SEMICOLON -> type(DOUBLE_SEMICOLON); +Inside_HASH: HASH -> type(HASH); +Inside_AT_NO_WS: AT_NO_WS -> type(AT_NO_WS); +Inside_AT_POST_WS: AT_POST_WS -> type(AT_POST_WS); +Inside_AT_PRE_WS: AT_PRE_WS -> type(AT_PRE_WS); +Inside_AT_BOTH_WS: AT_BOTH_WS -> type(AT_BOTH_WS); +Inside_QUEST_WS: '?' (Hidden | NL) -> type(QUEST_WS); +Inside_QUEST_NO_WS: QUEST_NO_WS -> type(QUEST_NO_WS); +Inside_LANGLE: LANGLE -> type(LANGLE); +Inside_RANGLE: RANGLE -> type(RANGLE); +Inside_LE: LE -> type(LE); +Inside_GE: GE -> type(GE); +Inside_EXCL_EQ: EXCL_EQ -> type(EXCL_EQ); +Inside_EXCL_EQEQ: EXCL_EQEQ -> type(EXCL_EQEQ); +Inside_IS: IS -> type(IS); +Inside_NOT_IS: NOT_IS -> type(NOT_IS); +Inside_NOT_IN: NOT_IN -> type(NOT_IN); +Inside_AS: AS -> type(AS); +Inside_AS_SAFE: AS_SAFE -> type(AS_SAFE); +Inside_EQEQ: EQEQ -> type(EQEQ); +Inside_EQEQEQ: EQEQEQ -> type(EQEQEQ); +Inside_SINGLE_QUOTE: SINGLE_QUOTE -> type(SINGLE_QUOTE); +Inside_QUOTE_OPEN: QUOTE_OPEN -> pushMode(LineString), type(QUOTE_OPEN); +Inside_TRIPLE_QUOTE_OPEN: TRIPLE_QUOTE_OPEN -> pushMode(MultiLineString), type(TRIPLE_QUOTE_OPEN); -StrExpr_LPAREN: LPAREN -> pushMode(Inside), type(LPAREN) ; -StrExpr_LSQUARE: LSQUARE -> pushMode(Inside), type(LSQUARE) ; +Inside_VAL: VAL -> type(VAL); +Inside_VAR: VAR -> type(VAR); +Inside_FUN: FUN -> type(FUN); +Inside_OBJECT: OBJECT -> type(OBJECT); +Inside_SUPER: SUPER -> type(SUPER); +Inside_IN: IN -> type(IN); +Inside_OUT: OUT -> type(OUT); +Inside_FIELD: FIELD -> type(FIELD); +Inside_FILE: FILE -> type(FILE); +Inside_PROPERTY: PROPERTY -> type(PROPERTY); +Inside_GET: GET -> type(GET); +Inside_SET: SET -> type(SET); +Inside_RECEIVER: RECEIVER -> type(RECEIVER); +Inside_PARAM: PARAM -> type(PARAM); +Inside_SETPARAM: SETPARAM -> type(SETPARAM); +Inside_DELEGATE: DELEGATE -> type(DELEGATE); +Inside_THROW: THROW -> type(THROW); +Inside_RETURN: RETURN -> type(RETURN); +Inside_CONTINUE: CONTINUE -> type(CONTINUE); +Inside_BREAK: BREAK -> type(BREAK); +Inside_RETURN_AT: RETURN_AT -> type(RETURN_AT); +Inside_CONTINUE_AT: CONTINUE_AT -> type(CONTINUE_AT); +Inside_BREAK_AT: BREAK_AT -> type(BREAK_AT); +Inside_IF: IF -> type(IF); +Inside_ELSE: ELSE -> type(ELSE); +Inside_WHEN: WHEN -> type(WHEN); +Inside_TRY: TRY -> type(TRY); +Inside_CATCH: CATCH -> type(CATCH); +Inside_FINALLY: FINALLY -> type(FINALLY); +Inside_FOR: FOR -> type(FOR); +Inside_DO: DO -> type(DO); +Inside_WHILE: WHILE -> type(WHILE); -StrExpr_RPAREN: ')' -> type(RPAREN) ; -StrExpr_RSQUARE: ']' -> type(RSQUARE); -StrExpr_LCURL: LCURL -> pushMode(StringExpression), type(LCURL) ; -StrExpr_DOT: DOT -> type(DOT) ; -StrExpr_COMMA: COMMA -> type(COMMA) ; -StrExpr_MULT: MULT -> type(MULT) ; -StrExpr_MOD: MOD -> type(MOD) ; -StrExpr_DIV: DIV -> type(DIV) ; -StrExpr_ADD: ADD -> type(ADD) ; -StrExpr_SUB: SUB -> type(SUB) ; -StrExpr_INCR: INCR -> type(INCR) ; -StrExpr_DECR: DECR -> type(DECR) ; -StrExpr_CONJ: CONJ -> type(CONJ) ; -StrExpr_DISJ: DISJ -> type(DISJ) ; -StrExpr_EXCL: EXCL -> type(EXCL) ; -StrExpr_COLON: COLON -> type(COLON) ; -StrExpr_SEMICOLON: SEMICOLON -> type(SEMICOLON) ; -StrExpr_ASSIGNMENT: ASSIGNMENT -> type(ASSIGNMENT) ; -StrExpr_ADD_ASSIGNMENT: ADD_ASSIGNMENT -> type(ADD_ASSIGNMENT) ; -StrExpr_SUB_ASSIGNMENT: SUB_ASSIGNMENT -> type(SUB_ASSIGNMENT) ; -StrExpr_MULT_ASSIGNMENT: MULT_ASSIGNMENT -> type(MULT_ASSIGNMENT) ; -StrExpr_DIV_ASSIGNMENT: DIV_ASSIGNMENT -> type(DIV_ASSIGNMENT) ; -StrExpr_MOD_ASSIGNMENT: MOD_ASSIGNMENT -> type(MOD_ASSIGNMENT) ; -StrExpr_ARROW: ARROW -> type(ARROW) ; -StrExpr_DOUBLE_ARROW: DOUBLE_ARROW -> type(DOUBLE_ARROW) ; -StrExpr_RANGE: RANGE -> type(RANGE) ; -StrExpr_COLONCOLON: COLONCOLON -> type(COLONCOLON) ; -StrExpr_Q_COLONCOLON: Q_COLONCOLON -> type(Q_COLONCOLON) ; -StrExpr_DOUBLE_SEMICOLON: DOUBLE_SEMICOLON -> type(DOUBLE_SEMICOLON) ; -StrExpr_HASH: HASH -> type(HASH) ; -StrExpr_AT: AT -> type(AT) ; -StrExpr_QUEST: QUEST -> type(QUEST) ; -StrExpr_ELVIS: ELVIS -> type(ELVIS) ; -StrExpr_LANGLE: LANGLE -> type(LANGLE) ; -StrExpr_RANGLE: RANGLE -> type(RANGLE) ; -StrExpr_LE: LE -> type(LE) ; -StrExpr_GE: GE -> type(GE) ; -StrExpr_EXCL_EQ: EXCL_EQ -> type(EXCL_EQ) ; -StrExpr_EXCL_EQEQ: EXCL_EQEQ -> type(EXCL_EQEQ) ; -StrExpr_AS: AS -> type(IS) ; -StrExpr_IS: IS -> type(IN) ; -StrExpr_IN: IN ; -StrExpr_NOT_IS: NOT_IS -> type(NOT_IS) ; -StrExpr_NOT_IN: NOT_IN -> type(NOT_IN) ; -StrExpr_AS_SAFE: AS_SAFE -> type(AS_SAFE) ; -StrExpr_EQEQ: EQEQ -> type(EQEQ) ; -StrExpr_EQEQEQ: EQEQEQ -> type(EQEQEQ) ; -StrExpr_SINGLE_QUOTE: SINGLE_QUOTE -> type(SINGLE_QUOTE) ; -StrExpr_QUOTE_OPEN: QUOTE_OPEN -> pushMode(LineString), type(QUOTE_OPEN) ; -StrExpr_TRIPLE_QUOTE_OPEN: TRIPLE_QUOTE_OPEN -> pushMode(MultiLineString), type(TRIPLE_QUOTE_OPEN) ; +Inside_PUBLIC: PUBLIC -> type(PUBLIC); +Inside_PRIVATE: PRIVATE -> type(PRIVATE); +Inside_PROTECTED: PROTECTED -> type(PROTECTED); +Inside_INTERNAL: INTERNAL -> type(INTERNAL); +Inside_ENUM: ENUM -> type(ENUM); +Inside_SEALED: SEALED -> type(SEALED); +Inside_ANNOTATION: ANNOTATION -> type(ANNOTATION); +Inside_DATA: DATA -> type(DATA); +Inside_INNER: INNER -> type(INNER); +Inside_VALUE: VALUE -> type(VALUE); +Inside_TAILREC: TAILREC -> type(TAILREC); +Inside_OPERATOR: OPERATOR -> type(OPERATOR); +Inside_INLINE: INLINE -> type(INLINE); +Inside_INFIX: INFIX -> type(INFIX); +Inside_EXTERNAL: EXTERNAL -> type(EXTERNAL); +Inside_SUSPEND: SUSPEND -> type(SUSPEND); +Inside_OVERRIDE: OVERRIDE -> type(OVERRIDE); +Inside_ABSTRACT: ABSTRACT -> type(ABSTRACT); +Inside_FINAL: FINAL -> type(FINAL); +Inside_OPEN: OPEN -> type(OPEN); +Inside_CONST: CONST -> type(CONST); +Inside_LATEINIT: LATEINIT -> type(LATEINIT); +Inside_VARARG: VARARG -> type(VARARG); +Inside_NOINLINE: NOINLINE -> type(NOINLINE); +Inside_CROSSINLINE: CROSSINLINE -> type(CROSSINLINE); +Inside_REIFIED: REIFIED -> type(REIFIED); +Inside_EXPECT: EXPECT -> type(EXPECT); +Inside_ACTUAL: ACTUAL -> type(ACTUAL); -StrExpr_BooleanLiteral: BooleanLiteral -> type(BooleanLiteral) ; -StrExpr_IntegerLiteral: IntegerLiteral -> type(IntegerLiteral) ; -StrExpr_HexLiteral: HexLiteral -> type(HexLiteral) ; -StrExpr_BinLiteral: BinLiteral -> type(BinLiteral) ; -StrExpr_CharacterLiteral: CharacterLiteral -> type(CharacterLiteral) ; -StrExpr_RealLiteral: RealLiteral -> type(RealLiteral) ; -StrExpr_NullLiteral: NullLiteral -> type(NullLiteral) ; -StrExpr_LongLiteral: LongLiteral -> type(LongLiteral) ; +Inside_BooleanLiteral: BooleanLiteral -> type(BooleanLiteral); +Inside_IntegerLiteral: IntegerLiteral -> type(IntegerLiteral); +Inside_HexLiteral: HexLiteral -> type(HexLiteral); +Inside_BinLiteral: BinLiteral -> type(BinLiteral); +Inside_CharacterLiteral: CharacterLiteral -> type(CharacterLiteral); +Inside_RealLiteral: RealLiteral -> type(RealLiteral); +Inside_NullLiteral: NullLiteral -> type(NullLiteral); +Inside_LongLiteral: LongLiteral -> type(LongLiteral); +Inside_UnsignedLiteral: UnsignedLiteral -> type(UnsignedLiteral); -StrExpr_Identifier: Identifier -> type(Identifier) ; -StrExpr_LabelReference: LabelReference -> type(LabelReference) ; -StrExpr_LabelDefinition: LabelDefinition -> type(LabelDefinition) ; -StrExpr_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN) ; -StrExpr_WS: WS -> skip ; -StrExpr_NL: NL -> skip ; +Inside_Identifier: Identifier -> type(Identifier); +Inside_Comment: (LineComment | DelimitedComment) -> channel(HIDDEN); +Inside_WS: WS -> channel(HIDDEN); +Inside_NL: NL -> channel(HIDDEN); + +mode DEFAULT_MODE; + +ErrorCharacter: .; diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.tokens b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.tokens new file mode 100644 index 0000000000..a207d69c19 --- /dev/null +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/KotlinLexer.tokens @@ -0,0 +1,290 @@ +ShebangLine=1 +DelimitedComment=2 +LineComment=3 +WS=4 +NL=5 +RESERVED=6 +DOT=7 +COMMA=8 +LPAREN=9 +RPAREN=10 +LSQUARE=11 +RSQUARE=12 +LCURL=13 +RCURL=14 +MULT=15 +MOD=16 +DIV=17 +ADD=18 +SUB=19 +INCR=20 +DECR=21 +CONJ=22 +DISJ=23 +EXCL_WS=24 +EXCL_NO_WS=25 +COLON=26 +SEMICOLON=27 +ASSIGNMENT=28 +ADD_ASSIGNMENT=29 +SUB_ASSIGNMENT=30 +MULT_ASSIGNMENT=31 +DIV_ASSIGNMENT=32 +MOD_ASSIGNMENT=33 +ARROW=34 +DOUBLE_ARROW=35 +RANGE=36 +COLONCOLON=37 +DOUBLE_SEMICOLON=38 +HASH=39 +AT_NO_WS=40 +AT_POST_WS=41 +AT_PRE_WS=42 +AT_BOTH_WS=43 +QUEST_WS=44 +QUEST_NO_WS=45 +LANGLE=46 +RANGLE=47 +LE=48 +GE=49 +EXCL_EQ=50 +EXCL_EQEQ=51 +AS_SAFE=52 +EQEQ=53 +EQEQEQ=54 +SINGLE_QUOTE=55 +RETURN_AT=56 +CONTINUE_AT=57 +BREAK_AT=58 +THIS_AT=59 +SUPER_AT=60 +FILE=61 +FIELD=62 +PROPERTY=63 +GET=64 +SET=65 +RECEIVER=66 +PARAM=67 +SETPARAM=68 +DELEGATE=69 +PACKAGE=70 +IMPORT=71 +CLASS=72 +INTERFACE=73 +FUN=74 +OBJECT=75 +VAL=76 +VAR=77 +TYPE_ALIAS=78 +CONSTRUCTOR=79 +BY=80 +COMPANION=81 +INIT=82 +THIS=83 +SUPER=84 +TYPEOF=85 +WHERE=86 +IF=87 +ELSE=88 +WHEN=89 +TRY=90 +CATCH=91 +FINALLY=92 +FOR=93 +DO=94 +WHILE=95 +THROW=96 +RETURN=97 +CONTINUE=98 +BREAK=99 +AS=100 +IS=101 +IN=102 +NOT_IS=103 +NOT_IN=104 +OUT=105 +DYNAMIC=106 +PUBLIC=107 +PRIVATE=108 +PROTECTED=109 +INTERNAL=110 +ENUM=111 +SEALED=112 +ANNOTATION=113 +DATA=114 +INNER=115 +VALUE=116 +TAILREC=117 +OPERATOR=118 +INLINE=119 +INFIX=120 +EXTERNAL=121 +SUSPEND=122 +OVERRIDE=123 +ABSTRACT=124 +FINAL=125 +OPEN=126 +CONST=127 +LATEINIT=128 +VARARG=129 +NOINLINE=130 +CROSSINLINE=131 +REIFIED=132 +EXPECT=133 +ACTUAL=134 +RealLiteral=135 +FloatLiteral=136 +DoubleLiteral=137 +IntegerLiteral=138 +HexLiteral=139 +BinLiteral=140 +UnsignedLiteral=141 +LongLiteral=142 +BooleanLiteral=143 +NullLiteral=144 +CharacterLiteral=145 +Identifier=146 +IdentifierOrSoftKey=147 +FieldIdentifier=148 +QUOTE_OPEN=149 +TRIPLE_QUOTE_OPEN=150 +UNICODE_CLASS_LL=151 +UNICODE_CLASS_LM=152 +UNICODE_CLASS_LO=153 +UNICODE_CLASS_LT=154 +UNICODE_CLASS_LU=155 +UNICODE_CLASS_ND=156 +UNICODE_CLASS_NL=157 +QUOTE_CLOSE=158 +LineStrRef=159 +LineStrText=160 +LineStrEscapedChar=161 +LineStrExprStart=162 +TRIPLE_QUOTE_CLOSE=163 +MultiLineStringQuote=164 +MultiLineStrRef=165 +MultiLineStrText=166 +MultiLineStrExprStart=167 +Inside_Comment=168 +Inside_WS=169 +Inside_NL=170 +ErrorCharacter=171 +'...'=6 +'.'=7 +','=8 +'('=9 +')'=10 +'['=11 +']'=12 +'{'=13 +'}'=14 +'*'=15 +'%'=16 +'/'=17 +'+'=18 +'-'=19 +'++'=20 +'--'=21 +'&&'=22 +'||'=23 +'!'=25 +':'=26 +';'=27 +'='=28 +'+='=29 +'-='=30 +'*='=31 +'/='=32 +'%='=33 +'->'=34 +'=>'=35 +'..'=36 +'::'=37 +';;'=38 +'#'=39 +'@'=40 +'?'=45 +'<'=46 +'>'=47 +'<='=48 +'>='=49 +'!='=50 +'!=='=51 +'as?'=52 +'=='=53 +'==='=54 +'\''=55 +'file'=61 +'field'=62 +'property'=63 +'get'=64 +'set'=65 +'receiver'=66 +'param'=67 +'setparam'=68 +'delegate'=69 +'package'=70 +'import'=71 +'class'=72 +'interface'=73 +'fun'=74 +'object'=75 +'val'=76 +'var'=77 +'typealias'=78 +'constructor'=79 +'by'=80 +'companion'=81 +'init'=82 +'this'=83 +'super'=84 +'typeof'=85 +'where'=86 +'if'=87 +'else'=88 +'when'=89 +'try'=90 +'catch'=91 +'finally'=92 +'for'=93 +'do'=94 +'while'=95 +'throw'=96 +'return'=97 +'continue'=98 +'break'=99 +'as'=100 +'is'=101 +'in'=102 +'out'=105 +'dynamic'=106 +'public'=107 +'private'=108 +'protected'=109 +'internal'=110 +'enum'=111 +'sealed'=112 +'annotation'=113 +'data'=114 +'inner'=115 +'value'=116 +'tailrec'=117 +'operator'=118 +'inline'=119 +'infix'=120 +'external'=121 +'suspend'=122 +'override'=123 +'abstract'=124 +'final'=125 +'open'=126 +'const'=127 +'lateinit'=128 +'vararg'=129 +'noinline'=130 +'crossinline'=131 +'reified'=132 +'expect'=133 +'actual'=134 +'null'=144 +'"""'=150 diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/README.md b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/README.md new file mode 100644 index 0000000000..84ef104798 --- /dev/null +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/README.md @@ -0,0 +1,69 @@ +# Kotlin Grammar + +The grammar files for Kotlin are taken from , released under the +Apache License, Version 2.0: + +``` +Copyright 2000-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +``` + +The grammar files still use the Apache License, but are slightly modified. +All other files in this PMD module are licensed under BSD. + +## Currently used version + +* Release: +* Source: + +### Modifications + +Some modifications are made in KotlinParser.g4: + +* The file "KotlinParser.g4" is renamed to "Kotlin.g4" +* `grammar Kotlin` instead of KotlinParser +* Additional headers: + +``` +@header { +import net.sourceforge.pmd.lang.ast.impl.antlr4.*; +import net.sourceforge.pmd.lang.ast.AstVisitor; +} +``` + +* Additional members: + +``` +@parser::members { + + static final AntlrNameDictionary DICO = new KotlinNameDictionary(VOCABULARY, ruleNames); + + @Override + public KotlinTerminalNode createPmdTerminal(ParserRuleContext parent, Token t) { + return new KotlinTerminalNode(t); + } + + @Override + public KotlinErrorNode createPmdError(ParserRuleContext parent, Token t) { + return new KotlinErrorNode(t); + } +} +``` + +* Additional options: + +``` +contextSuperClass = 'KotlinInnerNode'; +superClass = 'AntlrGeneratedParserBase'; +``` diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.g4 b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.g4 new file mode 100644 index 0000000000..71a0b720ae --- /dev/null +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.g4 @@ -0,0 +1,1650 @@ +// SPDX-License-Identifier: Apache-2.0 +/** + * Kotlin lexical grammar in ANTLR4 notation (Unicode classes) + * + * Taken from http://www.antlr3.org/grammar/1345144569663/AntlrUnicode.txt + */ + +lexer grammar UnicodeClasses; + +UNICODE_CLASS_LL: + '\u0061'..'\u007A' | + '\u00B5' | + '\u00DF'..'\u00F6' | + '\u00F8'..'\u00FF' | + '\u0101' | + '\u0103' | + '\u0105' | + '\u0107' | + '\u0109' | + '\u010B' | + '\u010D' | + '\u010F' | + '\u0111' | + '\u0113' | + '\u0115' | + '\u0117' | + '\u0119' | + '\u011B' | + '\u011D' | + '\u011F' | + '\u0121' | + '\u0123' | + '\u0125' | + '\u0127' | + '\u0129' | + '\u012B' | + '\u012D' | + '\u012F' | + '\u0131' | + '\u0133' | + '\u0135' | + '\u0137' | + '\u0138' | + '\u013A' | + '\u013C' | + '\u013E' | + '\u0140' | + '\u0142' | + '\u0144' | + '\u0146' | + '\u0148' | + '\u0149' | + '\u014B' | + '\u014D' | + '\u014F' | + '\u0151' | + '\u0153' | + '\u0155' | + '\u0157' | + '\u0159' | + '\u015B' | + '\u015D' | + '\u015F' | + '\u0161' | + '\u0163' | + '\u0165' | + '\u0167' | + '\u0169' | + '\u016B' | + '\u016D' | + '\u016F' | + '\u0171' | + '\u0173' | + '\u0175' | + '\u0177' | + '\u017A' | + '\u017C' | + '\u017E'..'\u0180' | + '\u0183' | + '\u0185' | + '\u0188' | + '\u018C' | + '\u018D' | + '\u0192' | + '\u0195' | + '\u0199'..'\u019B' | + '\u019E' | + '\u01A1' | + '\u01A3' | + '\u01A5' | + '\u01A8' | + '\u01AA' | + '\u01AB' | + '\u01AD' | + '\u01B0' | + '\u01B4' | + '\u01B6' | + '\u01B9' | + '\u01BA' | + '\u01BD'..'\u01BF' | + '\u01C6' | + '\u01C9' | + '\u01CC' | + '\u01CE' | + '\u01D0' | + '\u01D2' | + '\u01D4' | + '\u01D6' | + '\u01D8' | + '\u01DA' | + '\u01DC' | + '\u01DD' | + '\u01DF' | + '\u01E1' | + '\u01E3' | + '\u01E5' | + '\u01E7' | + '\u01E9' | + '\u01EB' | + '\u01ED' | + '\u01EF' | + '\u01F0' | + '\u01F3' | + '\u01F5' | + '\u01F9' | + '\u01FB' | + '\u01FD' | + '\u01FF' | + '\u0201' | + '\u0203' | + '\u0205' | + '\u0207' | + '\u0209' | + '\u020B' | + '\u020D' | + '\u020F' | + '\u0211' | + '\u0213' | + '\u0215' | + '\u0217' | + '\u0219' | + '\u021B' | + '\u021D' | + '\u021F' | + '\u0221' | + '\u0223' | + '\u0225' | + '\u0227' | + '\u0229' | + '\u022B' | + '\u022D' | + '\u022F' | + '\u0231' | + '\u0233'..'\u0239' | + '\u023C' | + '\u023F' | + '\u0240' | + '\u0242' | + '\u0247' | + '\u0249' | + '\u024B' | + '\u024D' | + '\u024F'..'\u0293' | + '\u0295'..'\u02AF' | + '\u0371' | + '\u0373' | + '\u0377' | + '\u037B'..'\u037D' | + '\u0390' | + '\u03AC'..'\u03CE' | + '\u03D0' | + '\u03D1' | + '\u03D5'..'\u03D7' | + '\u03D9' | + '\u03DB' | + '\u03DD' | + '\u03DF' | + '\u03E1' | + '\u03E3' | + '\u03E5' | + '\u03E7' | + '\u03E9' | + '\u03EB' | + '\u03ED' | + '\u03EF'..'\u03F3' | + '\u03F5' | + '\u03F8' | + '\u03FB' | + '\u03FC' | + '\u0430'..'\u045F' | + '\u0461' | + '\u0463' | + '\u0465' | + '\u0467' | + '\u0469' | + '\u046B' | + '\u046D' | + '\u046F' | + '\u0471' | + '\u0473' | + '\u0475' | + '\u0477' | + '\u0479' | + '\u047B' | + '\u047D' | + '\u047F' | + '\u0481' | + '\u048B' | + '\u048D' | + '\u048F' | + '\u0491' | + '\u0493' | + '\u0495' | + '\u0497' | + '\u0499' | + '\u049B' | + '\u049D' | + '\u049F' | + '\u04A1' | + '\u04A3' | + '\u04A5' | + '\u04A7' | + '\u04A9' | + '\u04AB' | + '\u04AD' | + '\u04AF' | + '\u04B1' | + '\u04B3' | + '\u04B5' | + '\u04B7' | + '\u04B9' | + '\u04BB' | + '\u04BD' | + '\u04BF' | + '\u04C2' | + '\u04C4' | + '\u04C6' | + '\u04C8' | + '\u04CA' | + '\u04CC' | + '\u04CE' | + '\u04CF' | + '\u04D1' | + '\u04D3' | + '\u04D5' | + '\u04D7' | + '\u04D9' | + '\u04DB' | + '\u04DD' | + '\u04DF' | + '\u04E1' | + '\u04E3' | + '\u04E5' | + '\u04E7' | + '\u04E9' | + '\u04EB' | + '\u04ED' | + '\u04EF' | + '\u04F1' | + '\u04F3' | + '\u04F5' | + '\u04F7' | + '\u04F9' | + '\u04FB' | + '\u04FD' | + '\u04FF' | + '\u0501' | + '\u0503' | + '\u0505' | + '\u0507' | + '\u0509' | + '\u050B' | + '\u050D' | + '\u050F' | + '\u0511' | + '\u0513' | + '\u0515' | + '\u0517' | + '\u0519' | + '\u051B' | + '\u051D' | + '\u051F' | + '\u0521' | + '\u0523' | + '\u0525' | + '\u0527' | + '\u0561'..'\u0587' | + '\u1D00'..'\u1D2B' | + '\u1D6B'..'\u1D77' | + '\u1D79'..'\u1D9A' | + '\u1E01' | + '\u1E03' | + '\u1E05' | + '\u1E07' | + '\u1E09' | + '\u1E0B' | + '\u1E0D' | + '\u1E0F' | + '\u1E11' | + '\u1E13' | + '\u1E15' | + '\u1E17' | + '\u1E19' | + '\u1E1B' | + '\u1E1D' | + '\u1E1F' | + '\u1E21' | + '\u1E23' | + '\u1E25' | + '\u1E27' | + '\u1E29' | + '\u1E2B' | + '\u1E2D' | + '\u1E2F' | + '\u1E31' | + '\u1E33' | + '\u1E35' | + '\u1E37' | + '\u1E39' | + '\u1E3B' | + '\u1E3D' | + '\u1E3F' | + '\u1E41' | + '\u1E43' | + '\u1E45' | + '\u1E47' | + '\u1E49' | + '\u1E4B' | + '\u1E4D' | + '\u1E4F' | + '\u1E51' | + '\u1E53' | + '\u1E55' | + '\u1E57' | + '\u1E59' | + '\u1E5B' | + '\u1E5D' | + '\u1E5F' | + '\u1E61' | + '\u1E63' | + '\u1E65' | + '\u1E67' | + '\u1E69' | + '\u1E6B' | + '\u1E6D' | + '\u1E6F' | + '\u1E71' | + '\u1E73' | + '\u1E75' | + '\u1E77' | + '\u1E79' | + '\u1E7B' | + '\u1E7D' | + '\u1E7F' | + '\u1E81' | + '\u1E83' | + '\u1E85' | + '\u1E87' | + '\u1E89' | + '\u1E8B' | + '\u1E8D' | + '\u1E8F' | + '\u1E91' | + '\u1E93' | + '\u1E95'..'\u1E9D' | + '\u1E9F' | + '\u1EA1' | + '\u1EA3' | + '\u1EA5' | + '\u1EA7' | + '\u1EA9' | + '\u1EAB' | + '\u1EAD' | + '\u1EAF' | + '\u1EB1' | + '\u1EB3' | + '\u1EB5' | + '\u1EB7' | + '\u1EB9' | + '\u1EBB' | + '\u1EBD' | + '\u1EBF' | + '\u1EC1' | + '\u1EC3' | + '\u1EC5' | + '\u1EC7' | + '\u1EC9' | + '\u1ECB' | + '\u1ECD' | + '\u1ECF' | + '\u1ED1' | + '\u1ED3' | + '\u1ED5' | + '\u1ED7' | + '\u1ED9' | + '\u1EDB' | + '\u1EDD' | + '\u1EDF' | + '\u1EE1' | + '\u1EE3' | + '\u1EE5' | + '\u1EE7' | + '\u1EE9' | + '\u1EEB' | + '\u1EED' | + '\u1EEF' | + '\u1EF1' | + '\u1EF3' | + '\u1EF5' | + '\u1EF7' | + '\u1EF9' | + '\u1EFB' | + '\u1EFD' | + '\u1EFF'..'\u1F07' | + '\u1F10'..'\u1F15' | + '\u1F20'..'\u1F27' | + '\u1F30'..'\u1F37' | + '\u1F40'..'\u1F45' | + '\u1F50'..'\u1F57' | + '\u1F60'..'\u1F67' | + '\u1F70'..'\u1F7D' | + '\u1F80'..'\u1F87' | + '\u1F90'..'\u1F97' | + '\u1FA0'..'\u1FA7' | + '\u1FB0'..'\u1FB4' | + '\u1FB6' | + '\u1FB7' | + '\u1FBE' | + '\u1FC2'..'\u1FC4' | + '\u1FC6' | + '\u1FC7' | + '\u1FD0'..'\u1FD3' | + '\u1FD6' | + '\u1FD7' | + '\u1FE0'..'\u1FE7' | + '\u1FF2'..'\u1FF4' | + '\u1FF6' | + '\u1FF7' | + '\u210A' | + '\u210E' | + '\u210F' | + '\u2113' | + '\u212F' | + '\u2134' | + '\u2139' | + '\u213C' | + '\u213D' | + '\u2146'..'\u2149' | + '\u214E' | + '\u2184' | + '\u2C30'..'\u2C5E' | + '\u2C61' | + '\u2C65' | + '\u2C66' | + '\u2C68' | + '\u2C6A' | + '\u2C6C' | + '\u2C71' | + '\u2C73' | + '\u2C74' | + '\u2C76'..'\u2C7B' | + '\u2C81' | + '\u2C83' | + '\u2C85' | + '\u2C87' | + '\u2C89' | + '\u2C8B' | + '\u2C8D' | + '\u2C8F' | + '\u2C91' | + '\u2C93' | + '\u2C95' | + '\u2C97' | + '\u2C99' | + '\u2C9B' | + '\u2C9D' | + '\u2C9F' | + '\u2CA1' | + '\u2CA3' | + '\u2CA5' | + '\u2CA7' | + '\u2CA9' | + '\u2CAB' | + '\u2CAD' | + '\u2CAF' | + '\u2CB1' | + '\u2CB3' | + '\u2CB5' | + '\u2CB7' | + '\u2CB9' | + '\u2CBB' | + '\u2CBD' | + '\u2CBF' | + '\u2CC1' | + '\u2CC3' | + '\u2CC5' | + '\u2CC7' | + '\u2CC9' | + '\u2CCB' | + '\u2CCD' | + '\u2CCF' | + '\u2CD1' | + '\u2CD3' | + '\u2CD5' | + '\u2CD7' | + '\u2CD9' | + '\u2CDB' | + '\u2CDD' | + '\u2CDF' | + '\u2CE1' | + '\u2CE3' | + '\u2CE4' | + '\u2CEC' | + '\u2CEE' | + '\u2CF3' | + '\u2D00'..'\u2D25' | + '\u2D27' | + '\u2D2D' | + '\uA641' | + '\uA643' | + '\uA645' | + '\uA647' | + '\uA649' | + '\uA64B' | + '\uA64D' | + '\uA64F' | + '\uA651' | + '\uA653' | + '\uA655' | + '\uA657' | + '\uA659' | + '\uA65B' | + '\uA65D' | + '\uA65F' | + '\uA661' | + '\uA663' | + '\uA665' | + '\uA667' | + '\uA669' | + '\uA66B' | + '\uA66D' | + '\uA681' | + '\uA683' | + '\uA685' | + '\uA687' | + '\uA689' | + '\uA68B' | + '\uA68D' | + '\uA68F' | + '\uA691' | + '\uA693' | + '\uA695' | + '\uA697' | + '\uA723' | + '\uA725' | + '\uA727' | + '\uA729' | + '\uA72B' | + '\uA72D' | + '\uA72F'..'\uA731' | + '\uA733' | + '\uA735' | + '\uA737' | + '\uA739' | + '\uA73B' | + '\uA73D' | + '\uA73F' | + '\uA741' | + '\uA743' | + '\uA745' | + '\uA747' | + '\uA749' | + '\uA74B' | + '\uA74D' | + '\uA74F' | + '\uA751' | + '\uA753' | + '\uA755' | + '\uA757' | + '\uA759' | + '\uA75B' | + '\uA75D' | + '\uA75F' | + '\uA761' | + '\uA763' | + '\uA765' | + '\uA767' | + '\uA769' | + '\uA76B' | + '\uA76D' | + '\uA76F' | + '\uA771'..'\uA778' | + '\uA77A' | + '\uA77C' | + '\uA77F' | + '\uA781' | + '\uA783' | + '\uA785' | + '\uA787' | + '\uA78C' | + '\uA78E' | + '\uA791' | + '\uA793' | + '\uA7A1' | + '\uA7A3' | + '\uA7A5' | + '\uA7A7' | + '\uA7A9' | + '\uA7FA' | + '\uFB00'..'\uFB06' | + '\uFB13'..'\uFB17' | + '\uFF41'..'\uFF5A'; + +UNICODE_CLASS_LM: + '\u02B0'..'\u02C1' | + '\u02C6'..'\u02D1' | + '\u02E0'..'\u02E4' | + '\u02EC' | + '\u02EE' | + '\u0374' | + '\u037A' | + '\u0559' | + '\u0640' | + '\u06E5' | + '\u06E6' | + '\u07F4' | + '\u07F5' | + '\u07FA' | + '\u081A' | + '\u0824' | + '\u0828' | + '\u0971' | + '\u0E46' | + '\u0EC6' | + '\u10FC' | + '\u17D7' | + '\u1843' | + '\u1AA7' | + '\u1C78'..'\u1C7D' | + '\u1D2C'..'\u1D6A' | + '\u1D78' | + '\u1D9B'..'\u1DBF' | + '\u2071' | + '\u207F' | + '\u2090'..'\u209C' | + '\u2C7C' | + '\u2C7D' | + '\u2D6F' | + '\u2E2F' | + '\u3005' | + '\u3031'..'\u3035' | + '\u303B' | + '\u309D' | + '\u309E' | + '\u30FC'..'\u30FE' | + '\uA015' | + '\uA4F8'..'\uA4FD' | + '\uA60C' | + '\uA67F' | + '\uA717'..'\uA71F' | + '\uA770' | + '\uA788' | + '\uA7F8' | + '\uA7F9' | + '\uA9CF' | + '\uAA70' | + '\uAADD' | + '\uAAF3' | + '\uAAF4' | + '\uFF70' | + '\uFF9E' | + '\uFF9F'; + +UNICODE_CLASS_LO: + '\u00AA' | + '\u00BA' | + '\u01BB' | + '\u01C0'..'\u01C3' | + '\u0294' | + '\u05D0'..'\u05EA' | + '\u05F0'..'\u05F2' | + '\u0620'..'\u063F' | + '\u0641'..'\u064A' | + '\u066E' | + '\u066F' | + '\u0671'..'\u06D3' | + '\u06D5' | + '\u06EE' | + '\u06EF' | + '\u06FA'..'\u06FC' | + '\u06FF' | + '\u0710' | + '\u0712'..'\u072F' | + '\u074D'..'\u07A5' | + '\u07B1' | + '\u07CA'..'\u07EA' | + '\u0800'..'\u0815' | + '\u0840'..'\u0858' | + '\u08A0' | + '\u08A2'..'\u08AC' | + '\u0904'..'\u0939' | + '\u093D' | + '\u0950' | + '\u0958'..'\u0961' | + '\u0972'..'\u0977' | + '\u0979'..'\u097F' | + '\u0985'..'\u098C' | + '\u098F' | + '\u0990' | + '\u0993'..'\u09A8' | + '\u09AA'..'\u09B0' | + '\u09B2' | + '\u09B6'..'\u09B9' | + '\u09BD' | + '\u09CE' | + '\u09DC' | + '\u09DD' | + '\u09DF'..'\u09E1' | + '\u09F0' | + '\u09F1' | + '\u0A05'..'\u0A0A' | + '\u0A0F' | + '\u0A10' | + '\u0A13'..'\u0A28' | + '\u0A2A'..'\u0A30' | + '\u0A32' | + '\u0A33' | + '\u0A35' | + '\u0A36' | + '\u0A38' | + '\u0A39' | + '\u0A59'..'\u0A5C' | + '\u0A5E' | + '\u0A72'..'\u0A74' | + '\u0A85'..'\u0A8D' | + '\u0A8F'..'\u0A91' | + '\u0A93'..'\u0AA8' | + '\u0AAA'..'\u0AB0' | + '\u0AB2' | + '\u0AB3' | + '\u0AB5'..'\u0AB9' | + '\u0ABD' | + '\u0AD0' | + '\u0AE0' | + '\u0AE1' | + '\u0B05'..'\u0B0C' | + '\u0B0F' | + '\u0B10' | + '\u0B13'..'\u0B28' | + '\u0B2A'..'\u0B30' | + '\u0B32' | + '\u0B33' | + '\u0B35'..'\u0B39' | + '\u0B3D' | + '\u0B5C' | + '\u0B5D' | + '\u0B5F'..'\u0B61' | + '\u0B71' | + '\u0B83' | + '\u0B85'..'\u0B8A' | + '\u0B8E'..'\u0B90' | + '\u0B92'..'\u0B95' | + '\u0B99' | + '\u0B9A' | + '\u0B9C' | + '\u0B9E' | + '\u0B9F' | + '\u0BA3' | + '\u0BA4' | + '\u0BA8'..'\u0BAA' | + '\u0BAE'..'\u0BB9' | + '\u0BD0' | + '\u0C05'..'\u0C0C' | + '\u0C0E'..'\u0C10' | + '\u0C12'..'\u0C28' | + '\u0C2A'..'\u0C33' | + '\u0C35'..'\u0C39' | + '\u0C3D' | + '\u0C58' | + '\u0C59' | + '\u0C60' | + '\u0C61' | + '\u0C85'..'\u0C8C' | + '\u0C8E'..'\u0C90' | + '\u0C92'..'\u0CA8' | + '\u0CAA'..'\u0CB3' | + '\u0CB5'..'\u0CB9' | + '\u0CBD' | + '\u0CDE' | + '\u0CE0' | + '\u0CE1' | + '\u0CF1' | + '\u0CF2' | + '\u0D05'..'\u0D0C' | + '\u0D0E'..'\u0D10' | + '\u0D12'..'\u0D3A' | + '\u0D3D' | + '\u0D4E' | + '\u0D60' | + '\u0D61' | + '\u0D7A'..'\u0D7F' | + '\u0D85'..'\u0D96' | + '\u0D9A'..'\u0DB1' | + '\u0DB3'..'\u0DBB' | + '\u0DBD' | + '\u0DC0'..'\u0DC6' | + '\u0E01'..'\u0E30' | + '\u0E32' | + '\u0E33' | + '\u0E40'..'\u0E45' | + '\u0E81' | + '\u0E82' | + '\u0E84' | + '\u0E87' | + '\u0E88' | + '\u0E8A' | + '\u0E8D' | + '\u0E94'..'\u0E97' | + '\u0E99'..'\u0E9F' | + '\u0EA1'..'\u0EA3' | + '\u0EA5' | + '\u0EA7' | + '\u0EAA' | + '\u0EAB' | + '\u0EAD'..'\u0EB0' | + '\u0EB2' | + '\u0EB3' | + '\u0EBD' | + '\u0EC0'..'\u0EC4' | + '\u0EDC'..'\u0EDF' | + '\u0F00' | + '\u0F40'..'\u0F47' | + '\u0F49'..'\u0F6C' | + '\u0F88'..'\u0F8C' | + '\u1000'..'\u102A' | + '\u103F' | + '\u1050'..'\u1055' | + '\u105A'..'\u105D' | + '\u1061' | + '\u1065' | + '\u1066' | + '\u106E'..'\u1070' | + '\u1075'..'\u1081' | + '\u108E' | + '\u10D0'..'\u10FA' | + '\u10FD'..'\u1248' | + '\u124A'..'\u124D' | + '\u1250'..'\u1256' | + '\u1258' | + '\u125A'..'\u125D' | + '\u1260'..'\u1288' | + '\u128A'..'\u128D' | + '\u1290'..'\u12B0' | + '\u12B2'..'\u12B5' | + '\u12B8'..'\u12BE' | + '\u12C0' | + '\u12C2'..'\u12C5' | + '\u12C8'..'\u12D6' | + '\u12D8'..'\u1310' | + '\u1312'..'\u1315' | + '\u1318'..'\u135A' | + '\u1380'..'\u138F' | + '\u13A0'..'\u13F4' | + '\u1401'..'\u166C' | + '\u166F'..'\u167F' | + '\u1681'..'\u169A' | + '\u16A0'..'\u16EA' | + '\u1700'..'\u170C' | + '\u170E'..'\u1711' | + '\u1720'..'\u1731' | + '\u1740'..'\u1751' | + '\u1760'..'\u176C' | + '\u176E'..'\u1770' | + '\u1780'..'\u17B3' | + '\u17DC' | + '\u1820'..'\u1842' | + '\u1844'..'\u1877' | + '\u1880'..'\u18A8' | + '\u18AA' | + '\u18B0'..'\u18F5' | + '\u1900'..'\u191C' | + '\u1950'..'\u196D' | + '\u1970'..'\u1974' | + '\u1980'..'\u19AB' | + '\u19C1'..'\u19C7' | + '\u1A00'..'\u1A16' | + '\u1A20'..'\u1A54' | + '\u1B05'..'\u1B33' | + '\u1B45'..'\u1B4B' | + '\u1B83'..'\u1BA0' | + '\u1BAE' | + '\u1BAF' | + '\u1BBA'..'\u1BE5' | + '\u1C00'..'\u1C23' | + '\u1C4D'..'\u1C4F' | + '\u1C5A'..'\u1C77' | + '\u1CE9'..'\u1CEC' | + '\u1CEE'..'\u1CF1' | + '\u1CF5' | + '\u1CF6' | + '\u2135'..'\u2138' | + '\u2D30'..'\u2D67' | + '\u2D80'..'\u2D96' | + '\u2DA0'..'\u2DA6' | + '\u2DA8'..'\u2DAE' | + '\u2DB0'..'\u2DB6' | + '\u2DB8'..'\u2DBE' | + '\u2DC0'..'\u2DC6' | + '\u2DC8'..'\u2DCE' | + '\u2DD0'..'\u2DD6' | + '\u2DD8'..'\u2DDE' | + '\u3006' | + '\u303C' | + '\u3041'..'\u3096' | + '\u309F' | + '\u30A1'..'\u30FA' | + '\u30FF' | + '\u3105'..'\u312D' | + '\u3131'..'\u318E' | + '\u31A0'..'\u31BA' | + '\u31F0'..'\u31FF' | + '\u3400' | + '\u4DB5' | + '\u4E00' | + '\u9FCC' | + '\uA000'..'\uA014' | + '\uA016'..'\uA48C' | + '\uA4D0'..'\uA4F7' | + '\uA500'..'\uA60B' | + '\uA610'..'\uA61F' | + '\uA62A' | + '\uA62B' | + '\uA66E' | + '\uA6A0'..'\uA6E5' | + '\uA7FB'..'\uA801' | + '\uA803'..'\uA805' | + '\uA807'..'\uA80A' | + '\uA80C'..'\uA822' | + '\uA840'..'\uA873' | + '\uA882'..'\uA8B3' | + '\uA8F2'..'\uA8F7' | + '\uA8FB' | + '\uA90A'..'\uA925' | + '\uA930'..'\uA946' | + '\uA960'..'\uA97C' | + '\uA984'..'\uA9B2' | + '\uAA00'..'\uAA28' | + '\uAA40'..'\uAA42' | + '\uAA44'..'\uAA4B' | + '\uAA60'..'\uAA6F' | + '\uAA71'..'\uAA76' | + '\uAA7A' | + '\uAA80'..'\uAAAF' | + '\uAAB1' | + '\uAAB5' | + '\uAAB6' | + '\uAAB9'..'\uAABD' | + '\uAAC0' | + '\uAAC2' | + '\uAADB' | + '\uAADC' | + '\uAAE0'..'\uAAEA' | + '\uAAF2' | + '\uAB01'..'\uAB06' | + '\uAB09'..'\uAB0E' | + '\uAB11'..'\uAB16' | + '\uAB20'..'\uAB26' | + '\uAB28'..'\uAB2E' | + '\uABC0'..'\uABE2' | + '\uAC00' | + '\uD7A3' | + '\uD7B0'..'\uD7C6' | + '\uD7CB'..'\uD7FB' | + '\uF900'..'\uFA6D' | + '\uFA70'..'\uFAD9' | + '\uFB1D' | + '\uFB1F'..'\uFB28' | + '\uFB2A'..'\uFB36' | + '\uFB38'..'\uFB3C' | + '\uFB3E' | + '\uFB40' | + '\uFB41' | + '\uFB43' | + '\uFB44' | + '\uFB46'..'\uFBB1' | + '\uFBD3'..'\uFD3D' | + '\uFD50'..'\uFD8F' | + '\uFD92'..'\uFDC7' | + '\uFDF0'..'\uFDFB' | + '\uFE70'..'\uFE74' | + '\uFE76'..'\uFEFC' | + '\uFF66'..'\uFF6F' | + '\uFF71'..'\uFF9D' | + '\uFFA0'..'\uFFBE' | + '\uFFC2'..'\uFFC7' | + '\uFFCA'..'\uFFCF' | + '\uFFD2'..'\uFFD7' | + '\uFFDA'..'\uFFDC'; + +UNICODE_CLASS_LT: + '\u01C5' | + '\u01C8' | + '\u01CB' | + '\u01F2' | + '\u1F88'..'\u1F8F' | + '\u1F98'..'\u1F9F' | + '\u1FA8'..'\u1FAF' | + '\u1FBC' | + '\u1FCC' | + '\u1FFC'; + +UNICODE_CLASS_LU: + '\u0041'..'\u005A' | + '\u00C0'..'\u00D6' | + '\u00D8'..'\u00DE' | + '\u0100' | + '\u0102' | + '\u0104' | + '\u0106' | + '\u0108' | + '\u010A' | + '\u010C' | + '\u010E' | + '\u0110' | + '\u0112' | + '\u0114' | + '\u0116' | + '\u0118' | + '\u011A' | + '\u011C' | + '\u011E' | + '\u0120' | + '\u0122' | + '\u0124' | + '\u0126' | + '\u0128' | + '\u012A' | + '\u012C' | + '\u012E' | + '\u0130' | + '\u0132' | + '\u0134' | + '\u0136' | + '\u0139' | + '\u013B' | + '\u013D' | + '\u013F' | + '\u0141' | + '\u0143' | + '\u0145' | + '\u0147' | + '\u014A' | + '\u014C' | + '\u014E' | + '\u0150' | + '\u0152' | + '\u0154' | + '\u0156' | + '\u0158' | + '\u015A' | + '\u015C' | + '\u015E' | + '\u0160' | + '\u0162' | + '\u0164' | + '\u0166' | + '\u0168' | + '\u016A' | + '\u016C' | + '\u016E' | + '\u0170' | + '\u0172' | + '\u0174' | + '\u0176' | + '\u0178' | + '\u0179' | + '\u017B' | + '\u017D' | + '\u0181' | + '\u0182' | + '\u0184' | + '\u0186' | + '\u0187' | + '\u0189'..'\u018B' | + '\u018E'..'\u0191' | + '\u0193' | + '\u0194' | + '\u0196'..'\u0198' | + '\u019C' | + '\u019D' | + '\u019F' | + '\u01A0' | + '\u01A2' | + '\u01A4' | + '\u01A6' | + '\u01A7' | + '\u01A9' | + '\u01AC' | + '\u01AE' | + '\u01AF' | + '\u01B1'..'\u01B3' | + '\u01B5' | + '\u01B7' | + '\u01B8' | + '\u01BC' | + '\u01C4' | + '\u01C7' | + '\u01CA' | + '\u01CD' | + '\u01CF' | + '\u01D1' | + '\u01D3' | + '\u01D5' | + '\u01D7' | + '\u01D9' | + '\u01DB' | + '\u01DE' | + '\u01E0' | + '\u01E2' | + '\u01E4' | + '\u01E6' | + '\u01E8' | + '\u01EA' | + '\u01EC' | + '\u01EE' | + '\u01F1' | + '\u01F4' | + '\u01F6'..'\u01F8' | + '\u01FA' | + '\u01FC' | + '\u01FE' | + '\u0200' | + '\u0202' | + '\u0204' | + '\u0206' | + '\u0208' | + '\u020A' | + '\u020C' | + '\u020E' | + '\u0210' | + '\u0212' | + '\u0214' | + '\u0216' | + '\u0218' | + '\u021A' | + '\u021C' | + '\u021E' | + '\u0220' | + '\u0222' | + '\u0224' | + '\u0226' | + '\u0228' | + '\u022A' | + '\u022C' | + '\u022E' | + '\u0230' | + '\u0232' | + '\u023A' | + '\u023B' | + '\u023D' | + '\u023E' | + '\u0241' | + '\u0243'..'\u0246' | + '\u0248' | + '\u024A' | + '\u024C' | + '\u024E' | + '\u0370' | + '\u0372' | + '\u0376' | + '\u0386' | + '\u0388'..'\u038A' | + '\u038C' | + '\u038E' | + '\u038F' | + '\u0391'..'\u03A1' | + '\u03A3'..'\u03AB' | + '\u03CF' | + '\u03D2'..'\u03D4' | + '\u03D8' | + '\u03DA' | + '\u03DC' | + '\u03DE' | + '\u03E0' | + '\u03E2' | + '\u03E4' | + '\u03E6' | + '\u03E8' | + '\u03EA' | + '\u03EC' | + '\u03EE' | + '\u03F4' | + '\u03F7' | + '\u03F9' | + '\u03FA' | + '\u03FD'..'\u042F' | + '\u0460' | + '\u0462' | + '\u0464' | + '\u0466' | + '\u0468' | + '\u046A' | + '\u046C' | + '\u046E' | + '\u0470' | + '\u0472' | + '\u0474' | + '\u0476' | + '\u0478' | + '\u047A' | + '\u047C' | + '\u047E' | + '\u0480' | + '\u048A' | + '\u048C' | + '\u048E' | + '\u0490' | + '\u0492' | + '\u0494' | + '\u0496' | + '\u0498' | + '\u049A' | + '\u049C' | + '\u049E' | + '\u04A0' | + '\u04A2' | + '\u04A4' | + '\u04A6' | + '\u04A8' | + '\u04AA' | + '\u04AC' | + '\u04AE' | + '\u04B0' | + '\u04B2' | + '\u04B4' | + '\u04B6' | + '\u04B8' | + '\u04BA' | + '\u04BC' | + '\u04BE' | + '\u04C0' | + '\u04C1' | + '\u04C3' | + '\u04C5' | + '\u04C7' | + '\u04C9' | + '\u04CB' | + '\u04CD' | + '\u04D0' | + '\u04D2' | + '\u04D4' | + '\u04D6' | + '\u04D8' | + '\u04DA' | + '\u04DC' | + '\u04DE' | + '\u04E0' | + '\u04E2' | + '\u04E4' | + '\u04E6' | + '\u04E8' | + '\u04EA' | + '\u04EC' | + '\u04EE' | + '\u04F0' | + '\u04F2' | + '\u04F4' | + '\u04F6' | + '\u04F8' | + '\u04FA' | + '\u04FC' | + '\u04FE' | + '\u0500' | + '\u0502' | + '\u0504' | + '\u0506' | + '\u0508' | + '\u050A' | + '\u050C' | + '\u050E' | + '\u0510' | + '\u0512' | + '\u0514' | + '\u0516' | + '\u0518' | + '\u051A' | + '\u051C' | + '\u051E' | + '\u0520' | + '\u0522' | + '\u0524' | + '\u0526' | + '\u0531'..'\u0556' | + '\u10A0'..'\u10C5' | + '\u10C7' | + '\u10CD' | + '\u1E00' | + '\u1E02' | + '\u1E04' | + '\u1E06' | + '\u1E08' | + '\u1E0A' | + '\u1E0C' | + '\u1E0E' | + '\u1E10' | + '\u1E12' | + '\u1E14' | + '\u1E16' | + '\u1E18' | + '\u1E1A' | + '\u1E1C' | + '\u1E1E' | + '\u1E20' | + '\u1E22' | + '\u1E24' | + '\u1E26' | + '\u1E28' | + '\u1E2A' | + '\u1E2C' | + '\u1E2E' | + '\u1E30' | + '\u1E32' | + '\u1E34' | + '\u1E36' | + '\u1E38' | + '\u1E3A' | + '\u1E3C' | + '\u1E3E' | + '\u1E40' | + '\u1E42' | + '\u1E44' | + '\u1E46' | + '\u1E48' | + '\u1E4A' | + '\u1E4C' | + '\u1E4E' | + '\u1E50' | + '\u1E52' | + '\u1E54' | + '\u1E56' | + '\u1E58' | + '\u1E5A' | + '\u1E5C' | + '\u1E5E' | + '\u1E60' | + '\u1E62' | + '\u1E64' | + '\u1E66' | + '\u1E68' | + '\u1E6A' | + '\u1E6C' | + '\u1E6E' | + '\u1E70' | + '\u1E72' | + '\u1E74' | + '\u1E76' | + '\u1E78' | + '\u1E7A' | + '\u1E7C' | + '\u1E7E' | + '\u1E80' | + '\u1E82' | + '\u1E84' | + '\u1E86' | + '\u1E88' | + '\u1E8A' | + '\u1E8C' | + '\u1E8E' | + '\u1E90' | + '\u1E92' | + '\u1E94' | + '\u1E9E' | + '\u1EA0' | + '\u1EA2' | + '\u1EA4' | + '\u1EA6' | + '\u1EA8' | + '\u1EAA' | + '\u1EAC' | + '\u1EAE' | + '\u1EB0' | + '\u1EB2' | + '\u1EB4' | + '\u1EB6' | + '\u1EB8' | + '\u1EBA' | + '\u1EBC' | + '\u1EBE' | + '\u1EC0' | + '\u1EC2' | + '\u1EC4' | + '\u1EC6' | + '\u1EC8' | + '\u1ECA' | + '\u1ECC' | + '\u1ECE' | + '\u1ED0' | + '\u1ED2' | + '\u1ED4' | + '\u1ED6' | + '\u1ED8' | + '\u1EDA' | + '\u1EDC' | + '\u1EDE' | + '\u1EE0' | + '\u1EE2' | + '\u1EE4' | + '\u1EE6' | + '\u1EE8' | + '\u1EEA' | + '\u1EEC' | + '\u1EEE' | + '\u1EF0' | + '\u1EF2' | + '\u1EF4' | + '\u1EF6' | + '\u1EF8' | + '\u1EFA' | + '\u1EFC' | + '\u1EFE' | + '\u1F08'..'\u1F0F' | + '\u1F18'..'\u1F1D' | + '\u1F28'..'\u1F2F' | + '\u1F38'..'\u1F3F' | + '\u1F48'..'\u1F4D' | + '\u1F59' | + '\u1F5B' | + '\u1F5D' | + '\u1F5F' | + '\u1F68'..'\u1F6F' | + '\u1FB8'..'\u1FBB' | + '\u1FC8'..'\u1FCB' | + '\u1FD8'..'\u1FDB' | + '\u1FE8'..'\u1FEC' | + '\u1FF8'..'\u1FFB' | + '\u2102' | + '\u2107' | + '\u210B'..'\u210D' | + '\u2110'..'\u2112' | + '\u2115' | + '\u2119'..'\u211D' | + '\u2124' | + '\u2126' | + '\u2128' | + '\u212A'..'\u212D' | + '\u2130'..'\u2133' | + '\u213E' | + '\u213F' | + '\u2145' | + '\u2183' | + '\u2C00'..'\u2C2E' | + '\u2C60' | + '\u2C62'..'\u2C64' | + '\u2C67' | + '\u2C69' | + '\u2C6B' | + '\u2C6D'..'\u2C70' | + '\u2C72' | + '\u2C75' | + '\u2C7E'..'\u2C80' | + '\u2C82' | + '\u2C84' | + '\u2C86' | + '\u2C88' | + '\u2C8A' | + '\u2C8C' | + '\u2C8E' | + '\u2C90' | + '\u2C92' | + '\u2C94' | + '\u2C96' | + '\u2C98' | + '\u2C9A' | + '\u2C9C' | + '\u2C9E' | + '\u2CA0' | + '\u2CA2' | + '\u2CA4' | + '\u2CA6' | + '\u2CA8' | + '\u2CAA' | + '\u2CAC' | + '\u2CAE' | + '\u2CB0' | + '\u2CB2' | + '\u2CB4' | + '\u2CB6' | + '\u2CB8' | + '\u2CBA' | + '\u2CBC' | + '\u2CBE' | + '\u2CC0' | + '\u2CC2' | + '\u2CC4' | + '\u2CC6' | + '\u2CC8' | + '\u2CCA' | + '\u2CCC' | + '\u2CCE' | + '\u2CD0' | + '\u2CD2' | + '\u2CD4' | + '\u2CD6' | + '\u2CD8' | + '\u2CDA' | + '\u2CDC' | + '\u2CDE' | + '\u2CE0' | + '\u2CE2' | + '\u2CEB' | + '\u2CED' | + '\u2CF2' | + '\uA640' | + '\uA642' | + '\uA644' | + '\uA646' | + '\uA648' | + '\uA64A' | + '\uA64C' | + '\uA64E' | + '\uA650' | + '\uA652' | + '\uA654' | + '\uA656' | + '\uA658' | + '\uA65A' | + '\uA65C' | + '\uA65E' | + '\uA660' | + '\uA662' | + '\uA664' | + '\uA666' | + '\uA668' | + '\uA66A' | + '\uA66C' | + '\uA680' | + '\uA682' | + '\uA684' | + '\uA686' | + '\uA688' | + '\uA68A' | + '\uA68C' | + '\uA68E' | + '\uA690' | + '\uA692' | + '\uA694' | + '\uA696' | + '\uA722' | + '\uA724' | + '\uA726' | + '\uA728' | + '\uA72A' | + '\uA72C' | + '\uA72E' | + '\uA732' | + '\uA734' | + '\uA736' | + '\uA738' | + '\uA73A' | + '\uA73C' | + '\uA73E' | + '\uA740' | + '\uA742' | + '\uA744' | + '\uA746' | + '\uA748' | + '\uA74A' | + '\uA74C' | + '\uA74E' | + '\uA750' | + '\uA752' | + '\uA754' | + '\uA756' | + '\uA758' | + '\uA75A' | + '\uA75C' | + '\uA75E' | + '\uA760' | + '\uA762' | + '\uA764' | + '\uA766' | + '\uA768' | + '\uA76A' | + '\uA76C' | + '\uA76E' | + '\uA779' | + '\uA77B' | + '\uA77D' | + '\uA77E' | + '\uA780' | + '\uA782' | + '\uA784' | + '\uA786' | + '\uA78B' | + '\uA78D' | + '\uA790' | + '\uA792' | + '\uA7A0' | + '\uA7A2' | + '\uA7A4' | + '\uA7A6' | + '\uA7A8' | + '\uA7AA' | + '\uFF21'..'\uFF3A'; + +UNICODE_CLASS_ND: + '\u0030'..'\u0039' | + '\u0660'..'\u0669' | + '\u06F0'..'\u06F9' | + '\u07C0'..'\u07C9' | + '\u0966'..'\u096F' | + '\u09E6'..'\u09EF' | + '\u0A66'..'\u0A6F' | + '\u0AE6'..'\u0AEF' | + '\u0B66'..'\u0B6F' | + '\u0BE6'..'\u0BEF' | + '\u0C66'..'\u0C6F' | + '\u0CE6'..'\u0CEF' | + '\u0D66'..'\u0D6F' | + '\u0E50'..'\u0E59' | + '\u0ED0'..'\u0ED9' | + '\u0F20'..'\u0F29' | + '\u1040'..'\u1049' | + '\u1090'..'\u1099' | + '\u17E0'..'\u17E9' | + '\u1810'..'\u1819' | + '\u1946'..'\u194F' | + '\u19D0'..'\u19D9' | + '\u1A80'..'\u1A89' | + '\u1A90'..'\u1A99' | + '\u1B50'..'\u1B59' | + '\u1BB0'..'\u1BB9' | + '\u1C40'..'\u1C49' | + '\u1C50'..'\u1C59' | + '\uA620'..'\uA629' | + '\uA8D0'..'\uA8D9' | + '\uA900'..'\uA909' | + '\uA9D0'..'\uA9D9' | + '\uAA50'..'\uAA59' | + '\uABF0'..'\uABF9' | + '\uFF10'..'\uFF19'; + +UNICODE_CLASS_NL: + '\u16EE'..'\u16F0' | + '\u2160'..'\u2182' | + '\u2185'..'\u2188' | + '\u3007' | + '\u3021'..'\u3029' | + '\u3038'..'\u303A' | + '\uA6E6'..'\uA6EF'; \ No newline at end of file diff --git a/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.tokens b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.tokens new file mode 100644 index 0000000000..bd91da7e6b --- /dev/null +++ b/pmd-kotlin/src/main/antlr4/net/sourceforge/pmd/lang/kotlin/ast/UnicodeClasses.tokens @@ -0,0 +1,7 @@ +UNICODE_CLASS_LL=1 +UNICODE_CLASS_LM=2 +UNICODE_CLASS_LO=3 +UNICODE_CLASS_LT=4 +UNICODE_CLASS_LU=5 +UNICODE_CLASS_ND=6 +UNICODE_CLASS_NL=7 diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/AbstractKotlinRule.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/AbstractKotlinRule.java new file mode 100644 index 0000000000..31262b1473 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/AbstractKotlinRule.java @@ -0,0 +1,19 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseRule; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinVisitor; + +public abstract class AbstractKotlinRule extends AntlrBaseRule { + + protected AbstractKotlinRule() { + // inheritance constructor + } + + @Override + public abstract KotlinVisitor buildVisitor(); +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinHandler.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinHandler.java new file mode 100644 index 0000000000..4302bf39d4 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinHandler.java @@ -0,0 +1,26 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin; + +import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; +import net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.kotlin.ast.PmdKotlinParser; +import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; + + +public class KotlinHandler extends AbstractPmdLanguageVersionHandler { + + private static final XPathHandler XPATH_HANDLER = XPathHandler.noFunctionDefinitions(); + + @Override + public XPathHandler getXPathHandler() { + return XPATH_HANDLER; + } + + @Override + public Parser getParser() { + return new PmdKotlinParser(); + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java new file mode 100644 index 0000000000..e8b9c7fe50 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java @@ -0,0 +1,26 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin; + +import net.sourceforge.pmd.lang.BaseLanguageModule; + +/** + * Language Module for Kotlin + */ +public class KotlinLanguageModule extends BaseLanguageModule { + + /** The name. */ + public static final String NAME = "Kotlin"; + /** The terse name. */ + public static final String TERSE_NAME = "kotlin"; + + /** + * Create a new instance of Kotlin Language Module. + */ + public KotlinLanguageModule() { + super(NAME, null, TERSE_NAME, "kt", "ktm"); + addDefaultVersion("1.6-rfc+0.1", new KotlinHandler(), "1.6"); + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinErrorNode.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinErrorNode.java new file mode 100644 index 0000000000..f1b2f686d0 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinErrorNode.java @@ -0,0 +1,17 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.Token; + +import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrErrorNode; + +public final class KotlinErrorNode extends BaseAntlrErrorNode implements KotlinNode { + + KotlinErrorNode(Token token) { + super(token); + } + +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java new file mode 100644 index 0000000000..a6d58699ae --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java @@ -0,0 +1,38 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.ParserRuleContext; + +import net.sourceforge.pmd.lang.ast.AstVisitor; +import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrInnerNode; + +public abstract class KotlinInnerNode + extends BaseAntlrInnerNode implements KotlinNode { + + KotlinInnerNode(ParserRuleContext parent, int invokingStateNumber) { + super(parent, invokingStateNumber); + } + + @Override + public R acceptVisitor(AstVisitor visitor, P data) { + if (visitor instanceof KotlinVisitor) { + // some of the generated antlr nodes have no accept method... + return ((KotlinVisitor) visitor).visitKotlinNode(this, data); + } + return visitor.visitNode(this, data); + } + + + @Override // override to make visible in package + protected PmdAsAntlrInnerNode asAntlrNode() { + return super.asAntlrNode(); + } + + @Override + public String getXPathNodeName() { + return KotlinParser.DICO.getXPathNameOfRule(getRuleIndex()); + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java new file mode 100644 index 0000000000..387717bb53 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java @@ -0,0 +1,24 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.Vocabulary; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrNameDictionary; + + +final class KotlinNameDictionary extends AntlrNameDictionary { + + KotlinNameDictionary(Vocabulary vocab, String[] ruleNames) { + super(vocab, ruleNames); + } + + @Override + protected @Nullable String nonAlphaNumName(String name) { + // todo + return super.nonAlphaNumName(name); + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNode.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNode.java new file mode 100644 index 0000000000..6bb32f677b --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNode.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrNode; + +/** + * Supertype of all kotlin nodes. + */ +public interface KotlinNode extends AntlrNode { +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinRootNode.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinRootNode.java new file mode 100644 index 0000000000..43fa0804f7 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinRootNode.java @@ -0,0 +1,34 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.ParserRuleContext; + +import net.sourceforge.pmd.lang.ast.AstInfo; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtKotlinFile; + +// package private base class +abstract class KotlinRootNode extends KotlinInnerNode implements RootNode { + + private AstInfo astInfo; + + KotlinRootNode(ParserRuleContext parent, int invokingStateNumber) { + super(parent, invokingStateNumber); + } + + @Override + public AstInfo getAstInfo() { + return astInfo; + } + + KtKotlinFile makeAstInfo(ParserTask task) { + KtKotlinFile me = (KtKotlinFile) this; + this.astInfo = new AstInfo<>(task, me); + return me; + } + +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinTerminalNode.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinTerminalNode.java new file mode 100644 index 0000000000..22f8b49628 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinTerminalNode.java @@ -0,0 +1,42 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.Token; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.ast.AstVisitor; +import net.sourceforge.pmd.lang.ast.impl.antlr4.BaseAntlrTerminalNode; + +public final class KotlinTerminalNode extends BaseAntlrTerminalNode implements KotlinNode { + + + KotlinTerminalNode(Token token) { + super(token); + } + + + @Override + public @NonNull String getText() { + String constImage = KotlinParser.DICO.getConstantImageOfToken(getFirstAntlrToken()); + return constImage == null ? getFirstAntlrToken().getText() : constImage; + } + + + @Override + public String getXPathNodeName() { + return KotlinParser.DICO.getXPathNameOfToken(getFirstAntlrToken().getType()); + } + + + @Override + public R acceptVisitor(AstVisitor visitor, P data) { + if (visitor instanceof KotlinVisitor) { + return ((KotlinVisitor) visitor).visitKotlinNode(this, data); + } + return super.acceptVisitor(visitor, data); + } + +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinVisitorBase.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinVisitorBase.java new file mode 100644 index 0000000000..b6b0054400 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinVisitorBase.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import net.sourceforge.pmd.lang.ast.AstVisitorBase; + +/** + * Base class for kotlin visitors. + */ +public abstract class KotlinVisitorBase extends AstVisitorBase implements KotlinVisitor { + +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/PmdKotlinParser.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/PmdKotlinParser.java new file mode 100644 index 0000000000..8ad3a430d1 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/PmdKotlinParser.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Lexer; + +import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrBaseParser; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtKotlinFile; + +/** + * Adapter for the KotlinParser. + */ +public final class PmdKotlinParser extends AntlrBaseParser { + + @Override + protected KtKotlinFile parse(final Lexer lexer, ParserTask task) { + KotlinParser parser = new KotlinParser(new CommonTokenStream(lexer)); + return parser.kotlinFile().makeAstInfo(task); + } + + @Override + protected Lexer getLexer(final CharStream source) { + return new KotlinLexer(source); + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java new file mode 100644 index 0000000000..224ea1b7b0 --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -0,0 +1,81 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +package net.sourceforge.pmd.lang.kotlin.rule.errorprone; + +import java.util.List; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.kotlin.AbstractKotlinRule; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtClassDeclaration; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtClassMemberDeclaration; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtClassMemberDeclarations; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtDeclaration; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtFunctionDeclaration; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinTerminalNode; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinVisitor; +import net.sourceforge.pmd.lang.kotlin.ast.KotlinVisitorBase; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; + +public class OverrideBothEqualsAndHashcodeRule extends AbstractKotlinRule { + + private static final Visitor INSTANCE = new Visitor(); + + @Override + public KotlinVisitor buildVisitor() { + return INSTANCE; + } + + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(KtClassMemberDeclarations.class); + } + + private static final class Visitor extends KotlinVisitorBase { + @Override + public Void visitClassMemberDeclarations(KtClassMemberDeclarations node, RuleContext data) { + List functions = node.children(KtClassMemberDeclaration.class) + .children(KtDeclaration.class) + .children(KtFunctionDeclaration.class) + .toList(); + + boolean hasEqualMethod = functions.stream().filter(this::isEqualsMethod).count() == 1L; + boolean hasHashCodeMethod = functions.stream().filter(this::isHashCodeMethod).count() == 1L; + + if (hasEqualMethod ^ hasHashCodeMethod) { + data.addViolation(node.ancestors(KtClassDeclaration.class).first()); + } + + return super.visitClassMemberDeclarations(node, data); + } + + private boolean isEqualsMethod(KtFunctionDeclaration fun) { + String name = getFunctionName(fun); + int arity = getArity(fun); + return "equals".equals(name) && hasOverrideModifier(fun) && arity == 1; + } + + private boolean isHashCodeMethod(KtFunctionDeclaration fun) { + String name = getFunctionName(fun); + int arity = getArity(fun); + return "hashCode".equals(name) && hasOverrideModifier(fun) && arity == 0; + } + + private String getFunctionName(KtFunctionDeclaration fun) { + return fun.simpleIdentifier().children(KotlinTerminalNode.class).first().getText(); + } + + private boolean hasOverrideModifier(KtFunctionDeclaration fun) { + return fun.modifiers().descendants(KotlinTerminalNode.class) + .any(t -> "override".equals(t.getText())); + } + + private int getArity(KtFunctionDeclaration fun) { + return fun.functionValueParameters().functionValueParameter().size(); + } + } +} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/xpath/internal/BaseKotlinXPathFunction.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/xpath/internal/BaseKotlinXPathFunction.java new file mode 100644 index 0000000000..8550b4ab6f --- /dev/null +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/rule/xpath/internal/BaseKotlinXPathFunction.java @@ -0,0 +1,15 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.rule.xpath.internal; + +import net.sourceforge.pmd.lang.kotlin.KotlinLanguageModule; +import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef; + +abstract class BaseKotlinXPathFunction extends AbstractXPathFunctionDef { + + protected BaseKotlinXPathFunction(String localName) { + super(localName, KotlinLanguageModule.TERSE_NAME); + } +} diff --git a/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..78c4e9b40d --- /dev/null +++ b/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.kotlin.KotlinLanguageModule diff --git a/pmd-kotlin/src/main/resources/category/kotlin/bestpractices.xml b/pmd-kotlin/src/main/resources/category/kotlin/bestpractices.xml new file mode 100644 index 0000000000..30a88668f0 --- /dev/null +++ b/pmd-kotlin/src/main/resources/category/kotlin/bestpractices.xml @@ -0,0 +1,38 @@ + + + + + +Rules which enforce generally accepted best practices. + + + + + Function names should be easy to understand and describe the intention. Makes developers happy. + + 3 + + + + + + + + + + + diff --git a/pmd-kotlin/src/main/resources/category/kotlin/categories.properties b/pmd-kotlin/src/main/resources/category/kotlin/categories.properties new file mode 100644 index 0000000000..e0ff81574a --- /dev/null +++ b/pmd-kotlin/src/main/resources/category/kotlin/categories.properties @@ -0,0 +1,18 @@ +# +# BSD-style license; for more info see http://pmd.sourceforge.net/license.html +# + +rulesets.filenames=\ + category/kotlin/bestpractices.xml,\ + category/kotlin/errorprone.xml + +# +# categories without rules +# + +# category/kotlin/codestyle.xml +# category/kotlin/design.xml +# category/kotlin/documentation.xml +# category/kotlin/multithreading.xml +# category/kotlin/performance.xml +# category/kotlin/security.xml diff --git a/pmd-kotlin/src/main/resources/category/kotlin/errorprone.xml b/pmd-kotlin/src/main/resources/category/kotlin/errorprone.xml new file mode 100644 index 0000000000..d0e5983f49 --- /dev/null +++ b/pmd-kotlin/src/main/resources/category/kotlin/errorprone.xml @@ -0,0 +1,48 @@ + + + + + +Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. + + + + +Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. + + 3 + + + + + + diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/RuleSetFactoryTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/RuleSetFactoryTest.java new file mode 100644 index 0000000000..ab10c8550b --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/RuleSetFactoryTest.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin; + +import net.sourceforge.pmd.AbstractRuleSetFactoryTest; + +public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { +} diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/BaseKotlinTreeDumpTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/BaseKotlinTreeDumpTest.java new file mode 100644 index 0000000000..3541b69096 --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/BaseKotlinTreeDumpTest.java @@ -0,0 +1,26 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.NodePrintersKt; + +/** + * + */ +public class BaseKotlinTreeDumpTest extends BaseTreeDumpTest { + + public BaseKotlinTreeDumpTest() { + super(NodePrintersKt.getSimpleNodePrinter(), ".kt"); + } + + @NonNull + @Override + public KotlinParsingHelper getParser() { + return KotlinParsingHelper.DEFAULT.withResourceContext(getClass(), "testdata"); + } +} diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParserTests.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParserTests.java new file mode 100644 index 0000000000..bd1944ae31 --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParserTests.java @@ -0,0 +1,19 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.junit.Test; + +/** + * + */ +public class KotlinParserTests extends BaseKotlinTreeDumpTest { + + @Test + public void testSimpleKotlin() { + doTest("Simple"); + } + +} diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParsingHelper.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParsingHelper.java new file mode 100644 index 0000000000..43cf4898e9 --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinParsingHelper.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast; + +import org.jetbrains.annotations.NotNull; + +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.kotlin.KotlinLanguageModule; + +/** + * + */ +public class KotlinParsingHelper extends BaseParsingHelper { + + public static final KotlinParsingHelper DEFAULT = new KotlinParsingHelper(Params.getDefault()); + + + public KotlinParsingHelper(@NotNull Params params) { + super(KotlinLanguageModule.NAME, KotlinParser.KtKotlinFile.class, params); + } + + @NotNull + @Override + protected KotlinParsingHelper clone(@NotNull Params params) { + return new KotlinParsingHelper(params); + } +} diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/FunctionNameTooShortTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/FunctionNameTooShortTest.java new file mode 100644 index 0000000000..4dd4772eb9 --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/FunctionNameTooShortTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.rule.bestpractices; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class FunctionNameTooShortTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java new file mode 100644 index 0000000000..7649acfaec --- /dev/null +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java @@ -0,0 +1,11 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.rule.errorprone; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class OverrideBothEqualsAndHashcodeTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.kt b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.kt new file mode 100644 index 0000000000..2cc0f1590e --- /dev/null +++ b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.kt @@ -0,0 +1,16 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.kotlin.ast.testdata + +class Simple { + private val name = "Simple" + fun info() = "This is $name class" +} + +fun main() { + val s = Simple() + println(s) + println(s.info()) +} diff --git a/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.txt b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.txt new file mode 100644 index 0000000000..00f70e058f --- /dev/null +++ b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/ast/testdata/Simple.txt @@ -0,0 +1,270 @@ ++- KotlinFile + +- T-NL + +- T-NL + +- PackageHeader + | +- T-PACKAGE + | +- Identifier + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- T-DOT + | | +- SimpleIdentifier + | | +- T-Identifier + | +- Semi + | +- T-NL + | +- T-NL + +- ImportList + +- TopLevelObject + | +- Declaration + | | +- ClassDeclaration + | | +- T-CLASS + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- ClassBody + | | +- T-LCURL + | | +- T-NL + | | +- ClassMemberDeclarations + | | | +- ClassMemberDeclaration + | | | | +- Declaration + | | | | +- PropertyDeclaration + | | | | +- Modifiers + | | | | | +- Modifier + | | | | | +- VisibilityModifier + | | | | | +- T-PRIVATE + | | | | +- T-VAL + | | | | +- VariableDeclaration + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- T-ASSIGNMENT + | | | | +- Expression + | | | | | +- Disjunction + | | | | | +- Conjunction + | | | | | +- Equality + | | | | | +- Comparison + | | | | | +- GenericCallLikeComparison + | | | | | +- InfixOperation + | | | | | +- ElvisExpression + | | | | | +- InfixFunctionCall + | | | | | +- RangeExpression + | | | | | +- AdditiveExpression + | | | | | +- MultiplicativeExpression + | | | | | +- AsExpression + | | | | | +- PrefixUnaryExpression + | | | | | +- PostfixUnaryExpression + | | | | | +- PrimaryExpression + | | | | | +- StringLiteral + | | | | | +- LineStringLiteral + | | | | | +- T-QUOTE_OPEN + | | | | | +- LineStringContent + | | | | | | +- T-LineStrText + | | | | | +- T-QUOTE_CLOSE + | | | | +- T-NL + | | | +- ClassMemberDeclaration + | | | | +- Declaration + | | | | +- FunctionDeclaration + | | | | +- T-FUN + | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- FunctionValueParameters + | | | | | +- T-LPAREN + | | | | | +- T-RPAREN + | | | | +- FunctionBody + | | | | +- T-ASSIGNMENT + | | | | +- Expression + | | | | +- Disjunction + | | | | +- Conjunction + | | | | +- Equality + | | | | +- Comparison + | | | | +- GenericCallLikeComparison + | | | | +- InfixOperation + | | | | +- ElvisExpression + | | | | +- InfixFunctionCall + | | | | +- RangeExpression + | | | | +- AdditiveExpression + | | | | +- MultiplicativeExpression + | | | | +- AsExpression + | | | | +- PrefixUnaryExpression + | | | | +- PostfixUnaryExpression + | | | | +- PrimaryExpression + | | | | +- StringLiteral + | | | | +- LineStringLiteral + | | | | +- T-QUOTE_OPEN + | | | | +- LineStringContent + | | | | | +- T-LineStrText + | | | | +- LineStringContent + | | | | | +- T-LineStrRef + | | | | +- LineStringContent + | | | | | +- T-LineStrText + | | | | +- T-QUOTE_CLOSE + | | | +- Semis + | | | +- T-NL + | | +- T-RCURL + | +- Semis + | +- T-NL + | +- T-NL + +- TopLevelObject + | +- Declaration + | | +- FunctionDeclaration + | | +- T-FUN + | | +- SimpleIdentifier + | | | +- T-Identifier + | | +- FunctionValueParameters + | | | +- T-LPAREN + | | | +- T-RPAREN + | | +- FunctionBody + | | +- Block + | | +- T-LCURL + | | +- T-NL + | | +- Statements + | | | +- Statement + | | | | +- Declaration + | | | | +- PropertyDeclaration + | | | | +- T-VAL + | | | | +- VariableDeclaration + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- T-ASSIGNMENT + | | | | +- Expression + | | | | +- Disjunction + | | | | +- Conjunction + | | | | +- Equality + | | | | +- Comparison + | | | | +- GenericCallLikeComparison + | | | | +- InfixOperation + | | | | +- ElvisExpression + | | | | +- InfixFunctionCall + | | | | +- RangeExpression + | | | | +- AdditiveExpression + | | | | +- MultiplicativeExpression + | | | | +- AsExpression + | | | | +- PrefixUnaryExpression + | | | | +- PostfixUnaryExpression + | | | | +- PrimaryExpression + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- PostfixUnarySuffix + | | | | +- CallSuffix + | | | | +- ValueArguments + | | | | +- T-LPAREN + | | | | +- T-RPAREN + | | | +- Semis + | | | | +- T-NL + | | | +- Statement + | | | | +- Expression + | | | | +- Disjunction + | | | | +- Conjunction + | | | | +- Equality + | | | | +- Comparison + | | | | +- GenericCallLikeComparison + | | | | +- InfixOperation + | | | | +- ElvisExpression + | | | | +- InfixFunctionCall + | | | | +- RangeExpression + | | | | +- AdditiveExpression + | | | | +- MultiplicativeExpression + | | | | +- AsExpression + | | | | +- PrefixUnaryExpression + | | | | +- PostfixUnaryExpression + | | | | +- PrimaryExpression + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- PostfixUnarySuffix + | | | | +- CallSuffix + | | | | +- ValueArguments + | | | | +- T-LPAREN + | | | | +- ValueArgument + | | | | | +- Expression + | | | | | +- Disjunction + | | | | | +- Conjunction + | | | | | +- Equality + | | | | | +- Comparison + | | | | | +- GenericCallLikeComparison + | | | | | +- InfixOperation + | | | | | +- ElvisExpression + | | | | | +- InfixFunctionCall + | | | | | +- RangeExpression + | | | | | +- AdditiveExpression + | | | | | +- MultiplicativeExpression + | | | | | +- AsExpression + | | | | | +- PrefixUnaryExpression + | | | | | +- PostfixUnaryExpression + | | | | | +- PrimaryExpression + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- T-RPAREN + | | | +- Semis + | | | | +- T-NL + | | | +- Statement + | | | | +- Expression + | | | | +- Disjunction + | | | | +- Conjunction + | | | | +- Equality + | | | | +- Comparison + | | | | +- GenericCallLikeComparison + | | | | +- InfixOperation + | | | | +- ElvisExpression + | | | | +- InfixFunctionCall + | | | | +- RangeExpression + | | | | +- AdditiveExpression + | | | | +- MultiplicativeExpression + | | | | +- AsExpression + | | | | +- PrefixUnaryExpression + | | | | +- PostfixUnaryExpression + | | | | +- PrimaryExpression + | | | | | +- SimpleIdentifier + | | | | | +- T-Identifier + | | | | +- PostfixUnarySuffix + | | | | +- CallSuffix + | | | | +- ValueArguments + | | | | +- T-LPAREN + | | | | +- ValueArgument + | | | | | +- Expression + | | | | | +- Disjunction + | | | | | +- Conjunction + | | | | | +- Equality + | | | | | +- Comparison + | | | | | +- GenericCallLikeComparison + | | | | | +- InfixOperation + | | | | | +- ElvisExpression + | | | | | +- InfixFunctionCall + | | | | | +- RangeExpression + | | | | | +- AdditiveExpression + | | | | | +- MultiplicativeExpression + | | | | | +- AsExpression + | | | | | +- PrefixUnaryExpression + | | | | | +- PostfixUnaryExpression + | | | | | +- PrimaryExpression + | | | | | | +- SimpleIdentifier + | | | | | | +- T-Identifier + | | | | | +- PostfixUnarySuffix + | | | | | | +- NavigationSuffix + | | | | | | +- MemberAccessOperator + | | | | | | | +- T-DOT + | | | | | | +- SimpleIdentifier + | | | | | | +- T-Identifier + | | | | | +- PostfixUnarySuffix + | | | | | +- CallSuffix + | | | | | +- ValueArguments + | | | | | +- T-LPAREN + | | | | | +- T-RPAREN + | | | | +- T-RPAREN + | | | +- Semis + | | | +- T-NL + | | +- T-RCURL + | +- Semis + | +- T-NL + +- EOF diff --git a/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/xml/FunctionNameTooShort.xml b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/xml/FunctionNameTooShort.xml new file mode 100644 index 0000000000..01067c1111 --- /dev/null +++ b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/bestpractices/xml/FunctionNameTooShort.xml @@ -0,0 +1,28 @@ + + + + + Good example #1 + 0 + + + + Bad example #1 + 2 + 2,3 + + + diff --git a/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml new file mode 100644 index 0000000000..e95842b726 --- /dev/null +++ b/pmd-kotlin/src/test/resources/net/sourceforge/pmd/lang/kotlin/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml @@ -0,0 +1,47 @@ + + + + + Good example #1 + 0 + + + + + Bad example #1 - missing hashCode + 1 + 1 + + + + + Bad example #2 - missing equals + 1 + 1 + + + diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index 3f1e9bd3e8..aacab56128 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -13,7 +13,7 @@ net.sourceforge.pmd pmd 7.0.0-SNAPSHOT - ../ + ../pom.xml @@ -112,10 +112,6 @@ org.apache.commons commons-lang3 - - commons-io - commons-io - + 6.47.0 net.sourceforge.pmd pmd-java - 6.45.0 + 6.47.0 net.sourceforge.pmd pmd-jsp - 6.45.0 + 6.47.0 net.sourceforge.pmd pmd-javascript - 6.45.0 + 6.47.0 @@ -444,6 +444,11 @@ + + org.apache.maven.plugins + maven-site-plugin + 4.0.0-M1 + org.codehaus.mojo versions-maven-plugin @@ -508,6 +513,33 @@ + + enforce-no-snapshots + + enforce + + + + + No Snapshots Allowed! + true + + + + + + enforce-plugin-versions + + enforce + + + + + Best Practice is to always define plugin versions! + + + + @@ -702,11 +734,6 @@ ${javacc.version} provided - - commons-io - commons-io - 2.6 - org.apache.commons commons-lang3 @@ -874,6 +901,15 @@ kotest-runner-junit5-jvm ${kotest.version} test + + + + commons-io + commons-io + + io.kotest @@ -900,6 +936,26 @@ 13.0 test + + + + io.github.classgraph + classgraph + 4.8.112 + + + + + com.google.protobuf + protobuf-java + 3.16.1 + @@ -952,6 +1008,17 @@ true + + apache.snapshots + Apache Snapshot Repository + https://repository.apache.org/snapshots + + false + + + true + +