diff --git a/.ci/README.md b/.ci/README.md index 8c1afbeab3..7614027b70 100644 --- a/.ci/README.md +++ b/.ci/README.md @@ -115,7 +115,7 @@ cd pmd git init git remote add origin https://github.com/pmd/pmd git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +refs/heads/${MAIN_BRANCH}:refs/remotes/origin/${MAIN_BRANCH} -git checkout --progress --force -B master refs/remotes/origin/${MAIN_BRANCH} +git checkout --progress --force -B ${MAIN_BRANCH} refs/remotes/origin/${MAIN_BRANCH} .ci/check-environment.sh @@ -159,9 +159,11 @@ You'll be dropped into a bash. Use the following script, to setup and start the ``` export MAVEN_OPTS="-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3" -export PMD_CI_BRANCH="master" # base branch +export PMD_CI_BRANCH="master" # base branch of the pull request export PMD_CI_PULL_REQUEST_NUMBER=2913 +unset PMD_CI_SECRET_PASSPHRASE + # these are used by danger export GITHUB_EVENT_PATH=/workspaces/event.json export GITHUB_REPOSITORY=pmd/pmd @@ -179,5 +181,5 @@ git checkout --progress --force refs/remotes/pull/${PMD_CI_PULL_REQUEST_NUMBER}/ .ci/check-environment.sh -.ci/build-pr.sh +.ci/build-pr-win-macos.sh ``` diff --git a/.ci/build.sh b/.ci/build.sh index ad838fd772..1341fe97d3 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -189,6 +189,7 @@ function pmd_ci_build_and_upload_doc() { gh_release_updateRelease "$GH_RELEASE" "$release_name" "$rendered_release_notes" sourceforge_uploadReleaseNotes "${VERSION}" "${rendered_release_notes}" + # updates https://pmd.github.io/latest/ and https://pmd.github.io/pmd-${VERSION} publish_release_documentation_github sourceforge_rsyncSnapshotDocumentation "${VERSION}" "pmd-${VERSION}" fi diff --git a/.ci/files/all-java.xml b/.ci/files/all-java.xml index 6c0838d795..da63fd7deb 100644 --- a/.ci/files/all-java.xml +++ b/.ci/files/all-java.xml @@ -1,41 +1,41 @@ - + Every java rule in PMD which is used for the regression tests with pmdtester - - + + - - + + - + - - - + + + - + - - - - - + + + + + @@ -46,7 +46,7 @@ - + @@ -54,70 +54,70 @@ - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - - - - + + + + - - + + - - - + + + - - + + - + - - - - - - - + + + + + + + - + - - + + - + @@ -152,7 +152,7 @@ - + @@ -168,27 +168,27 @@ - + - + - - + + - + - + @@ -198,11 +198,11 @@ - + - + @@ -213,18 +213,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + @@ -233,8 +233,8 @@ - - + + @@ -258,7 +258,7 @@ - + @@ -268,55 +268,55 @@ - + - - - - - - + + + + + + - - + + - - + + - + - + - + - + - - + + - - - - + + + + - + - + - + - - + + diff --git a/.ci/inc/maven-dependencies.inc b/.ci/inc/maven-dependencies.inc index 0aaa020b81..12d1de380a 100644 --- a/.ci/inc/maven-dependencies.inc +++ b/.ci/inc/maven-dependencies.inc @@ -20,7 +20,18 @@ function maven_dependencies_resolve() { dokka_version=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${dokka.version}' \ --non-recursive org.codehaus.mojo:exec-maven-plugin:3.0.0:exec) - ./mvnw dependency:resolve + # build first the modules, that have dependencies between themselves + # first build pmd-lang-test, pmd-test and pmd-core - used by all modules + ./mvnw clean install -pl pmd-core,pmd-test,pmd-lang-test -DskipTests -Dpmd.skip=true \ + -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true -Dmaven.source.skip=true + # then build dependencies for pmd-visualforce needs: pmd-apex->pmd-apex-jorje+pmd-test+pmd-core + ./mvnw clean install -pl pmd-core,pmd-test,pmd-lang-test,pmd-apex-jorje,pmd-apex -DskipTests -Dpmd.skip=true \ + -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true -Dmaven.source.skip=true + + # the resolve most other projects. The excluded projects depend on other projects in the reactor, which is not + # completely built yet, so these are excluded. + ./mvnw dependency:resolve -pl '!pmd-dist,!pmd-doc,!pmd-scala' + ./mvnw dependency:get -DgroupId=org.jetbrains.dokka \ -DartifactId=dokka-maven-plugin \ -Dversion=${dokka_version} \ diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 1b65d6f883..94f9864ae3 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -40,14 +40,17 @@ function regression_tester_setup_ci() { function regression_tester_uploadBaseline() { log_debug "$FUNCNAME branch=${PMD_CI_BRANCH}" local targetUrl="https://sourceforge.net/projects/pmd/files/pmd-regression-tester/" + local pmdcodeUrl="https://pmd-code.org/pmd-regression-tester/" local errexitstate="$(shopt -po errexit)" set +e # disable errexit ( # This handler is called if any command fails function upload_failed() { - log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!" - log_error "Please upload manually: ${targetUrl}" + log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to pmd-code.org!" + log_error "Please upload manually: ${pmdcodeUrl}" + #log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!" + #log_error "Please upload manually: ${targetUrl}" } # exit subshell after trap @@ -57,17 +60,21 @@ function regression_tester_uploadBaseline() { log_info "Generating and uploading baseline for pmdtester..." cd .. bundle config --local gemfile pmd/Gemfile - pmd/.ci/travis_wait "bundle exec pmdtester - --mode single - --local-git-repo ./pmd - --patch-branch ${PMD_CI_BRANCH} - --patch-config ./pmd/.ci/files/all-java.xml - --list-of-project ./pmd/.ci/files/project-list.xml --html-flag" + bundle exec pmdtester \ + --mode single \ + --local-git-repo ./pmd \ + --patch-branch ${PMD_CI_BRANCH} \ + --patch-config ./pmd/.ci/files/all-java.xml \ + --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ + --error-recovery cd target/reports BRANCH_FILENAME="${PMD_CI_BRANCH/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ - ../../pmd/.ci/travis_wait "rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/" - log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${targetUrl}" + # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh + scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ + log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" + #../../pmd/.ci/travis_wait "rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/" + #log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${targetUrl}" ) # restore errexit state eval "$errexitstate" @@ -100,8 +107,13 @@ function regression_tester_executeDanger() { git branch ${PMD_CI_BRANCH} origin/${PMD_CI_BRANCH} log_debug "Created local branch ${PMD_CI_BRANCH}" fi - # Fetch more commits of the PR for danger + # Fetch more commits of the PR for danger and regression tester git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): + # Fetch more commits from master branch for regression tester + if [[ "${PMD_CI_BRANCH}" != "master" ]]; then + git fetch --no-tags --depth=50 origin +master: + git branch master origin/master + fi log_info "Running danger on branch ${PMD_CI_BRANCH}" bundle exec danger --verbose diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 56a5a13ccb..738f691819 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Question + url: https://github.com/pmd/pmd/discussions?discussions_q=category%3AQ%26A + about: Feel free to ask any question about PMD and its usage - name: PMD Designer Issues url: https://github.com/pmd/pmd-designer/issues about: Issues about the rule designer diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 5af12acfa1..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Question -about: Feel free to ask any question about PMD and its usage -title: '' -labels: 'a:question' -assignees: '' - ---- - - - -**Description:** - diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index a07af8d1e5..169f000a80 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -21,9 +21,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: pr-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + pr-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: diff --git a/.github/workflows/pushes.yml b/.github/workflows/pushes.yml index e47aff3f6f..e6ea3f33df 100644 --- a/.github/workflows/pushes.yml +++ b/.github/workflows/pushes.yml @@ -22,9 +22,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: push-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + push-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: @@ -79,9 +79,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-coveralls-${{ hashFiles('**/pom.xml') }} + key: coveralls-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}-coveralls- + coveralls-${{ runner.os }}- - name: Check Environment run: .ci/check-environment.sh shell: bash @@ -104,9 +104,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-sonar-${{ hashFiles('**/pom.xml') }} + key: sonar-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}-sonar- + sonar-${{ runner.os }}- - name: Check Environment run: .ci/check-environment.sh shell: bash diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 2948cb6653..9e96a7f1ac 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -10,6 +10,10 @@ jobs: continue-on-error: false steps: - uses: actions/checkout@v2 + - name: Set up Ruby 2.7 + uses: actions/setup-ruby@v1 + with: + ruby-version: 2.7 - name: Check Environment run: .ci/check-environment.sh shell: bash diff --git a/.github/workflows/troubleshooting.yml b/.github/workflows/troubleshooting.yml index 8065541981..dd79aa8c3e 100644 --- a/.github/workflows/troubleshooting.yml +++ b/.github/workflows/troubleshooting.yml @@ -19,9 +19,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: push-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + push-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: diff --git a/Dangerfile b/Dangerfile index 5c506554c7..0665207264 100644 --- a/Dangerfile +++ b/Dangerfile @@ -14,6 +14,8 @@ def get_args(base_branch) '--mode', 'online', '--auto-gen-config', '--keep-reports', + '--error-recovery', + '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', # '--debug', ] end @@ -21,10 +23,9 @@ end def run_pmdtester Dir.chdir('..') do begin - @base_branch = ENV['TRAVIS_BRANCH'] + @base_branch = ENV['PMD_CI_BRANCH'] @logger.info "Run against PR base #{@base_branch}" - runner = PmdTester::Runner.new(get_args(@base_branch)) - @new_errors, @removed_errors, @new_violations, @removed_violations, @new_configerrors, @removed_configerrors = runner.run + @summary = PmdTester::Runner.new(get_args(@base_branch)).run unless Dir.exist?('target/reports/diff') message("No java rules are changed!", sticky: true) @@ -36,11 +37,11 @@ def run_pmdtester message1 = create_message # run against master branch (if the PR is not already against master) - unless ENV['TRAVIS_BRANCH'] == 'master' + unless ENV['PMD_CI_BRANCH'] == 'master' @base_branch = 'master' @logger.info "Run against #{@base_branch}" - runner = PmdTester::Runner.new(get_args(@base_branch)) - @new_errors, @removed_errors, @new_violations, @removed_violations, @new_configerrors, @removed_configerrors = runner.run + @summary = PmdTester::Runner.new(get_args(@base_branch)).run + # move the generated report out of the way FileUtils.mv 'target/reports/diff', 'target/diff2' message2 = create_message @@ -69,11 +70,14 @@ end def create_message "Compared to #{@base_branch}:\n"\ - "This changeset introduces "\ - "#{@new_violations} new violations, #{@new_errors} new errors and "\ - "#{@new_configerrors} new configuration errors,\n"\ - "removes #{@removed_violations} violations, #{@removed_errors} errors and "\ - "#{@removed_configerrors} configuration errors.\n" + "This changeset " \ + "changes #{@summary[:violations][:changed]} violations,\n" \ + "introduces #{@summary[:violations][:new]} new violations, " \ + "#{@summary[:errors][:new]} new errors and " \ + "#{@summary[:configerrors][:new]} new configuration errors,\n" \ + "removes #{@summary[:violations][:removed]} violations, "\ + "#{@summary[:errors][:removed]} errors and " \ + "#{@summary[:configerrors][:removed]} configuration errors.\n" end def upload_report diff --git a/Gemfile b/Gemfile index 8d17050419..b8861eb9c3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,9 @@ source 'https://rubygems.org/' # bleeding edge from git -gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' +#gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' -#gem 'pmdtester', '~> 1.0' +gem 'pmdtester', '~> 1.1' gem 'danger', '~> 5.6', '>= 5.6' # This group is only needed for rendering release notes diff --git a/Gemfile.lock b/Gemfile.lock index 5cbf3cd857..9bdbcf639f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,3 @@ -GIT - remote: https://github.com/pmd/pmd-regression-tester.git - revision: 6875868e8be772807498ab46411e44d163633d64 - specs: - pmdtester (1.1.0.pre.SNAPSHOT) - differ (~> 0.1) - nokogiri (~> 1.8) - rufus-scheduler (~> 3.5) - slop (~> 4.6) - GEM remote: https://rubygems.org/ specs: @@ -41,13 +31,14 @@ GEM multipart-post (>= 1.2, < 3) faraday-http-cache (1.3.1) faraday (~> 0.8) - fugit (1.4.0) + fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) git (1.7.0) rchardet (~> 1.8) kramdown (1.17.0) liquid (4.0.3) + logger-colors (1.0.0) mini_portile2 (2.4.0) multipart-post (2.1.1) nap (1.1.0) @@ -58,10 +49,17 @@ GEM faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) + pmdtester (1.1.0) + differ (~> 0.1) + liquid (>= 4.0) + logger-colors (~> 1.0) + nokogiri (~> 1.8) + rufus-scheduler (~> 3.5) + slop (~> 4.6) public_suffix (4.0.6) raabro (1.4.0) rchardet (1.8.0) - rouge (3.24.0) + rouge (3.25.0) rufus-scheduler (3.6.0) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) @@ -71,7 +69,7 @@ GEM slop (4.8.2) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tzinfo (2.0.2) + tzinfo (2.0.3) concurrent-ruby (~> 1.0) unicode-display_width (1.7.0) @@ -81,7 +79,7 @@ PLATFORMS DEPENDENCIES danger (~> 5.6, >= 5.6) liquid (>= 4.0.0) - pmdtester! + pmdtester (~> 1.1) rouge (>= 1.7, < 4) safe_yaml (>= 1.0) diff --git a/README.md b/README.md index a819536f6d..038b529c54 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![PMD Logo](https://raw.githubusercontent.com/pmd/pmd/pmd/7.0.x/docs/images/logo/pmd-logo-300px.png) [![Join the chat at https://gitter.im/pmd/pmd](https://badges.gitter.im/pmd/pmd.svg)](https://gitter.im/pmd/pmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://github.com/pmd/pmd/workflows/.github/workflows/pushes.yml/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) +[![Build Status](https://github.com/pmd/pmd/workflows/Pushes/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd) [![Reproducible Builds](https://img.shields.io/badge/Reproducible_Builds-ok-green?labelColor=blue)](https://github.com/jvm-repo-rebuild/reproducible-central#net.sourceforge.pmd:pmd) [![Coverage Status](https://coveralls.io/repos/github/pmd/pmd/badge.svg)](https://coveralls.io/github/pmd/pmd) diff --git a/do-release.sh b/do-release.sh index fa8617e1a4..bff8d1f6aa 100755 --- a/do-release.sh +++ b/do-release.sh @@ -24,8 +24,7 @@ echo "-------------------------------------------" echo "Releasing PMD" echo "-------------------------------------------" -# see also https://gist.github.com/pdunnavant/4743895 -CURRENT_VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:3.0.0:exec) +CURRENT_VERSION=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout) RELEASE_VERSION=${CURRENT_VERSION%-SNAPSHOT} MAJOR=$(echo $RELEASE_VERSION | cut -d . -f 1) MINOR=$(echo $RELEASE_VERSION | cut -d . -f 2) @@ -128,7 +127,7 @@ bundle install export RELEASE_NOTES_POST="_posts/$(date -u +%Y-%m-%d)-PMD-${RELEASE_VERSION}.md" echo "Generating ../pmd.github.io/${RELEASE_NOTES_POST}..." -NEW_RELEASE_NOTES=$(bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) +NEW_RELEASE_NOTES=$(bundle exec .ci/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) cat > ../pmd.github.io/${RELEASE_NOTES_POST} <" +echo "Tag has been pushed.... now check github actions: " echo echo echo "Press enter to continue..." @@ -228,7 +227,7 @@ echo echo echo "Verify the new release on github: " echo -echo "* Wait until the new version is synced to maven central and appears in as latest version in" +echo "* Wait until the new version is synced to maven central and appears as latest version in" echo " ." echo "* Submit news to SF on page. Use same text as in the email below." echo "* Send out an announcement mail to the mailing list:" diff --git a/docs/_config.yml b/docs/_config.yml index 252ef3bd60..90d8377a47 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,8 +2,8 @@ repository: pmd/pmd pmd: version: 7.0.0-SNAPSHOT - previous_version: 6.29.0 - date: ??-?????-2020 + previous_version: 6.30.0 + date: ??-?????-2021 release_type: major # release types: major, minor, bugfix diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 7893a725fa..03c8619c6a 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -98,6 +98,8 @@ The following previously deprecated rules have been finally removed: * LoggerIsNotStaticFinal (java-errorprone) * MIsLeadingVariableName (java-codestyle) * ModifiedCyclomaticComplexity (java-design) +* PositionLiteralsFirstInCaseInsensitiveComparisons (java-bestpractices) +* PositionLiteralsFirstInComparisons (java-bestpractices) * StdCyclomaticComplexity (java-design) * SuspiciousConstantFieldName (java-codestyle) * UnsynchronizedStaticDateFormatter (java-multithreading) @@ -105,17 +107,48 @@ The following previously deprecated rules have been finally removed: * VariableNamingConventions (java-codestyle) * WhileLoopsMustUseBraces (java-codestyle) +#### Changed rules + +##### Java + +* {% rule "java/errorprone/EmptyCatchBlock" %}: `CloneNotSupportedException` and `InterruptedException` are not special-cased anymore. Rename the exception parameter to `ignored` to ignore them. + + ### Fixed Issues * java-bestpractices + * [#342](https://github.com/pmd/pmd/issues/342): \[java] AccessorMethodGeneration: Name clash with another public field not properly handled + * [#807](https://github.com/pmd/pmd/issues/807): \[java] AccessorMethodGeneration false positive with overloads + * [#1212](https://github.com/pmd/pmd/issues/1212): \[java] Don't raise JUnitTestContainsTooManyAsserts on JUnit 5's assertAll + * [#1422](https://github.com/pmd/pmd/issues/1422): \[java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field + * [#1565](https://github.com/pmd/pmd/issues/1565): \[java] JUnitAssertionsShouldIncludeMessage false positive with AssertJ + * [#1969](https://github.com/pmd/pmd/issues/1969): \[java] MissingOverride false-positive triggered by package-private method overwritten in another package by extending class + * [#1998](https://github.com/pmd/pmd/issues/1998): \[java] AccessorClassGeneration false-negative: subclass calls private constructor + * [#2147](https://github.com/pmd/pmd/issues/2147): \[java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods + * [#2542](https://github.com/pmd/pmd/issues/2542): \[java] UseCollectionIsEmpty can not detect the case `foo.bar().size()` * [#2796](https://github.com/pmd/pmd/issue/2796): \[java] UnusedAssignment false positive with call chains * [#2797](https://github.com/pmd/pmd/issues/2797): \[java] MissingOverride long-standing issues + * [#2806](https://github.com/pmd/pmd/issues/2806): \[java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches + * [#2883](https://github.com/pmd/pmd/issues/2883): \[java] JUnitAssertionsShouldIncludeMessage false positive with method call * java-codestyle * [#1673](https://github.com/pmd/pmd/issues/1673): \[java] UselessParentheses false positive with conditional operator * [#1790](https://github.com/pmd/pmd/issues/1790): \[java] UnnecessaryFullyQualifiedName false positive with enum constant * [#1918](https://github.com/pmd/pmd/issues/1918): \[java] UselessParentheses false positive with boolean operators * [#2299](https://github.com/pmd/pmd/issues/2299): \[java] UnnecessaryFullyQualifiedName false positive with similar package name + * [#2528](https://github.com/pmd/pmd/issues/2528): \[java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest * [#2739](https://github.com/pmd/pmd/issues/2739): \[java] UselessParentheses false positive for string concatenation +* java-errorprone + * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces + * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) + * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums + * [#2880](https://github.com/pmd/pmd/issues/2880): \[java] CompareObjectsWithEquals - false negative with type res +* java-multithreading + * [#2537](https://github.com/pmd/pmd/issues/2537): \[java] DontCallThreadRun can't detect the case that call run() in `this.run()` + * [#2538](https://github.com/pmd/pmd/issues/2538): \[java] DontCallThreadRun can't detect the case that call run() in `foo.bar.run()` + * [#2577](https://github.com/pmd/pmd/issues/2577): \[java] UseNotifyAllInsteadOfNotify falsely detect a special case with argument: `foo.notify(bar)` +* 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 ### API Changes diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index 31dd1316bd..f618b5117b 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -246,6 +246,47 @@ 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.30.0 + +##### Deprecated API + +###### Around RuleSet parsing + +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} +* Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +###### Around the `PMD` class + +Many classes around PMD's entry point ({% jdoc core::PMD %}) have been deprecated as internal, including: +* The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} +* {% jdoc core::SourceCodeProcessor %} +* The constructors of {% jdoc core::PMD %} (the class will be made a utility class) + +###### Miscellaneous + +* {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, + {% jdoc !!java::lang.java.ast.ASTTypeParameter#getParameterName() %} + and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`, + the attribute is `@Name`. +* {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isAnonymousInnerClass() %}, + and {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isEnumChild() %}, + refs [#905](https://github.com/pmd/pmd/issues/905) + +##### 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 !!javascript::lang.ecmascript.Ecmascript3Handler %} +* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Parser %} +* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#parserOptions %} +* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#getSuppressMap() %} +* {% jdoc !!core::lang.rule.ParametricRuleViolation %} +* {% jdoc !!core::lang.ParserOptions#suppressMarker %} +* {% jdoc !!modelica::lang.modelica.rule.ModelicaRuleViolationFactory %} + #### 6.29.0 No changes. @@ -1192,8 +1233,8 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see will be removed with PMD 7.0.0. The rule is replaced by the more general {% rule "java/multithreading/UnsynchronizedStaticFormatter" %}. -* The two Java rules {% rule "java/bestpractices/PositionLiteralsFirstInComparisons" %} - and {% rule "java/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisons" %} (ruleset `java-bestpractices`) +* The two Java rules [`PositionLiteralsFirstInComparisons`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_bestpractices.html#positionliteralsfirstincomparisons) + 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 diff --git a/docs/pages/pmd/devdocs/building.md b/docs/pages/pmd/devdocs/building.md index 0fcd7ce293..d9a7bcbc11 100644 --- a/docs/pages/pmd/devdocs/building.md +++ b/docs/pages/pmd/devdocs/building.md @@ -12,7 +12,7 @@ author: Tom Copeland, Xavier Le Vourch * JDK 11 or higher -{% include note.html content="While Java 11 is required for building, running PMD only requires Java 7 (or Java 8 for Apex and the Designer)." %} +{% include note.html content="While Java 11 is required for building, running PMD only requires Java 7 (or Java 8 for Apex, Scala, Visualforce, and the Designer)." %} You’ll need to either check out the source code or download the latest source release. Assuming you’ve got the latest source release, unzip it to a directory: diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index c97bbdfbfd..8d94294926 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -116,6 +116,11 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt default="false" languages="Java" %} + {% include custom/cli_option_row.html options="--ignore-literal-sequences" + description="Ignore sequences of literals (common e.g. in list initializers)" + default="false" + languages="C#" + %} {% include custom/cli_option_row.html options="--ignore-usings" description="Ignore `using` directives in C# when comparing text" default="false" @@ -370,7 +375,7 @@ Here's a screenshot of CPD after running on the JDK 8 java.lang package: ## Suppression Arbitrary blocks of code can be ignored through comments on **Java**, **C/C++**, **Dart**, **Go**, **Javascript**, -**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python**, **Swift** and **C#** by including the keywords `CPD-OFF` and `CPD-ON`. +**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python**, **Scala**, **Swift** and **C#** by including the keywords `CPD-OFF` and `CPD-ON`. ```java public Object someParameterizedFactoryMethod(int x) throws Exception { diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md index 4bbfce41ec..5ca846ac69 100644 --- a/docs/pages/pmd/userdocs/pmd_report_formats.md +++ b/docs/pages/pmd/userdocs/pmd_report_formats.md @@ -169,8 +169,8 @@ and configuration errors are reported. Example: ``` -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: Logger calls should be surrounded by log level guards. -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: This for loop can be replaced by a foreach loop +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: GuardLogStatement: Logger calls should be surrounded by log level guards. +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: ForLoopCanBeForeach: This for loop can be replaced by a foreach loop /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java - PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java CloseResource rule violation suppressed by Annotation in /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java LoosePackageCoupling - No packages or classes specified diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index ef10c25b9b..8abb75f161 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,36 +21,9 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues -* pmd-core - * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling - - ### API Changes -#### Deprecated API - -* {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, - {% jdoc !!java::lang.java.ast.ASTTypeParameter#getParameterName() %} - and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`, - the attribute is `@Name`. -* {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isAnonymousInnerClass() %}, - and {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isEnumChild() %}, - refs [#905](https://github.com/pmd/pmd/issues/905) - -#### 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 !!javascript::lang.ecmascript.Ecmascript3Handler %} -* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Parser %} -* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#parserOptions %} -* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#getSuppressMap() %} -* {% jdoc !!core::lang.rule.ParametricRuleViolation %} -* {% jdoc !!core::lang.ParserOptions#suppressMarker %} -* {% jdoc !!modelica::lang.modelica.rule.ModelicaRuleViolationFactory %} - - ### External Contributions {% endtocmaker %} + diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 2e90ca3079..1736070850 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,123 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 12-December-2020 - 6.30.0 + +The PMD team is pleased to announce PMD 6.30.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [CPD](#cpd) + * [Type information for VisualForce](#type-information-for-visualforce) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Deprecated API](#deprecated-api) + * [Around RuleSet parsing](#around-ruleset-parsing) + * [Around the `PMD` class](#around-the-`pmd`-class) + * [Miscellaneous](#miscellaneous) + * [Internal API](#internal-api) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +##### CPD + +* The C# module now supports the new option [`--ignore-literal-sequences`](https://pmd.github.io/latest/pmd_userdocs_cpd.html#-ignore-literal-sequences), which can be used to avoid detection of some uninteresting clones. Support for other languages may be added in the future. See [#2945](https://github.com/pmd/pmd/pull/2945) + +* The Scala module now supports [suppression](https://pmd.github.io/latest/pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) + + +##### Type information for VisualForce + +The Visualforce AST now can resolve the data type of Visualforce expressions that reference Apex Controller properties and Custom Object fields. This feature improves the precision of existing rules, like [`VfUnescapeEl`](https://pmd.github.io/pmd-6.30.0/pmd_rules_vf_security.html#vfunescapeel). + +This can be configured using two environment variables: +* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes`. Specifying an empty string will disable data type resolution for Apex Controller properties. +* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for Custom Objects. Absolute or relative to the Visualforce directory. Default is `../objects`. Specifying an empty string will disable data type resolution for Custom Object fields. + +This feature is experimental, in particular, expect changes to the way the configuration is specified. We'll probably extend the CLI instead of relying on environment variables in a future version. + +Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! + +### Fixed Issues + +* core + * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling + * [#1961](https://github.com/pmd/pmd/issues/1961): \[core] Text renderer should include name of violated rule + * [#2874](https://github.com/pmd/pmd/pull/2874): \[core] Fix XMLRenderer with UTF-16 +* cs + * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: ignoring using directives could not be disabled +* java + * [#2911](https://github.com/pmd/pmd/issues/2911): \[java] `ClassTypeResolver#searchNodeNameForClass` leaks memory + * [#2934](https://github.com/pmd/pmd/pull/2934): \[java] CompareObjectsWithEquals / UseEqualsToCompareStrings - False negatives with fields + * [#2940](https://github.com/pmd/pmd/pull/2940): \[java] Catch additional TypeNotPresentExceptions / LinkageErrors +* scala + * [#2480](https://github.com/pmd/pmd/issues/2480): \[scala] Support CPD suppressions + + +### API Changes + +#### Deprecated API + + +##### Around RuleSet parsing + +* RuleSetFactory and RuleSetFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to RuleSet for simple cases, eg forSingleRule. These replace some counterparts in RuleSetFactory +* Since RuleSets is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* RuleSetReferenceId, RuleSetReference, RuleSetFactoryCompatibility are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +##### Around the `PMD` class + +Many classes around PMD's entry point (PMD) have been deprecated as internal, including: +* The contents of the packages net.sourceforge.pmd.cli, net.sourceforge.pmd.processor +* SourceCodeProcessor +* The constructors of PMD (the class will be made a utility class) + +##### Miscellaneous + +* ASTPackageDeclaration#getPackageNameImage, + ASTTypeParameter#getParameterName + and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`, + the attribute is `@Name`. +* ASTClassOrInterfaceBody#isAnonymousInnerClass, + and ASTClassOrInterfaceBody#isEnumChild, + refs [#905](https://github.com/pmd/pmd/issues/905) + +#### 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. + +* net.sourceforge.pmd.lang.ecmascript.Ecmascript3Handler +* net.sourceforge.pmd.lang.ecmascript.Ecmascript3Parser +* EcmascriptParser#parserOptions +* EcmascriptParser#getSuppressMap +* net.sourceforge.pmd.lang.rule.ParametricRuleViolation +* ParserOptions#suppressMarker +* net.sourceforge.pmd.lang.modelica.rule.ModelicaRuleViolationFactory + + +### External Contributions + +* [#2864](https://github.com/pmd/pmd/pull/2864): [vf] Provide expression type information to Visualforce rules to avoid false positives - [Jeff Bartolotta](https://github.com/jbartolotta-sfdc) +* [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) +* [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#2929](https://github.com/pmd/pmd/pull/2929): \[scala] Add support for CPD-ON and CPD-OFF special comments - [Andy Robinson](https://github.com/andyrobinson) +* [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) +* [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) + +### Stats +* 190 commits +* 25 closed tickets & PRs +* Days since last release: 49 + ## 24-October-2020 - 6.29.0 The PMD team is pleased to announce PMD 6.29.0. diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java index fdee79b4a3..7c7879df5e 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java @@ -9,7 +9,6 @@ import java.util.List; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.apex.ast.ApexParser; @@ -33,12 +32,7 @@ public class ApexHandler extends AbstractPmdLanguageVersionHandler { } @Override - public ParserOptions getDefaultParserOptions() { - return new ApexParserOptions(); - } - - @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new ApexParser(); } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java deleted file mode 100644 index 8d41b5d1ba..0000000000 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.apex; - -import net.sourceforge.pmd.lang.ParserOptions; - -/** - * @deprecated Not useful - */ -@Deprecated -public class ApexParserOptions extends ParserOptions { - - // empty class for now, since we don't have extra options for Apex - // Once you add something here, make sure to override hashCode and equals - -} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index 6c8ca348c6..0606311632 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -6,9 +6,7 @@ package net.sourceforge.pmd.lang.apex.rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; -import net.sourceforge.pmd.lang.apex.ApexParserOptions; import net.sourceforge.pmd.lang.apex.ast.ApexParserVisitor; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -20,11 +18,6 @@ public abstract class AbstractApexRule extends AbstractRule super.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME)); } - @Override - public ParserOptions getParserOptions() { - return new ApexParserOptions(); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index e8f53a6d68..617dd1bd78 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -15,32 +15,29 @@ import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetLoader; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetLoader().enableCompatibility(false).toFactory(); - @Test - public void loadDefaultRuleset() throws Exception { - RuleSet ruleset = factory.createRuleSet("rulesets/apex/ruleset.xml"); + public void loadDefaultRuleset() { + RuleSet ruleset = rulesetLoader().loadFromResource("rulesets/apex/ruleset.xml"); Assert.assertNotNull(ruleset); } @After public void cleanup() { - Handler[] handlers = Logger.getLogger(RuleSetFactory.class.getName()).getHandlers(); + Handler[] handlers = Logger.getLogger(RuleSetLoader.class.getName()).getHandlers(); for (Handler handler : handlers) { - Logger.getLogger(RuleSetFactory.class.getName()).removeHandler(handler); + Logger.getLogger(RuleSetLoader.class.getName()).removeHandler(handler); } } @Test - public void loadQuickstartRuleset() throws Exception { - Logger.getLogger(RuleSetFactory.class.getName()).addHandler(new Handler() { + public void loadQuickstartRuleset() { + Logger.getLogger(RuleSetLoader.class.getName()).addHandler(new Handler() { @Override public void publish(LogRecord record) { Assert.fail("No Logging expected: " + record.getMessage()); @@ -54,7 +51,11 @@ public class DefaultRulesetTest { public void close() throws SecurityException { } }); - RuleSet ruleset = factory.createRuleSet("rulesets/apex/quickstart.xml"); + RuleSet ruleset = rulesetLoader().loadFromResource("rulesets/apex/quickstart.xml"); Assert.assertNotNull(ruleset); } + + private RuleSetLoader rulesetLoader() { + return new RuleSetLoader().enableCompatibility(false); + } } 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 fcdf957533..6e638917c2 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 @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex.ast; +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertPosition; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertEquals; @@ -194,14 +195,4 @@ public class ApexParserTest extends ApexParserTestBase { } return result; } - - // TEST HELPER - - private static void assertPosition(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { - FileLocation loc = node.getReportLocation(); - assertEquals("Wrong begin line", beginLine, loc.getBeginLine()); - assertEquals("Wrong begin column", beginColumn, loc.getBeginColumn()); - assertEquals("Wrong end line", endLine, loc.getEndLine()); - assertEquals("Wrong end column", endColumn, loc.getEndColumn()); - } } 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 7cda3e1d0f..83204382fb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -20,7 +21,7 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; -import net.sourceforge.pmd.annotation.DeprecatedUntil700; +import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -30,6 +31,7 @@ import net.sourceforge.pmd.benchmark.TimingReportRenderer; import net.sourceforge.pmd.cache.NoopAnalysisCache; import net.sourceforge.pmd.cli.PMDCommandLineInterface; import net.sourceforge.pmd.cli.PMDParameters; +import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; @@ -77,14 +79,7 @@ public final class PMD { // Load the RuleSets final RuleSetLoader ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration); - - final List ruleSets; - try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { - ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), ruleSetFactory); - } - if (ruleSets == null) { - return PMDCommandLineInterface.NO_ERRORS_STATUS; - } + final List ruleSets = getRuleSetsWithBenchmark(configuration.getRuleSetPaths(), ruleSetFactory); final Set languages = getApplicableLanguages(configuration, ruleSets); @@ -120,34 +115,118 @@ public final class PMD { * Make sure it's our own classloader before attempting to close it.... * Maven + Jacoco provide us with a cloaseable classloader that if closed * will throw a ClassNotFoundException. - */ + */ if (configuration.getClassLoader() instanceof ClasspathClassLoader) { IOUtil.tryCloseClassLoader(configuration.getClassLoader()); } } } + private static List getRuleSetsWithBenchmark(List rulesetPaths, RuleSetLoader factory) { + try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { + List ruleSets; + try { + ruleSets = factory.loadFromResources(rulesetPaths); + printRuleNamesInDebug(ruleSets); + if (isEmpty(ruleSets)) { + String msg = "No rules found. Maybe you misspelled a rule name? (" + + String.join(",", rulesetPaths) + ')'; + LOG.log(Level.SEVERE, msg); + throw new IllegalArgumentException(msg); + } + } catch (RuleSetLoadException rsnfe) { + LOG.log(Level.SEVERE, "Ruleset not found", rsnfe); + throw rsnfe; + } + return ruleSets; + } + } + + private static boolean isEmpty(List rsets) { + return rsets.stream().noneMatch(it -> it.size() > 0); + } + /** - * Run PMD on a list of files using the number of threads specified - * by the configuration. - * - * TODO rulesets should be validated more strictly upon construction. - * We shouldn't be removing rules after construction. - * - * @param configuration Configuration - * @param ruleSets RuleSetFactory - * @param files List of {@link DataSource}s - * @param listener RuleContext - * - * @throws Exception If an exception occurs while closing the data sources - * Todo wrap that into a known exception type - * - * @deprecated Use {@link #processTextFiles(PMDConfiguration, List, List, GlobalAnalysisListener)}, - * which uses a list of {@link TextFile} and not the deprecated {@link DataSource}. + * If in debug modus, print the names of the rules. * + * @param rulesets the RuleSets to print + */ + private static void printRuleNamesInDebug(List rulesets) { + if (LOG.isLoggable(Level.FINER)) { + for (RuleSet rset : rulesets) { + for (Rule r : rset.getRules()) { + LOG.finer("Loaded rule " + r.getName()); + } + } + } + } + + /** + * Run PMD using the given configuration. This replaces the other overload. + * + * @param configuration Configuration for the run. Note that the files, + * and rulesets, are ignored, as they are supplied + * as parameters + * @param ruleSets Parsed rulesets + * @param files Files to process, will be closed by this method. + * @param renderers Renderers that render the report (may be empty) + * + * @return Report in which violations are accumulated + * + * @throws Exception If there was a problem when opening or closing the renderers + */ + @SuppressWarnings("PMD.CloseResource") + public static Report processFiles(PMDConfiguration configuration, + List ruleSets, + List files, + List renderers) throws Exception { + + + GlobalAnalysisListener rendererListeners = createComposedRendererListener(renderers); + GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener(); + + List allListeners = listOf(reportBuilder, rendererListeners); + + try (GlobalAnalysisListener listener = GlobalAnalysisListener.tee(allListeners)) { + processFiles(configuration, ruleSets, files, listener); + } + + return reportBuilder.getResult(); + } + + private static GlobalAnalysisListener createComposedRendererListener(List renderers) throws Exception { + if (renderers.isEmpty()) { + return GlobalAnalysisListener.noop(); + } + + List rendererListeners = new ArrayList<>(renderers.size()); + for (Renderer renderer : renderers) { + try { + rendererListeners.add(renderer.newListener()); + } catch (IOException ioe) { + // close listeners so far, throw their close exception or the ioe + IOUtil.ensureClosed(rendererListeners, ioe); + throw AssertionUtil.shouldNotReachHere("ensureClosed should have thrown"); + } + } + return GlobalAnalysisListener.tee(rendererListeners); + } + + + /** + * Run PMD using the given configuration. This replaces the other overload. + * + * @param configuration Configuration for the run. Note that the files, + * and rulesets, are ignored, as they are supplied + * as parameters + * @param ruleSets Parsed rulesets + * @param files Files to process, will be closed by this method. + * @param listener Listener to which analysis events are forwarded. + * The listener is NOT closed by this routine and should + * be closed by the caller. + * + * @throws Exception If there was a problem when opening or closing the renderers */ - @Deprecated - @DeprecatedUntil700 public static void processFiles(PMDConfiguration configuration, List ruleSets, List files, @@ -192,7 +271,6 @@ public final class PMD { encourageToUseIncrementalAnalysis(configuration); - // Make sure the cache is listening for analysis results listener = GlobalAnalysisListener.tee(listOf(listener, configuration.getAnalysisCache())); @@ -205,22 +283,7 @@ public final class PMD { ex = e; } finally { configuration.getAnalysisCache().persist(); - - // in case we analyzed files within Zip Files/Jars, we need to close them after - // the analysis is finished - Exception closed = IOUtil.closeAll(inputFiles); - - if (closed != null) { - if (ex != null) { - ex.addSuppressed(closed); - } else { - ex = closed; - } - } - } - - if (ex != null) { - throw ex; + IOUtil.ensureClosed(inputFiles, ex); } } @@ -248,16 +311,17 @@ public final class PMD { } - private static List sortFiles(final PMDConfiguration configuration, List files) { + private static List sortFiles(final PMDConfiguration configuration, Collection files) { // the input collection may be unmodifiable - files = new ArrayList<>(files); + List result = new ArrayList<>(files); if (configuration.isStressTest()) { // randomize processing order - Collections.shuffle(files); + Collections.shuffle(result); } else { - files.sort(Comparator.comparing(TextFile::getPathId)); + result.sort(Comparator.comparing(TextFile::getPathId)); } - return files; + + return result; } private static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) { 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 d13bacf912..38326a0165 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -13,6 +13,9 @@ import java.util.List; import java.util.Objects; import java.util.Properties; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.annotation.DeprecatedUntil700; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -52,7 +55,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader; * *

The aspects related to Rules and Source files are:

*
    - *
  • A comma separated list of RuleSets URIs. {@link #getRuleSets()}
  • + *
  • RuleSets URIs: {@link #getRuleSetPaths()}
  • *
  • A minimum priority threshold when loading Rules from RuleSets, defaults * to {@link RulePriority#LOW}. {@link #getMinimumPriority()}
  • *
  • The character encoding of source files, defaults to the system default as @@ -61,7 +64,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader; *
  • A comma separated list of input paths to process for source files. This * may include files, directories, archives (e.g. ZIP files), etc. * {@link #getInputPaths()}
  • - *
  • A flag which controls, whether {@link RuleSetFactoryCompatibility} filter + *
  • A flag which controls, whether {@link RuleSetLoader#enableCompatibility(boolean)} filter * should be used or not: #isRuleSetFactoryCompatibilityEnabled; *
* @@ -94,7 +97,7 @@ public class PMDConfiguration extends AbstractConfiguration { private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer(); // Rule and source file options - private String ruleSets; + private List ruleSets; private RulePriority minimumPriority = RulePriority.LOW; private @NonNull List inputPaths = Collections.emptyList(); private String inputUri; @@ -267,19 +270,44 @@ public class PMDConfiguration extends AbstractConfiguration { * Get the comma separated list of RuleSet URIs. * * @return The RuleSet URIs. + * + * @deprecated Use {@link #getRuleSetPaths()} */ + @Deprecated + @DeprecatedUntil700 public String getRuleSets() { + return String.join(",", ruleSets); + } + + /** + * Returns the list of ruleset URIs. + * + * @see RuleSetLoader#loadFromResource(String) + */ + public List getRuleSetPaths() { return ruleSets; } + /** + * Sets the rulesets. + * + * @throws NullPointerException If the parameter is null + */ + public void setRuleSets(@NonNull List ruleSets) { + this.ruleSets = new ArrayList<>(ruleSets); + } + /** * Set the comma separated list of RuleSet URIs. * - * @param ruleSets - * the rulesets to set + * @param ruleSets the rulesets to set + * + * @deprecated Use {@link #setRuleSets(List)} */ + @Deprecated + @DeprecatedUntil700 public void setRuleSets(String ruleSets) { - this.ruleSets = ruleSets; + this.ruleSets = Arrays.asList(ruleSets.split(",")); } /** @@ -587,7 +615,7 @@ public class PMDConfiguration extends AbstractConfiguration { * * @return true, if the rule set factory compatibility feature is enabled * - * @see RuleSetFactoryCompatibility + * @see RuleSetLoader#enableCompatibility(boolean) */ public boolean isRuleSetFactoryCompatibilityEnabled() { return ruleSetFactoryCompatibilityEnabled; @@ -598,7 +626,7 @@ public class PMDConfiguration extends AbstractConfiguration { * * @param ruleSetFactoryCompatibilityEnabled {@code true} if the feature should be enabled * - * @see RuleSetFactoryCompatibility + * @see RuleSetLoader#enableCompatibility(boolean) */ public void setRuleSetFactoryCompatibilityEnabled(boolean ruleSetFactoryCompatibilityEnabled) { this.ruleSetFactoryCompatibilityEnabled = ruleSetFactoryCompatibilityEnabled; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java index 8877094ef0..6342222fd4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java @@ -195,8 +195,11 @@ public class Report { * summary over all violations is needed as PMD creates one report per file * by default. * - * @param r - * the report to be merged into this. + *

This is synchronized on an internal lock (note that other mutation + * operations are not synchronized, todo for pmd 7). + * + * @param r the report to be merged into this. + * * @see AbstractAccumulatingRenderer */ public void merge(Report r) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index af609e581c..0b7d610fe8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -9,10 +9,8 @@ import java.util.List; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.AstProcessingStage; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.StringProperty; @@ -252,21 +250,6 @@ public interface Rule extends PropertySource { */ void setPriority(RulePriority priority); - /** - * Get the parser options for this Rule. Parser options are used to - * configure the {@link Parser} to create an AST in - * the form the Rule is expecting. Because ParserOptions are mutable, a Rule - * should return a new instance on each call. - * - * @return the parser options - * - * @deprecated This was never implemented and will never be. PMD - * cannot parse files once per rule. Let this method assume - * its default by not overriding it. - */ - @Deprecated - ParserOptions getParserOptions(); - /** * Returns true if this rule depends on the given processing stage diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index f50fe238f4..56f0e82750 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -554,18 +554,6 @@ public class RuleSet implements ChecksumAware { } } - /** - * @deprecated Use {@link #getFileExclusions()} - */ - @Deprecated - public List getExcludePatterns() { - List excludes = new ArrayList<>(); - for (Pattern p : excludePatterns) { - excludes.add(p.pattern()); - } - return excludes; - } - /** * Returns the number of rules in this ruleset * @@ -686,18 +674,6 @@ public class RuleSet implements ChecksumAware { return description; } - /** - * @deprecated Use {@link #getFileInclusions()} - */ - @Deprecated - public List getIncludePatterns() { - List includes = new ArrayList<>(); - for (Pattern p : includePatterns) { - includes.add(p.pattern()); - } - return includes; - } - /** * Returns the file exclusion patterns as an unmodifiable list. */ 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 edf9ed427e..e3dbb3e702 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -7,10 +7,8 @@ package net.sourceforge.pmd; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,14 +39,10 @@ import net.sourceforge.pmd.util.ResourceLoader; * RuleSetFactory is responsible for creating RuleSet instances from XML * content. See {@link RuleSetLoader} for configuration options and * their defaults. - * - * @deprecated Use a {@link RuleSetLoader} instead. This will be hidden in PMD 7 - * (it's the implementation, while {@link RuleSetLoader} is the API). */ -@Deprecated -public class RuleSetFactory { +final class RuleSetFactory { - private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName()); + private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); private static final String DESCRIPTION = "description"; private static final String UNEXPECTED_ELEMENT = "Unexpected element <"; @@ -62,153 +56,19 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); - /** - * @deprecated Use a {@link RuleSetLoader} to build a new factory - */ - @Deprecated // to be removed with PMD 7.0.0. - public RuleSetFactory() { - this(new ResourceLoader(), RulePriority.LOW, false, true); - } - - /** - * @deprecated Use a {@link RuleSetLoader} to build a new factory - */ - @Deprecated // to be removed with PMD 7.0.0. - public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility) { - this(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * @deprecated Use a {@link RuleSetLoader} to build a new factory - */ - @Deprecated // to be hidden with PMD 7.0.0. - public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility) { - this(resourceLoader, minimumPriority, warnDeprecated, enableCompatibility, false); - } - - RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility, boolean includeDeprecatedRuleReferences) { + RuleSetFactory(ResourceLoader resourceLoader, + RulePriority minimumPriority, + boolean warnDeprecated, + RuleSetFactoryCompatibility compatFilter, + boolean includeDeprecatedRuleReferences) { this.resourceLoader = resourceLoader; this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; this.includeDeprecatedRuleReferences = includeDeprecatedRuleReferences; - if (enableCompatibility) { - this.compatibilityFilter = new RuleSetFactoryCompatibility(); - } else { - this.compatibilityFilter = null; - } + this.compatibilityFilter = compatFilter; } - /** - * Constructor copying all configuration from another factory. - * - * @param factory The factory whose configuration to copy. - * @param warnDeprecated Whether deprecation warnings are to be produced by this - * factory - * - * @deprecated Use {@link #toLoader()} to rebuild a factory from a configuration - */ - @Deprecated - public RuleSetFactory(final RuleSetFactory factory, final boolean warnDeprecated) { - this(factory.resourceLoader, factory.minimumPriority, warnDeprecated, factory.compatibilityFilter != null); - } - - - /** - * Gets the compatibility filter in order to adjust it, e.g. add additional - * filters. - * - * @return the {@link RuleSetFactoryCompatibility} - */ - /* package */ RuleSetFactoryCompatibility getCompatibilityFilter() { - return compatibilityFilter; - } - - /** - * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. - * - * @return An Iterator of RuleSet objects. - * - * @throws RuleSetNotFoundException if the ruleset file could not be found - * - * @deprecated Use {@link RuleSetLoader#getStandardRuleSets()} - */ - @Deprecated - public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException { - return toLoader().getStandardRuleSets().iterator(); - } - - /** - * Create a RuleSets from a comma separated list of RuleSet reference IDs. - * This is a convenience method which calls - * {@link RuleSetReferenceId#parse(String)}, and then calls - * {@link #createRuleSets(List)}. The currently configured ResourceLoader is - * used. - * - * @param referenceString - * A comma separated list of RuleSet reference IDs. - * @return The new RuleSets. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Use {@link RuleSetLoader#loadFromResource(String)}, - * but note that that method does not split on commas - */ - @Deprecated - public List createRuleSets(String referenceString) throws RuleSetNotFoundException { - return createRuleSets(RuleSetReferenceId.parse(referenceString)); - } - - /** - * Create a RuleSets from a list of RuleSetReferenceIds. The currently - * configured ResourceLoader is used. - * - * @param ruleSetReferenceIds - * The List of RuleSetReferenceId of the RuleSets to create. - * @return The new RuleSets. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Will not be replaced - */ - @Deprecated - public List createRuleSets(List ruleSetReferenceIds) throws RuleSetNotFoundException { - List ruleSets = new ArrayList<>(); - for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { - RuleSet ruleSet = createRuleSet(ruleSetReferenceId); - ruleSets.add(ruleSet); - } - return ruleSets; - } - - /** - * Create a RuleSet from a RuleSet reference ID string. This is a - * convenience method which calls {@link RuleSetReferenceId#parse(String)}, - * gets the first item in the List, and then calls - * {@link #createRuleSet(RuleSetReferenceId)}. The currently configured - * ResourceLoader is used. - * - * @param referenceString - * A comma separated list of RuleSet reference IDs. - * @return A new RuleSet. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Use {@link RuleSetLoader#loadFromResource(String)} and discard the rest of the list. - */ - @Deprecated - public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { - List references = RuleSetReferenceId.parse(referenceString); - if (references.isEmpty()) { - throw new RuleSetNotFoundException( - "No RuleSetReferenceId can be parsed from the string: <" + referenceString + '>'); - } - return createRuleSet(references.get(0)); - } /** * Create a RuleSet from a RuleSetReferenceId. Priority filtering is ignored @@ -217,98 +77,16 @@ public class RuleSetFactory { * @param ruleSetReferenceId * The RuleSetReferenceId of the RuleSet to create. * @return A new RuleSet. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Will not be replaced */ - @Deprecated - public RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { + RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } private RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + throws RuleSetLoadException { return parseRuleSetNode(ruleSetReferenceId, withDeprecatedRuleReferences); } - /** - * Creates a copy of the given ruleset. All properties like name, description, fileName - * and exclude/include patterns are copied. - * - *

Note: The rule instances are shared between the original - * and the new ruleset (copy-by-reference). This might lead to concurrency issues, - * if the original ruleset and the new ruleset are used in different threads. - *

- * - * @param original the original rule set to copy from - * @return the copy - * - * @deprecated Use {@link RuleSet#copy(RuleSet)} - */ - @Deprecated - public RuleSet createRuleSetCopy(RuleSet original) { - RuleSetBuilder builder = new RuleSetBuilder(original); - return builder.build(); - } - - /** - * Creates a new ruleset with the given metadata such as name, description, - * fileName, exclude/include patterns are used. The rules are taken from the given - * collection. - * - *

Note: The rule instances are shared between the collection - * and the new ruleset (copy-by-reference). This might lead to concurrency issues, - * if the rules of the collection are also referenced by other rulesets and used - * in different threads. - *

- * - * @param name the name of the ruleset - * @param description the description - * @param fileName the filename - * @param excludePatterns list of exclude patterns, if any is not a valid regular expression, it will be ignored - * @param includePatterns list of include patterns, if any is not a valid regular expression, it will be ignored - * @param rules the collection with the rules to add to the new ruleset - * @return the new ruleset - * - * @deprecated Use {@link RuleSet#create(String, String, String, Collection, Collection, Iterable)} - */ - @Deprecated - public RuleSet createNewRuleSet(String name, - String description, - String fileName, - Collection excludePatterns, - Collection includePatterns, - Collection rules) { - return RuleSet.create(name, description, fileName, toPatterns(excludePatterns), toPatterns(includePatterns), rules); - } - - private Collection toPatterns(Collection sources) { - List result = new ArrayList<>(); - for (String s : sources) { - try { - result.add(Pattern.compile(s)); - } catch (PatternSyntaxException ignored) { - - } - } - return result; - } - - /** - * Creates a new RuleSet containing a single rule. - * - * @param rule The rule being created - * - * @return The newly created RuleSet - * - * @deprecated Use {@link RuleSet#forSingleRule(Rule)} - */ - @Deprecated - public RuleSet createSingleRuleRuleSet(final Rule rule) { - return RuleSet.forSingleRule(rule); - } - /** * Create a Rule from a RuleSet created from a file name resource. The * currently configured ResourceLoader is used. @@ -323,14 +101,11 @@ public class RuleSetFactory { * Whether RuleReferences that are deprecated should be ignored * or not * @return A new Rule. - * @throws RuleSetNotFoundException - * if unable to find a resource. */ - private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { if (ruleSetReferenceId.isAllRules()) { throw new IllegalArgumentException( - "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); + "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); } RuleSet ruleSet; // java8: computeIfAbsent @@ -346,28 +121,21 @@ public class RuleSetFactory { /** * Parse a ruleset node to construct a RuleSet. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param withDeprecatedRuleReferences - * whether rule references that are deprecated should be ignored - * or not + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param withDeprecatedRuleReferences whether rule references that are deprecated should be ignored + * or not + * * @return The new RuleSet. */ - private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { try (CheckedInputStream inputStream = new CheckedInputStream( - ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { + ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { if (!ruleSetReferenceId.isExternal()) { throw new IllegalArgumentException( - "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); + "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } DocumentBuilder builder = createDocumentBuilder(); - InputSource inputSource; - if (compatibilityFilter != null) { - inputSource = new InputSource(compatibilityFilter.filterRuleSetFile(inputStream)); - } else { - inputSource = new InputSource(inputStream); - } + InputSource inputSource = new InputSource(inputStream); Document document = builder.parse(inputSource); Element ruleSetElement = document.getDocumentElement(); @@ -420,9 +188,6 @@ public class RuleSetFactory { ruleSetBuilder.filterRulesByPriority(minimumPriority); return ruleSetBuilder.build(); - } catch (ReflectiveOperationException ex) { - ex.printStackTrace(); - throw new RuntimeException("Couldn't find the class " + ex.getMessage(), ex); } catch (ParserConfigurationException | IOException | SAXException ex) { ex.printStackTrace(); throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId + ": " + ex.getMessage(), ex); @@ -493,10 +258,13 @@ public class RuleSetFactory { * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, - boolean withDeprecatedRuleReferences, Set rulesetReferences) - throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException { + 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)) { @@ -520,8 +288,7 @@ public 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) - throws RuleSetNotFoundException { + private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); Set excludedRulesCheck = new HashSet<>(); @@ -530,7 +297,10 @@ public class RuleSetFactory { if (isElementNode(child, "exclude")) { Element excludeElement = (Element) child; String excludedRuleName = excludeElement.getAttribute("name"); - excludedRulesCheck.add(excludedRuleName); + excludedRuleName = compatibilityFilter.applyExclude(ref, excludedRuleName, this.warnDeprecated); + if (excludedRuleName != null) { + excludedRulesCheck.add(excludedRuleName); + } } else if (isElementNode(child, PRIORITY)) { priority = parseTextNode(child).trim(); } @@ -602,7 +372,7 @@ public class RuleSetFactory { * Must be a rule element node. */ private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Node ruleNode) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not @@ -642,13 +412,13 @@ public class RuleSetFactory { * or not */ private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, String ref, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { + Node ruleNode, String ref, boolean withDeprecatedRuleReferences) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not // it. if (StringUtils.isNotBlank(ruleSetReferenceId.getRuleName()) - && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { + && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { return; } @@ -752,7 +522,7 @@ public class RuleSetFactory { } } } catch (Exception e) { - throw new RuntimeException(e); + throw new RuleSetLoadException("Cannot load " + ruleSetReferenceId, e); } return found; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index 6ed1d189a4..d730065b47 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -4,21 +4,13 @@ package net.sourceforge.pmd; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.util.LinkedList; +import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; - -import net.sourceforge.pmd.annotation.InternalApi; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Provides a simple filter mechanism to avoid failing to parse an old ruleset, @@ -26,72 +18,72 @@ import net.sourceforge.pmd.annotation.InternalApi; * renamed or moved to another ruleset. * * @see issue 1360 - * - * @deprecated Use {@link RuleSetLoader#enableCompatibility(boolean)} to enable this feature. - * This implementation is internal API. */ -@InternalApi -@Deprecated -public class RuleSetFactoryCompatibility { - private static final Logger LOG = Logger.getLogger(RuleSetFactoryCompatibility.class.getName()); +final class RuleSetFactoryCompatibility { - private List filters = new LinkedList<>(); + static final RuleSetFactoryCompatibility EMPTY = new RuleSetFactoryCompatibility(); + /** The instance with the built-in filters for the modified PMD rules. */ + static final RuleSetFactoryCompatibility DEFAULT = new RuleSetFactoryCompatibility(); - /** - * Creates a new instance of the compatibility filter with the built-in - * filters for the modified PMD rules. - */ - public RuleSetFactoryCompatibility() { + + static { // PMD 5.3.0 - addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); - addFilterRuleRemoved("java", "controversial", "BooleanInversion"); + DEFAULT.addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); + DEFAULT.addFilterRuleRemoved("java", "controversial", "BooleanInversion"); // PMD 5.3.1 - addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); + DEFAULT.addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); // PMD 5.4.0 - addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); - addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); - addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); - addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); - addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); // PMD 5.6.0 - addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); + DEFAULT.addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); // unused/UnusedModifier moved AND renamed, order is important! - addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); + DEFAULT.addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); // PMD 6.0.0 - addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); - addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); - addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); - addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); - addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); - addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); - addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); - addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); - addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); + DEFAULT.addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); + DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); + DEFAULT.addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); + DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); + DEFAULT.addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); // GuardLogStatementJavaUtil moved and renamed... - addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); - addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); + DEFAULT.addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); + DEFAULT.addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); + } + + private static final Logger LOG = Logger.getLogger(RuleSetFactoryCompatibility.class.getName()); + + private final List filters = new ArrayList<>(); + + void addFilterRuleMovedAndRenamed(String language, String oldRuleset, String oldName, String newRuleset, String newName) { filters.add(RuleSetFilter.ruleMoved(language, oldRuleset, newRuleset, oldName)); - filters.add(RuleSetFilter.ruleRenamedMoved(language, newRuleset, oldName, newName)); + filters.add(RuleSetFilter.ruleRenamed(language, newRuleset, oldName, newName)); } void addFilterRuleRenamed(String language, String ruleset, String oldName, String newName) { @@ -106,134 +98,130 @@ public class RuleSetFactoryCompatibility { filters.add(RuleSetFilter.ruleRemoved(language, ruleset, name)); } - /** - * Applies all configured filters against the given input stream. The - * resulting reader will contain the original ruleset modified by the - * filters. - * - * @param stream the original ruleset file input stream - * @return a reader, from which the filtered ruleset can be read - * @throws IOException if the stream couldn't be read - */ - public Reader filterRuleSetFile(InputStream stream) throws IOException { - byte[] bytes = IOUtils.toByteArray(stream); - String encoding = determineEncoding(bytes); - String ruleset = new String(bytes, encoding); - - ruleset = applyAllFilters(ruleset); - - return new StringReader(ruleset); + @Nullable String applyRef(String ref) { + return applyRef(ref, false); } - private String applyAllFilters(String ruleset) { - String result = ruleset; + + /** + * Returns the new rule ref, or null if the rule was deleted. Returns + * the argument if no replacement is needed. + * + * @param ref Original ref + * @param warn Whether to output a warning if a replacement is done + */ + public @Nullable String applyRef(String ref, boolean warn) { + String result = ref; for (RuleSetFilter filter : filters) { - result = filter.apply(result); + result = filter.applyRef(result, warn); + if (result == null) { + return null; + } } return result; } - private static final Pattern ENCODING_PATTERN = Pattern.compile("encoding=\"([^\"]+)\""); - /** - * Determines the encoding of the given bytes, assuming this is a XML - * document, which specifies the encoding in the first 1024 bytes. + * Returns the new rule name, or null if the rule was deleted. Returns + * the argument if no replacement is needed. * - * @param bytes - * the input bytes, might be more or less than 1024 bytes - * @return the determined encoding, falls back to the default UTF-8 encoding + * @param rulesetRef Ruleset name + * @param excludeName Original excluded name + * @param warn Whether to output a warning if a replacement is done */ - String determineEncoding(byte[] bytes) { - String firstBytes = new String(bytes, 0, bytes.length > 1024 ? 1024 : bytes.length, - StandardCharsets.ISO_8859_1); - Matcher matcher = ENCODING_PATTERN.matcher(firstBytes); - String encoding = StandardCharsets.UTF_8.name(); - if (matcher.find()) { - encoding = matcher.group(1); + public @Nullable String applyExclude(String rulesetRef, String excludeName, boolean warn) { + String result = excludeName; + for (RuleSetFilter filter : filters) { + result = filter.applyExclude(rulesetRef, result, warn); + if (result == null) { + return null; + } } - return encoding; + return result; } private static class RuleSetFilter { - private final Pattern refPattern; - private final String replacement; - private Pattern exclusionPattern; - private String exclusionReplacement; + + private static final String MOVED_MESSAGE = "The rule \"{1}\" has been moved from ruleset \"{0}\" to \"{2}\". Please change your ruleset!"; + private static final String RENAMED_MESSAGE = "The rule \"{1}\" has been renamed to \"{3}\". Please change your ruleset!"; + private static final String REMOVED_MESSAGE = "The rule \"{1}\" in ruleset \"{0}\" has been removed from PMD and no longer exists. Please change your ruleset!"; + private final String ruleRef; + private final String oldRuleset; + private final String oldName; + private final String newRuleset; + private final String newName; private final String logMessage; - private RuleSetFilter(String refPattern, String replacement, String logMessage) { + private RuleSetFilter(String oldRuleset, + String oldName, + @Nullable String newRuleset, + @Nullable String newName, + String logMessage) { + this.oldRuleset = oldRuleset; + this.oldName = oldName; + this.newRuleset = newRuleset; + this.newName = newName; this.logMessage = logMessage; - if (replacement != null) { - this.refPattern = Pattern.compile("ref=\"" + Pattern.quote(refPattern) + "\""); - this.replacement = "ref=\"" + replacement + "\""; - } else { - this.refPattern = Pattern.compile(""); - this.replacement = ""; - } - } - - private void setExclusionPattern(String oldName, String newName) { - exclusionPattern = Pattern.compile(""); - if (newName != null) { - exclusionReplacement = ""; - } else { - exclusionReplacement = ""; - } + this.ruleRef = oldRuleset + "/" + oldName; } public static RuleSetFilter ruleRenamed(String language, String ruleset, String oldName, String newName) { - RuleSetFilter filter = ruleRenamedMoved(language, ruleset, oldName, newName); - filter.setExclusionPattern(oldName, newName); - return filter; - } - - public static RuleSetFilter ruleRenamedMoved(String language, String ruleset, String oldName, String newName) { - String base = "rulesets/" + language + "/" + ruleset + ".xml/"; - return new RuleSetFilter(base + oldName, base + newName, "The rule \"" + oldName - + "\" has been renamed to \"" + newName + "\". Please change your ruleset!"); + String base = "rulesets/" + language + "/" + ruleset + ".xml"; + return new RuleSetFilter(base, oldName, base, newName, RENAMED_MESSAGE); } public static RuleSetFilter ruleMoved(String language, String oldRuleset, String newRuleset, String ruleName) { String base = "rulesets/" + language + "/"; - return new RuleSetFilter(base + oldRuleset + ".xml/" + ruleName, base + newRuleset + ".xml/" + ruleName, - "The rule \"" + ruleName + "\" has been moved from ruleset \"" + oldRuleset + "\" to \"" - + newRuleset + "\". Please change your ruleset!"); + return new RuleSetFilter(base + oldRuleset + ".xml", ruleName, + base + newRuleset + ".xml", ruleName, + MOVED_MESSAGE); } public static RuleSetFilter ruleRemoved(String language, String ruleset, String name) { - RuleSetFilter filter = new RuleSetFilter("rulesets/" + language + "/" + ruleset + ".xml/" + name, null, - "The rule \"" + name + "\" in ruleset \"" + ruleset - + "\" has been removed from PMD and no longer exists. Please change your ruleset!"); - filter.setExclusionPattern(name, null); - return filter; + String oldRuleset = "rulesets/" + language + "/" + ruleset + ".xml"; + return new RuleSetFilter(oldRuleset, name, + null, null, + REMOVED_MESSAGE); } - String apply(String ruleset) { - String result = ruleset; - Matcher matcher = refPattern.matcher(ruleset); + @Nullable String applyExclude(String ref, String name, boolean warn) { + if (oldRuleset.equals(ref) + && oldName.equals(name) + && oldRuleset.equals(newRuleset)) { + if (warn) { + warn(); + } - if (matcher.find()) { - result = matcher.replaceAll(replacement); + return newName; + } - if (LOG.isLoggable(Level.WARNING)) { - LOG.warning("Applying rule set filter: " + logMessage); + return name; + } + + @Nullable String applyRef(String ref, boolean warn) { + + if (ref.equals(this.ruleRef)) { + + if (warn) { + warn(); + } + + if (newName != null) { + return newRuleset + "/" + newName; + } else { + // deleted + return null; } } - if (exclusionPattern == null) { - return result; + return ref; + } + + private void warn() { + if (LOG.isLoggable(Level.WARNING)) { + String log = MessageFormat.format(logMessage, oldRuleset, oldName, newRuleset, newName); + LOG.warning("Applying rule set filter: " + log); } - - Matcher exclusions = exclusionPattern.matcher(result); - if (exclusions.find()) { - result = exclusions.replaceAll(exclusionReplacement); - - if (LOG.isLoggable(Level.WARNING)) { - LOG.warning("Applying rule set filter for exclusions: " + logMessage); - } - } - - return result; } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java new file mode 100644 index 0000000000..4fa14bcb62 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import net.sourceforge.pmd.annotation.InternalApi; + +/** + * An exception that is thrown when something wrong occurs while + * {@linkplain RuleSetLoader loading rulesets}. This may be because the + * XML is not well-formed, does not respect the ruleset schema, is + * not a valid ruleset or is otherwise unparsable. + */ +public final class RuleSetLoadException extends RuntimeException { + + /** Constructors are internal. */ + @InternalApi + public RuleSetLoadException(String message, Throwable cause) { + super(message, cause); + } + + /** Constructors are internal. */ + @InternalApi + public RuleSetLoadException(String message) { + super(message); + } + +} 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 61364fafb0..1aee4342b5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -4,33 +4,35 @@ package net.sourceforge.pmd; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; -import java.util.logging.Logger; + +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.ResourceLoader; /** - * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} - * and {@link RuleSetFactory}. This can be configured using a fluent - * API, see eg {@link #warnDeprecated(boolean)}. To create a list of - * rulesets, use {@link #loadFromResource(String)}. + * Configurable object to load rulesets from XML resources. + * This can be configured using a fluent API, see eg {@link #warnDeprecated(boolean)}. + * To create a new ruleset, use {@link #loadFromResource(String)} + * or some such overload. */ public final class RuleSetLoader { - private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); - private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; - private boolean enableCompatibility = true; + private @NonNull RuleSetFactoryCompatibility compatFilter = RuleSetFactoryCompatibility.DEFAULT; private boolean includeDeprecatedRuleReferences = false; /** @@ -53,6 +55,7 @@ public final class RuleSetLoader { * Filter loaded rules to only those that match or are above * the given priority. The default is {@link RulePriority#LOW}, * ie, no filtering occurs. + * * @return This instance, modified */ public RuleSetLoader filterAbovePriority(RulePriority minimumPriority) { @@ -63,6 +66,7 @@ public final class RuleSetLoader { /** * Log a warning when referencing a deprecated rule. * This is enabled by default. + * * @return This instance, modified */ public RuleSetLoader warnDeprecated(boolean warn) { @@ -75,10 +79,17 @@ public final class RuleSetLoader { * been moved or renamed. This is enabled by default, if disabled, * unresolved references will not be translated and will produce an * error. + * * @return This instance, modified */ public RuleSetLoader enableCompatibility(boolean enable) { - this.enableCompatibility = enable; + return setCompatibility(enable ? RuleSetFactoryCompatibility.DEFAULT + : RuleSetFactoryCompatibility.EMPTY); + } + + // test only + RuleSetLoader setCompatibility(@NonNull RuleSetFactoryCompatibility filter) { + this.compatFilter = filter; return this; } @@ -98,8 +109,8 @@ public final class RuleSetLoader { * Create a new rule set factory, if you have to (that class is deprecated). * That factory will use the configuration that was set using the setters of this. * - * @deprecated {@link RuleSetFactory} is deprecated, replace its usages with usages of this class, - * or of static factory methods of {@link RuleSet} + * @deprecated {@link RuleSetFactory} is deprecated, replace its usages + * with usages of this class, or of static factory methods of {@link RuleSet} */ @Deprecated public RuleSetFactory toFactory() { @@ -107,7 +118,7 @@ public final class RuleSetLoader { this.resourceLoader, this.minimumPriority, this.warnDeprecated, - this.enableCompatibility, + this.compatFilter, this.includeDeprecatedRuleReferences ); } @@ -117,26 +128,41 @@ public final class RuleSetLoader { * Parses and returns a ruleset from its location. The location may * be a file system path, or a resource path (see {@link #loadResourcesWith(ClassLoader)}). * - *

This replaces {@link RuleSetFactory#createRuleSet(String)}, - * but does not split commas. - * * @param rulesetPath A reference to a single ruleset * - * @throws RuleSetNotFoundException If the path does not correspond to a resource + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found) */ - public RuleSet loadFromResource(String rulesetPath) throws RuleSetNotFoundException { + public RuleSet loadFromResource(String rulesetPath) { return loadFromResource(new RuleSetReferenceId(rulesetPath)); } + /** + * Parses and returns a ruleset from string content. + * + * @param filename The symbolic "file name", for error messages. + * @param rulesetXmlContent Xml file contents + * + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax) + */ + public RuleSet loadFromString(String filename, String rulesetXmlContent) { + return loadFromResource(new RuleSetReferenceId(filename) { + @Override + public InputStream getInputStream(ResourceLoader rl) { + return new ByteArrayInputStream(rulesetXmlContent.getBytes(StandardCharsets.UTF_8)); + } + }); + } + /** * Parses several resources into a list of rulesets. * * @param paths Paths * - * @throws RuleSetNotFoundException If any resource throws - * @throws NullPointerException If the parameter, or any component is null + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ - public List loadFromResources(Collection paths) throws RuleSetNotFoundException { + public List loadFromResources(Collection paths) { List ruleSets = new ArrayList<>(paths.size()); for (String path : paths) { ruleSets.add(loadFromResource(path)); @@ -147,18 +173,24 @@ public final class RuleSetLoader { /** * Parses several resources into a list of rulesets. * - * @param paths Paths + * @param first First path + * @param rest Paths * - * @throws RuleSetNotFoundException If any resource throws - * @throws NullPointerException If the parameter, or any component is null + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ - public List loadFromResources(String... paths) throws RuleSetNotFoundException { - return loadFromResources(Arrays.asList(paths)); + public List loadFromResources(String first, String... rest) { + return loadFromResources(CollectionUtil.listOf(first, rest)); } // package private - RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { - return toFactory().createRuleSet(ruleSetReferenceId); + RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) { + try { + return toFactory().createRuleSet(ruleSetReferenceId); + } catch (Exception e) { + throw new RuleSetLoadException("Cannot parse " + ruleSetReferenceId, e); + } } @@ -174,17 +206,19 @@ public final class RuleSetLoader { /** * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. This + * "categories.properties" resource for each language. This * uses the classpath of the resource loader ({@link #loadResourcesWith(ClassLoader)}). * * @return A list of all category rulesets * - * @throws RuleSetNotFoundException if some ruleset file could not be parsed - * TODO shouldn't our API forbid this case? + * @throws RuleSetLoadException If a standard ruleset cannot be loaded. + * This is a corner case, that probably should not be caught by clients. + * The standard rulesets are well-formed, at least in stock PMD distributions. + * */ - public List getStandardRuleSets() throws RuleSetNotFoundException { + public List getStandardRuleSets() { String rulesetsProperties; - List ruleSetReferenceIds = new ArrayList<>(); + List ruleSetReferenceIds = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { Properties props = new Properties(); rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; @@ -192,16 +226,19 @@ public final class RuleSetLoader { props.load(inputStream); String rulesetFilenames = props.getProperty("rulesets.filenames"); if (rulesetFilenames != null) { - ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); + ruleSetReferenceIds.addAll(Arrays.asList(rulesetFilenames.split(","))); } - } catch (RuleSetNotFoundException e) { - LOG.fine("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - } catch (IOException ioe) { - throw new RuleSetNotFoundException("Couldn't read " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path"), ioe); + } catch (IOException e) { + throw new RuntimeException("Couldn't find " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path")); } } - return toFactory().createRuleSets(ruleSetReferenceIds); + + List ruleSets = new ArrayList<>(); + for (String id : ruleSetReferenceIds) { + ruleSets.add(loadFromResource(id)); // may throw + } + return ruleSets; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java deleted file mode 100644 index 5c86702518..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -public class RuleSetNotFoundException extends Exception { - private static final long serialVersionUID = -4617033110919250810L; - - public RuleSetNotFoundException(String msg) { - super(msg); - } - - public RuleSetNotFoundException(String msg, Throwable cause) { - super(msg, cause); - } -} 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 66dd5e47c9..d7ade89948 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -397,24 +398,29 @@ public class RuleSetReferenceId { * * @param rl The {@link ResourceLoader} to use. * @return An InputStream to that resource. - * @throws RuleSetNotFoundException - * if unable to find a resource. */ - public InputStream getInputStream(final ResourceLoader rl) throws RuleSetNotFoundException { + public InputStream getInputStream(final ResourceLoader rl) throws IOException { if (externalRuleSetReferenceId == null) { - InputStream in = StringUtils.isBlank(ruleSetFileName) ? null - : rl.loadResourceAsStream(ruleSetFileName); - if (in == null) { - throw new RuleSetNotFoundException("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")); + if (StringUtils.isBlank(ruleSetFileName)) { + throw notFoundException(); + } + try { + return rl.loadResourceAsStream(ruleSetFileName); + } catch (FileNotFoundException ignored) { + throw notFoundException(); } - return in; } else { return externalRuleSetReferenceId.getInputStream(rl); } } + 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 the String form of this Rule reference. * 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 4719748ad0..126d768b3a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; @@ -104,12 +105,12 @@ public class RuleSetWriter { Element descriptionElement = createDescriptionElement(ruleSet.getDescription()); ruleSetElement.appendChild(descriptionElement); - for (String excludePattern : ruleSet.getExcludePatterns()) { - Element excludePatternElement = createExcludePatternElement(excludePattern); + for (Pattern excludePattern : ruleSet.getFileExclusions()) { + Element excludePatternElement = createExcludePatternElement(excludePattern.pattern()); ruleSetElement.appendChild(excludePatternElement); } - for (String includePattern : ruleSet.getIncludePatterns()) { - Element includePatternElement = createIncludePatternElement(includePattern); + for (Pattern includePattern : ruleSet.getFileInclusions()) { + Element includePatternElement = createIncludePatternElement(includePattern.pattern()); ruleSetElement.appendChild(includePatternElement); } for (Rule rule : ruleSet.getRules()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java index 6ed06601cf..8820c284e9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java @@ -49,7 +49,7 @@ public class RuleSets { this.ruleSets = Collections.unmodifiableList(rsets); } - public RuleSets(Collection ruleSets) { + public RuleSets(Collection ruleSets) { this.ruleSets = Collections.unmodifiableList(new ArrayList<>(ruleSets)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java deleted file mode 100644 index bbc04e9e98..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ /dev/null @@ -1,210 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.util.ResourceLoader; - -/** - * @deprecated Use a {@link RuleSetLoader} instead - */ -@Deprecated -public final class RulesetsFactoryUtils { - - private static final Logger LOG = Logger.getLogger(RulesetsFactoryUtils.class.getName()); - - private RulesetsFactoryUtils() { - } - - /** - * Creates a new rulesets with the given string. The resulting rulesets will - * contain all referenced rulesets. - * - * @param rulesets - * the string with the rulesets to load - * @param factory - * the ruleset factory - * @return the rulesets - * @throws IllegalArgumentException - * if rulesets is empty (means, no rules have been found) or if - * a ruleset couldn't be found. - * @deprecated Internal API - */ - @InternalApi - @Deprecated - public static List getRuleSets(String rulesets, RuleSetLoader factory) { - List ruleSets; - try { - ruleSets = factory.loadFromResources(rulesets.split(",")); - printRuleNamesInDebug(ruleSets); - if (ruleSets.stream().mapToInt(RuleSet::size).sum() == 0) { - String msg = "No rules found. Maybe you misspelled a rule name? (" + rulesets + ')'; - LOG.log(Level.SEVERE, msg); - throw new IllegalArgumentException(msg); - } - } catch (RuleSetNotFoundException rsnfe) { - LOG.log(Level.SEVERE, "Ruleset not found", rsnfe); - throw new IllegalArgumentException(rsnfe); - } - return ruleSets; - } - - /** - * @deprecated Use a {@link RuleSetLoader} - */ - @InternalApi - @Deprecated - public static RuleSetFactory getRulesetFactory(final PMDConfiguration configuration, - final ResourceLoader resourceLoader) { - return new RuleSetFactory(resourceLoader, configuration.getMinimumPriority(), true, - configuration.isRuleSetFactoryCompatibilityEnabled()); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param configuration PMD configuration, contains info about the - * factory parameters - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration, ClassLoader) - * - * @deprecated Use {@link RuleSetLoader#fromPmdConfig(PMDConfiguration)} - */ - @Deprecated - public static RuleSetFactory createFactory(final PMDConfiguration configuration) { - return createFactory(configuration, RulesetsFactoryUtils.class.getClassLoader()); - } - - /** - * Returns a ruleset factory with default parameters. It doesn't prune - * rules based on priority, and doesn't warn for deprecations. - * - * @return A ruleset factory - * - * @see RuleSetLoader - */ - public static RuleSetFactory defaultFactory() { - return new RuleSetFactory(); - } - - /** - * Returns a ruleset factory which uses the provided {@link ClassLoader} - * to resolve resource references. It warns for deprecated rule usages. - * - * @param configuration PMD configuration, contains info about the - * factory parameters - * @param classLoader Class loader to load resources - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { - return createFactory(classLoader, - configuration.getMinimumPriority(), - true, - configuration.isRuleSetFactoryCompatibilityEnabled()); - } - - /** - * Returns a ruleset factory which uses the provided {@link ClassLoader} - * to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * @param classLoader Class loader to load resources - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(ClassLoader classLoader, - RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility) { - - return new RuleSetFactory(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility) { - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * @param includeDeprecatedRuleReferences If true, deprecated rule references are retained. Usually, these - * references are ignored, since they indicate renamed/moved rules, and the referenced - * rule is often included in the same ruleset. Enabling this might result in - * duplicated rules. - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility, - boolean includeDeprecatedRuleReferences) { - - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility, - includeDeprecatedRuleReferences); - } - - /** - * If in debug modus, print the names of the rules. - * - * @param rulesets the RuleSets to print - */ - private static void printRuleNamesInDebug(List rulesets) { - if (LOG.isLoggable(Level.FINER)) { - for (RuleSet rset : rulesets) { - for (Rule r : rset.getRules()) { - LOG.finer("Loaded rule " + r.getName()); - } - } - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index b03cd7c626..0e24a2024a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -4,13 +4,14 @@ package net.sourceforge.pmd.ant.internal; +import java.io.File; import static java.util.Arrays.asList; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import org.apache.commons.lang3.StringUtils; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -24,8 +25,8 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetLoadException; import net.sourceforge.pmd.RuleSetLoader; -import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.ant.Formatter; import net.sourceforge.pmd.ant.PMDTask; import net.sourceforge.pmd.ant.SourceLanguage; @@ -50,6 +51,7 @@ public class PMDTaskImpl { private final List formatters = new ArrayList<>(); private final List filesets = new ArrayList<>(); private final PMDConfiguration configuration = new PMDConfiguration(); + private final String rulesetPaths; private boolean failOnRuleViolation; private int maxRuleViolations = 0; private String failuresPropertyName; @@ -65,7 +67,7 @@ public class PMDTaskImpl { if (this.maxRuleViolations > 0) { this.failOnRuleViolation = true; } - configuration.setRuleSets(task.getRulesetFiles()); + this.rulesetPaths = task.getRulesetFiles() == null ? "" : task.getRulesetFiles(); configuration.setRuleSetFactoryCompatibilityEnabled(!task.isNoRuleSetCompatibility()); if (task.getEncoding() != null) { configuration.setSourceEncoding(task.getEncoding()); @@ -102,20 +104,7 @@ public class PMDTaskImpl { RuleSetLoader rulesetLoader = RuleSetLoader.fromPmdConfig(configuration) .loadResourcesWith(setupResourceLoader()); - List rules; - try { - // This is just used to validate and display rules. Each thread will create its own ruleset - String ruleSets = configuration.getRuleSets(); - if (StringUtils.isNotBlank(ruleSets)) { - // Substitute env variables/properties - configuration.setRuleSets(project.replaceProperties(ruleSets)); - } - List paths = asList(configuration.getRuleSets().split(",")); - rules = rulesetLoader.loadFromResources(paths); - logRulesUsed(rules); - } catch (RuleSetNotFoundException e) { - throw new BuildException(e.getMessage(), e); - } + List rules = loadRulesets(rulesetLoader); if (configuration.getSuppressMarker() != null) { project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); @@ -146,6 +135,24 @@ public class PMDTaskImpl { } } + private List loadRulesets(RuleSetLoader rulesetLoader) { + try { + // This is just used to validate and display rules. Each thread will create its own ruleset + // Substitute env variables/properties + String ruleSetString = project.replaceProperties(rulesetPaths); + + List rulesets = Arrays.asList(ruleSetString.split(",")); + List rulesetList = rulesetLoader.loadFromResources(rulesets); + if (rulesetList.isEmpty()) { + throw new BuildException("No rulesets"); + } + logRulesUsed(rulesetList); + return rulesetList; + } catch (RuleSetLoadException e) { + throw new BuildException(e.getMessage(), e); + } + } + private List collectFiles(List filesets, Project project, boolean reportShortNames) { final List files = new ArrayList<>(); for (FileSet fs : filesets) { @@ -248,10 +255,10 @@ public class PMDTaskImpl { } } - private void logRulesUsed(List rules) { - project.log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE); + private void logRulesUsed(List rulesets) { + project.log("Using these rulesets: " + rulesetPaths, Project.MSG_VERBOSE); - for (RuleSet ruleSet : rules) { + for (RuleSet ruleSet : rulesets) { for (Rule rule : ruleSet.getRules()) { project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE); } 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 0f37b74d06..c8e4cd7941 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 @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cli; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -203,7 +204,7 @@ public class PMDParameters { configuration.setReportFile(this.getReportfile()); configuration.setReportProperties(this.getProperties()); configuration.setReportShortNames(this.isShortnames()); - configuration.setRuleSets(this.getRulesets()); + configuration.setRuleSets(Arrays.asList(this.getRulesets().split(","))); configuration.setRuleSetFactoryCompatibilityEnabled(!this.noRuleSetCompatibility); configuration.setShowSuppressedViolations(this.isShowsuppressed()); configuration.setSourceEncoding(this.getEncoding()); @@ -293,8 +294,7 @@ public class PMDParameters { return reportfile; } - @Nullable - private LanguageVersion getLangVersion() { + private @Nullable LanguageVersion getLangVersion() { Language lang = language != null ? LanguageRegistry.findLanguageByTerseName(language) : LanguageRegistry.getDefaultLanguage(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index da009e56cc..262b0e5096 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -91,6 +91,9 @@ public class CPDConfiguration extends AbstractConfiguration { @Parameter(names = "--ignore-usings", description = "Ignore using directives in C#", required = false) private boolean ignoreUsings; + @Parameter(names = "--ignore-literal-sequences", description = "Ignore sequences of literals", required = false) + private boolean ignoreLiteralSequences = false; + @Parameter(names = "--skip-lexical-errors", description = "Skip files which can't be tokenized due to invalid characters instead of aborting CPD", required = false) @@ -273,6 +276,11 @@ public class CPDConfiguration extends AbstractConfiguration { } else { properties.remove(Tokenizer.IGNORE_USINGS); } + if (configuration.isIgnoreLiteralSequences()) { + properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, "true"); + } else { + properties.remove(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES); + } properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(!configuration.isNoSkipBlocks())); properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, configuration.getSkipBlocksPattern()); configuration.getLanguage().setProperties(properties); @@ -411,6 +419,14 @@ public class CPDConfiguration extends AbstractConfiguration { this.ignoreUsings = ignoreUsings; } + public boolean isIgnoreLiteralSequences() { + return ignoreLiteralSequences; + } + + public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + public boolean isSkipLexicalErrors() { return skipLexicalErrors; } 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 fd7e436af2..3f44d76b34 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 @@ -115,6 +115,10 @@ public class GUI implements CPDListener { return false; } + public boolean canIgnoreLiteralSequences() { + return false; + } + public abstract String[] extensions(); } @@ -160,6 +164,11 @@ public class GUI implements CPDListener { public boolean canIgnoreUsings() { return "cs".equals(terseName); } + + @Override + public boolean canIgnoreLiteralSequences() { + return "cs".equals(terseName); + } }; } LANGUAGE_SETS[index][0] = "by extension..."; @@ -333,6 +342,7 @@ public class GUI implements CPDListener { private JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false); private JCheckBox ignoreAnnotationsCheckbox = new JCheckBox("", false); private JCheckBox ignoreUsingsCheckbox = new JCheckBox("", false); + private JCheckBox ignoreLiteralSequencesCheckbox = new JCheckBox("", false); private JComboBox languageBox = new JComboBox<>(); private JTextField extensionField = new JTextField(); private JLabel extensionLabel = new JLabel("Extension:", SwingConstants.RIGHT); @@ -420,6 +430,7 @@ public class GUI implements CPDListener { ignoreLiteralsCheckbox.setEnabled(current.canIgnoreLiterals()); ignoreAnnotationsCheckbox.setEnabled(current.canIgnoreAnnotations()); ignoreUsingsCheckbox.setEnabled(current.canIgnoreUsings()); + ignoreLiteralSequencesCheckbox.setEnabled(current.canIgnoreLiteralSequences()); extensionField.setText(current.extensions()[0]); boolean enableExtension = current.extensions()[0].isEmpty(); extensionField.setEnabled(enableExtension); @@ -478,6 +489,13 @@ public class GUI implements CPDListener { helper.nextRow(); helper.addLabel("Ignore usings?"); helper.add(ignoreUsingsCheckbox); + helper.addLabel(""); + helper.addLabel(""); + helper.nextRow(); + + helper.nextRow(); + helper.addLabel("Ignore literal sequences?"); + helper.add(ignoreLiteralSequencesCheckbox); helper.add(goButton); helper.add(cxButton); helper.nextRow(); @@ -663,6 +681,7 @@ public class GUI implements CPDListener { config.setIgnoreLiterals(ignoreLiteralsCheckbox.isSelected()); config.setIgnoreAnnotations(ignoreAnnotationsCheckbox.isSelected()); config.setIgnoreUsings(ignoreUsingsCheckbox.isSelected()); + config.setIgnoreLiteralSequences(ignoreLiteralSequencesCheckbox.isSelected()); p.setProperty(LanguageFactory.EXTENSION, extensionField.getText()); LanguageConfig conf = languageConfigFor((String) languageBox.getSelectedItem()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 77e2de54d9..e6876fb960 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -11,6 +11,10 @@ public interface Tokenizer { String IGNORE_IDENTIFIERS = "ignore_identifiers"; String IGNORE_ANNOTATIONS = "ignore_annotations"; + /** + * Ignore sequences of literals (e.g, 0,0,0,0...). + */ + String OPTION_IGNORE_LITERAL_SEQUENCES = "net.sourceforge.pmd.cpd.Tokenizer.skipLiteralSequences"; /** * Ignore using directives in C#. The default value is false. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java index 572492d864..0444155638 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java @@ -6,10 +6,13 @@ package net.sourceforge.pmd.lang; import java.util.Collections; import java.util.List; +import java.util.Locale; import org.apache.commons.lang3.EnumUtils; import net.sourceforge.pmd.lang.ast.AstProcessingStage; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertySource; /** @@ -53,4 +56,48 @@ public abstract class AbstractPmdLanguageVersionHandler extends AbstractLanguage } + + /** + * Returns the environment variable name that a user can set in order to override the default value. + */ + String getEnvironmentVariableName(String langTerseName, PropertyDescriptor propertyDescriptor) { + if (langTerseName == null) { + throw new IllegalStateException("Language is null"); + } + return "PMD_" + langTerseName.toUpperCase(Locale.ROOT) + "_" + + propertyDescriptor.name().toUpperCase(Locale.ROOT); + } + + /** + * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment + * variable has been set. + */ + + String getEnvValue(String langTerseName, PropertyDescriptor propertyDescriptor) { + // note: since we use environent variables and not system properties, + // tests override this method. + return System.getenv(getEnvironmentVariableName(langTerseName, propertyDescriptor)); + } + + /** + * Overrides the default PropertyDescriptors with values found in environment variables. + * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented + */ + @Deprecated + protected final void overridePropertiesFromEnv(String langTerseName, PropertySource source) { + for (PropertyDescriptor propertyDescriptor : source.getPropertyDescriptors()) { + String propertyValue = getEnvValue(langTerseName, propertyDescriptor); + + if (propertyValue != null) { + setPropertyCapture(source, propertyDescriptor, propertyValue); + } + } + } + + @Deprecated + private void setPropertyCapture(PropertySource source, PropertyDescriptor propertyDescriptor, String propertyValue) { + T value = propertyDescriptor.valueFrom(propertyValue); + source.setProperty(propertyDescriptor, value); + } + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java index 834eaa36f4..dfc70d4508 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java @@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; +import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.util.designerbindings.DesignerBindings; import net.sourceforge.pmd.util.designerbindings.DesignerBindings.DefaultDesignerBindings; @@ -48,12 +49,11 @@ public interface LanguageVersionHandler { /** - * Get the default ParserOptions. - * - * @return ParserOptions + * @deprecated This is transitional */ - default ParserOptions getDefaultParserOptions() { - return new ParserOptions(); + @Deprecated + default void declareParserTaskProperties(PropertySource source) { + // do nothing } @@ -62,13 +62,9 @@ public interface LanguageVersionHandler { * * @return Parser */ - Parser getParser(ParserOptions parserOptions); + Parser getParser(); - default Parser getParser() { - return getParser(getDefaultParserOptions()); - } - /** * Get the RuleViolationFactory. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java deleted file mode 100644 index 0834306906..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang; - -import java.util.Objects; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.lang.ast.Parser; - -/** - * Represents a set of configuration options for a {@link Parser}. For each - * unique combination of ParserOptions a Parser will be used to create an AST. - * Therefore, implementations must implement {@link Object#equals(Object)} and - * {@link Object#hashCode()}. - */ -public class ParserOptions { - private String suppressMarker = PMD.SUPPRESS_MARKER; - - public final @NonNull String getSuppressMarker() { - return suppressMarker; - } - - public final void setSuppressMarker(@NonNull String suppressMarker) { - Objects.requireNonNull(suppressMarker); - this.suppressMarker = suppressMarker; - } - - public ParserOptions() { - this(PMD.SUPPRESS_MARKER); - } - - public ParserOptions(String suppressMarker) { - setSuppressMarker(suppressMarker); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final ParserOptions that = (ParserOptions) obj; - return this.getSuppressMarker().equals(that.getSuppressMarker()); - } - - @Override - public int hashCode() { - return getSuppressMarker().hashCode(); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java index c08390ed32..8dd5b262c9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java @@ -10,6 +10,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.properties.AbstractPropertySource; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.util.document.TextDocument; /** @@ -46,17 +50,25 @@ public interface Parser { private final TextDocument textDoc; private final SemanticErrorReporter reporter; - private final String commentMarker; - + private final PropertySource propertySource; public ParserTask(TextDocument textDoc, SemanticErrorReporter reporter) { - this(textDoc, reporter, PMD.SUPPRESS_MARKER); - } - - public ParserTask(TextDocument textDoc, SemanticErrorReporter reporter, String commentMarker) { this.textDoc = Objects.requireNonNull(textDoc, "Text document was null"); this.reporter = Objects.requireNonNull(reporter, "reporter was null"); - this.commentMarker = Objects.requireNonNull(commentMarker, "commentMarker was null"); + + this.propertySource = new ParserTaskProperties(); + propertySource.definePropertyDescriptor(COMMENT_MARKER); + } + + public static final PropertyDescriptor COMMENT_MARKER = + PropertyFactory.stringProperty("suppressionCommentMarker") + .desc("deprecated! NOPMD") + .defaultValue(PMD.SUPPRESS_MARKER) + .build(); + + @Deprecated // transitional until language properties are implemented + public PropertySource getProperties() { + return propertySource; } @@ -97,7 +109,39 @@ public interface Parser { * The suppression marker for comments. */ public @NonNull String getCommentMarker() { - return commentMarker; + return getProperties().getProperty(COMMENT_MARKER); + } + + + private static final class ParserTaskProperties extends AbstractPropertySource { + + @Override + protected String getPropertySourceType() { + return "ParserOptions"; + } + + @Override + public String getName() { + return "n/a"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ParserTaskProperties)) { + return false; + } + final ParserTaskProperties that = (ParserTaskProperties) obj; + return Objects.equals(getPropertiesByPropertyDescriptor(), + that.getPropertiesByPropertyDescriptor()); + } + + @Override + public int hashCode() { + return getPropertiesByPropertyDescriptor().hashCode(); + } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java index f34e442045..be57a2cbc7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java @@ -12,7 +12,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -182,11 +181,6 @@ public abstract class AbstractDelegateRule implements Rule { rule.setPriority(priority); } - @Override - public ParserOptions getParserOptions() { - return rule.getParserOptions(); - } - @Override public void definePropertyDescriptor(PropertyDescriptor propertyDescriptor) throws IllegalArgumentException { rule.definePropertyDescriptor(propertyDescriptor); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java index 8127daf29b..566836ae0b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.properties.AbstractPropertySource; @@ -228,18 +227,6 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul this.priority = priority; } - /** - * This implementation returns a new instance of {@link ParserOptions} using - * default settings. - * - * @see Rule#setPriority(RulePriority) - */ - @Override - @Deprecated - public ParserOptions getParserOptions() { - return new ParserOptions(); - } - private Set> getClassRuleChainVisits() { if (classRuleChainVisits.isEmpty() && ruleChainVisits.isEmpty()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java index 52b29b72d9..3e7aa0a90e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java @@ -38,7 +38,21 @@ public class AttributeAxisIterator implements Iterator { Long.TYPE, Character.TYPE, Float.TYPE)); private static final Set FILTERED_OUT_NAMES - = new HashSet<>(Arrays.asList("toString", "getNumChildren", "getIndexInParent", "getParent", "getSourceCodeFile", "getClass", "getRuleIndex", "getXPathNodeName", "altNumber", "toStringTree", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope")); + = new HashSet<>(Arrays.asList("toString", + "getNumChildren", + "getIndexInParent", + "getParent", + "getClass", + "getSourceCodeFile", + "isFindBoundary", + "getRuleIndex", + "getXPathNodeName", + "altNumber", + "toStringTree", + "getTypeNameNode", + "hashCode", + "getImportedNameNode", + "getScope")); /* Iteration variables */ private final Iterator iterator; 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 c725f2d8ed..0e7a1061fd 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 @@ -14,6 +14,8 @@ import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.cache.AnalysisCache; import net.sourceforge.pmd.internal.RulesetStageDependencyHelper; import net.sourceforge.pmd.internal.SystemProps; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.LanguageVersionHandler; import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; @@ -114,11 +116,16 @@ abstract class PmdRunnable implements Runnable { ParserTask task = new ParserTask( textDocument, - SemanticErrorReporter.noop(), // TODO - configuration.getSuppressMarker() + SemanticErrorReporter.noop() // TODO ); - Parser parser = textDocument.getLanguageVersion().getLanguageVersionHandler().getParser(); + + LanguageVersionHandler handler = textDocument.getLanguageVersion().getLanguageVersionHandler(); + + handler.declareParserTaskProperties(task.getProperties()); + task.getProperties().setProperty(ParserTask.COMMENT_MARKER, configuration.getSuppressMarker()); + + Parser parser = handler.getParser(); RootNode rootNode = parse(parser, task); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 3d7d7a3c95..2cb4beb2a3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -216,4 +216,5 @@ public abstract class AbstractPropertySource implements PropertySource { private String errorForPropCapture(PropertyDescriptor descriptor) { return descriptor.errorFor(getProperty(descriptor)); } + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java index 8a01f07572..854dab11d5 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java @@ -6,15 +6,12 @@ package net.sourceforge.pmd.properties; import java.util.Objects; -import net.sourceforge.pmd.RuleSetFactory; - /** * Field names for parsing the properties out of the ruleset xml files. These are intended to be used as the keys to a * map of fields to values. Most property descriptors can be built directly from such a map using their factory. * * @author Brian Remedios - * @see RuleSetFactory * @see PropertyTypeId * @deprecated Will be removed with 7.0.0 */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index e541aad8a1..1880e45de5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,6 +16,10 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { + private static final char SMALL_SEPARATOR = ':'; + private static final String MEDIUM_SEPARATOR = ":\t"; + private static final String LARGE_SEPARATOR = "\t-\t"; + public static final String NAME = "text"; public TextRenderer() { @@ -35,8 +39,9 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(':').append(rv.getBeginLine()); - buf.append(":\t").append(rv.getDescription()).append(PMD.EOL); + buf.append(SMALL_SEPARATOR).append(rv.getBeginLine()); + buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()); + buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()).append(PMD.EOL); writer.write(buf.toString()); } } @@ -48,7 +53,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ProcessingError error : errors) { buf.setLength(0); buf.append(determineFileName(error.getFile())); - buf.append("\t-\t").append(error.getMsg()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.getMsg()).append(PMD.EOL); writer.write(buf.toString()); } @@ -66,7 +71,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ConfigurationError error : configErrors) { buf.setLength(0); buf.append(error.rule().getName()); - buf.append("\t-\t").append(error.issue()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.issue()).append(PMD.EOL); writer.write(buf.toString()); } } 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 88fc97bcc2..a7ff8cbbee 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 @@ -10,6 +10,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Date; @@ -68,7 +70,8 @@ public class XMLRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { String encoding = getProperty(ENCODING); - lineSeparator = PMD.EOL.getBytes(encoding); + String unmarkedEncoding = toUnmarkedEncoding(encoding); + lineSeparator = PMD.EOL.getBytes(unmarkedEncoding); try { xmlWriter.writeStartDocument(encoding, "1.0"); @@ -88,6 +91,26 @@ public class XMLRenderer extends AbstractIncrementingRenderer { } } + /** + * Return a encoding, which doesn't write a BOM (byte order mark). + * Only UTF-16 encoders might write a BOM, see {@link Charset}. + * + *

This is needed, so that we don't accidentally add BOMs whenever + * we insert a newline. + * + * @return + */ + private static String toUnmarkedEncoding(String encoding) { + if (StandardCharsets.UTF_16.name().equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16BE.name(); + } + // edge case: UTF-16LE with BOM + if ("UTF-16LE_BOM".equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16LE.name(); + } + return encoding; + } + /** * Outputs a platform dependent line separator. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java index 422f2f0dd6..218ddf5a76 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java @@ -294,6 +294,23 @@ public final class CollectionUtil { return newM; } + /** + * Returns an unmodifiable set containing the set union of the collection, + * and the new elements. + */ + @SafeVarargs + @SuppressWarnings("unchecked") + public static Set setUnion(Collection set, V first, V... newElements) { + if (set instanceof PSet) { + return ((PSet) set).plus(first).plusAll(Arrays.asList(newElements)); + } + Set newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length); + newSet.addAll(set); + newSet.add(first); + Collections.addAll(newSet, newElements); + return Collections.unmodifiableSet(newSet); + } + /** * Returns a map associating each key in the first list to its 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 38acec4624..629314a40e 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 @@ -17,10 +17,12 @@ import java.nio.file.Files; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; +import java.util.List; 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; import net.sourceforge.pmd.annotation.InternalApi; @@ -136,4 +138,24 @@ public final class IOUtil { } return composed; } + + /** + * Ensure that the closeables are closed. In the end, throws the + * pending exception if not null, or the exception retuned by {@link #closeAll(Collection)} + * if not null. If both are non-null, adds one of them to the suppress + * list of the other, and throws that one. + */ + public static void ensureClosed(List toClose, + @Nullable Exception pendingException) throws Exception { + Exception closeException = closeAll(toClose); + if (closeException != null) { + if (pendingException != null) { + closeException.addSuppressed(pendingException); + throw closeException; + } + // else no exception at all + } else if (pendingException != null) { + throw pendingException; + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java index 5f9fe917bc..d9d4d7e5b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.util; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -13,8 +14,10 @@ import java.net.URLConnection; import java.nio.file.Files; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.annotation.InternalApi; /** @@ -58,10 +61,10 @@ public class ResourceLoader { * Caller is responsible for closing the {@link InputStream}. * * @param name The resource to attempt and load + * * @return InputStream - * @throws RuleSetNotFoundException */ - public InputStream loadResourceAsStream(final String name) throws RuleSetNotFoundException { + public @NonNull InputStream loadResourceAsStream(final String name) throws IOException { // Search file locations first final File file = new File(name); if (file.exists()) { @@ -69,7 +72,8 @@ public class ResourceLoader { return Files.newInputStream(file.toPath()); } catch (final IOException e) { // if the file didn't exist, we wouldn't be here - throw new RuntimeException(e); // somehow the file vanished between checking for existence and opening + // somehow the file vanished between checking for existence and opening + throw new IOException("File was checked to exist", e); } } @@ -78,20 +82,18 @@ public class ResourceLoader { final HttpURLConnection connection = (HttpURLConnection) new URL(name).openConnection(); connection.setConnectTimeout(TIMEOUT); connection.setReadTimeout(TIMEOUT); - return connection.getInputStream(); - } catch (final Exception e) { - try { - return loadClassPathResourceAsStream(name); - } catch (final IOException ignored) { - // We will throw our own exception, with a different message + InputStream is = connection.getInputStream(); + if (is != null) { + return is; } + } catch (final Exception e) { + return loadClassPathResourceAsStreamOrThrow(name); } - throw new RuleSetNotFoundException("Can't find resource " + name - + ". Make sure the resource is a valid file or URL or is on the CLASSPATH"); + throw new IOException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the classpath"); } - public InputStream loadClassPathResourceAsStream(final String name) throws IOException { + public @Nullable InputStream loadClassPathResourceAsStream(final String name) throws IOException { /* * Don't use getResourceAsStream to avoid reusing connections between threads * See https://github.com/pmd/pmd/issues/234 @@ -110,7 +112,7 @@ public class ResourceLoader { } } - public InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws RuleSetNotFoundException { + public @NonNull InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws IOException { InputStream is = null; try { is = loadClassPathResourceAsStream(name); @@ -119,8 +121,8 @@ public class ResourceLoader { } if (is == null) { - throw new RuleSetNotFoundException("Can't find resource " + name - + ". Make sure the resource is on the CLASSPATH"); + throw new FileNotFoundException("Can't find resource " + name + + ". Make sure the resource is on the classpath"); } return is; @@ -128,12 +130,6 @@ public class ResourceLoader { /** * Load the rule from the classloader from resource loader, consistent with the ruleset - * - * @param clazz - * @return - * @throws ClassNotFoundException - * @throws IllegalAccessException - * @throws InstantiationException */ public Rule loadRuleFromClassPath(final String clazz) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return (Rule) classLoader.loadClass(clazz).newInstance(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/CpdCompat.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/CpdCompat.java index fa677a573f..53c400634d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/CpdCompat.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/CpdCompat.java @@ -24,7 +24,7 @@ public final class CpdCompat { @Deprecated private static final Language DUMMY_LANG = new BaseLanguageModule("dummy", "dummy", "dummy", "dummy") { { - addDefaultVersion("", parserOptions -> task -> { + addDefaultVersion("", () -> task -> { throw new UnsupportedOperationException(); }); } diff --git a/pmd-core/src/main/resources/rulesets/releases/33.xml b/pmd-core/src/main/resources/rulesets/releases/33.xml index 08d1dcd8bf..895e7820d9 100644 --- a/pmd-core/src/main/resources/rulesets/releases/33.xml +++ b/pmd-core/src/main/resources/rulesets/releases/33.xml @@ -8,7 +8,7 @@ This ruleset contains links to rules that are new in PMD v3.3 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/510.xml b/pmd-core/src/main/resources/rulesets/releases/510.xml index 82e6c2530e..09a1172297 100644 --- a/pmd-core/src/main/resources/rulesets/releases/510.xml +++ b/pmd-core/src/main/resources/rulesets/releases/510.xml @@ -12,7 +12,7 @@ This ruleset contains links to rules that are new in PMD v5.1.0 - + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java index 480bdb24a1..d765920004 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java @@ -111,13 +111,6 @@ public class ConfigurationTest { Assert.assertArrayEquals(expectedUris, uris); } - @Test - public void testRuleSets() { - PMDConfiguration configuration = new PMDConfiguration(); - assertEquals("Default RuleSets", null, configuration.getRuleSets()); - configuration.setRuleSets("/rulesets/basic.xml"); - assertEquals("Changed RuleSets", "/rulesets/basic.xml", configuration.getRuleSets()); - } @Test public void testMinimumPriority() { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java index 4d313689fa..bcf485503e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java @@ -18,7 +18,7 @@ import net.sourceforge.pmd.reporting.FileAnalysisListener; public class RuleContextTest { - public static Report getReport(Consumer sideEffects) throws Exception { + public static Report getReport(Consumer sideEffects) { ReportBuilderListener listener = new ReportBuilderListener(); try { sideEffects.accept(listener); @@ -47,13 +47,6 @@ public class RuleContextTest { Assert.assertEquals("message with \"{\"", report.getViolations().get(0).getDescription()); } - @Test - public void testMessageArgs() throws Exception { - Report report = getReport(new FooRule(), (r, ctx) -> ctx.addViolationWithMessage(DummyTreeUtil.tree(DummyTreeUtil::root), "message with 1 argument: \"{0}\"", "testarg1")); - - Assert.assertEquals("message with 1 argument: \"testarg1\"", report.getViolations().get(0).getDescription()); - } - @Test public void testMessageEscaping() throws Exception { RuleViolation violation = makeViolation("message with \"'{'\""); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java index 7dfb6e1f09..bbc4a5db96 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java @@ -4,17 +4,9 @@ package net.sourceforge.pmd; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.Reader; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.util.ResourceLoader; - public class RuleSetFactoryCompatibilityTest { @Test @@ -26,37 +18,30 @@ public class RuleSetFactoryCompatibilityTest { + " Test\n" + "\n" + " \n" + "\n"; - RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); - factory.getCompatibilityFilter().addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); + RuleSetFactoryCompatibility compat = new RuleSetFactoryCompatibility(); + compat.addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); + + + RuleSetLoader factory = new RuleSetLoader().setCompatibility(compat); + RuleSet createdRuleSet = factory.loadFromString("dummy.xml", ruleset); - RuleSet createdRuleSet = createRulesetFromString(ruleset, factory); Assert.assertNotNull(createdRuleSet.getRuleByName("DummyBasicMockRule")); } @Test - public void testCorrectMovedAndRename() throws Exception { - final String ruleset = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" + "\n"; + public void testCorrectMovedAndRename() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMoved("dummy", "notexisting", "basic", "OldDummyBasicMockRule"); rsfc.addFilterRuleRenamed("dummy", "basic", "OldDummyBasicMockRule", "NewNameForDummyBasicMockRule"); - InputStream stream = new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String out = rsfc.applyRef("rulesets/dummy/notexisting.xml/OldDummyBasicMockRule"); - Assert.assertFalse(out.contains("notexisting.xml")); - Assert.assertFalse(out.contains("OldDummyBasicMockRule")); - Assert.assertTrue(out.contains("")); + Assert.assertEquals("rulesets/dummy/basic.xml/NewNameForDummyBasicMockRule", out); } @Test - public void testExclusion() throws Exception { + public void testExclusion() { final String ruleset = "\n" + "\n" + "Test\n" + "\n" + " \n" + " \n" + " \n" + "\n"; - RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); - factory.getCompatibilityFilter().addFilterRuleRenamed("dummy", "basic", "OldNameOfSampleXPathRule", - "SampleXPathRule"); + RuleSetFactoryCompatibility compat = new RuleSetFactoryCompatibility(); + compat.addFilterRuleRenamed("dummy", "basic", "OldNameOfSampleXPathRule", "SampleXPathRule"); + + RuleSetLoader factory = new RuleSetLoader().setCompatibility(compat); + RuleSet createdRuleSet = factory.loadFromString("dummy.xml", ruleset); - RuleSet createdRuleSet = createRulesetFromString(ruleset, factory); Assert.assertNotNull(createdRuleSet.getRuleByName("DummyBasicMockRule")); Assert.assertNull(createdRuleSet.getRuleByName("SampleXPathRule")); } @Test - public void testExclusionRenamedAndMoved() throws Exception { - final String ruleset = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" - + " \n" - + " \n" - + "\n"; + public void testExclusionRenamedAndMoved() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMovedAndRenamed("dummy", "oldbasic", "OldDummyBasicMockRule", "basic", "NewNameForDummyBasicMockRule"); - InputStream stream = new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String in = "rulesets/dummy/oldbasic.xml"; + String out = rsfc.applyRef(in); - Assert.assertTrue(out.contains("OldDummyBasicMockRule")); + Assert.assertEquals(in, out); } @Test - public void testFilter() throws Exception { + public void testFilter() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); rsfc.addFilterRuleRemoved("dummy", "basic", "DeletedRule"); rsfc.addFilterRuleRenamed("dummy", "basic", "OldNameOfBasicMockRule", "NewNameOfBasicMockRule"); - String in = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" - + " \n" - + " \n" + "\n"; - InputStream stream = new ByteArrayInputStream(in.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + Assert.assertEquals("rulesets/dummy/basic.xml/DummyBasicMockRule", + rsfc.applyRef("rulesets/dummy/notexisting.xml/DummyBasicMockRule")); - Assert.assertFalse(out.contains("notexisting.xml")); - Assert.assertTrue(out.contains("")); + Assert.assertEquals("rulesets/dummy/basic.xml/NewNameOfBasicMockRule", + rsfc.applyRef("rulesets/dummy/basic.xml/OldNameOfBasicMockRule")); - Assert.assertFalse(out.contains("DeletedRule")); - - Assert.assertFalse(out.contains("OldNameOfBasicMockRule")); - Assert.assertTrue(out.contains("")); + Assert.assertNull(rsfc.applyRef("rulesets/dummy/basic.xml/DeletedRule")); } @Test - public void testExclusionFilter() throws Exception { + public void testExclusionFilter() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleRenamed("dummy", "basic", "AnotherOldNameOfBasicMockRule", "NewNameOfBasicMockRule"); - String in = "\n" + "\n" + "\n" - + " Test\n" + "\n" + " \n" - + " \n" + " \n" + "\n"; - InputStream stream = new ByteArrayInputStream(in.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String out = rsfc.applyExclude("rulesets/dummy/basic.xml", "AnotherOldNameOfBasicMockRule", false); - Assert.assertFalse(out.contains("OldNameOfBasicMockRule")); - Assert.assertTrue(out.contains("")); + Assert.assertEquals("NewNameOfBasicMockRule", out); } - @Test - public void testEncoding() { - RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); - String testString; - - testString = ""; - Assert.assertEquals("ISO-8859-1", rsfc.determineEncoding(testString.getBytes(StandardCharsets.ISO_8859_1))); - - testString = ""; - Assert.assertEquals("UTF-8", rsfc.determineEncoding(testString.getBytes(StandardCharsets.ISO_8859_1))); - } - - private RuleSet createRulesetFromString(final String ruleset, RuleSetFactory factory) - throws RuleSetNotFoundException { - return factory.createRuleSet(new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.UTF_8)); - } - }); - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java index 38f58273c5..64bb25fce4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java @@ -22,10 +22,10 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { public LocaleRule localeRule = LocaleRule.en(); @org.junit.Rule - public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetFactory.class.getName()); + public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetLoader.class.getName()); @Test - public void duplicatedRuleReferenceShouldWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceShouldWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReference.xml"); assertEquals(1, ruleset.getRules().size()); @@ -37,7 +37,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void duplicatedRuleReferenceWithOverrideShouldNotWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceWithOverrideShouldNotWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverride.xml"); assertEquals(2, ruleset.getRules().size()); @@ -49,7 +49,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void duplicatedRuleReferenceWithOverrideBeforeShouldNotWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceWithOverrideBeforeShouldNotWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverrideBefore.xml"); assertEquals(2, ruleset.getRules().size()); @@ -61,7 +61,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void multipleDuplicates() throws RuleSetNotFoundException { + public void multipleDuplicates() { RuleSet ruleset = loadRuleSet("multipleDuplicates.xml"); assertEquals(2, ruleset.getRules().size()); @@ -74,8 +74,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { assertTrue(logging.getLog().contains("The ruleset rulesets/dummy/basic.xml is referenced multiple times in \"Custom Rules\".")); } - private RuleSet loadRuleSet(String ruleSetFilename) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - return rsf.createRuleSet("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); + private RuleSet loadRuleSet(String ruleSetFilename) { + return new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); } } 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 80f153ed1b..0b0bab592b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,22 +4,23 @@ package net.sourceforge.pmd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -40,23 +41,20 @@ public class RuleSetFactoryTest { @org.junit.Rule public LocaleRule localeRule = LocaleRule.en(); + @org.junit.Rule + public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetLoader.class.getName()); + @Test - public void testRuleSetFileName() throws RuleSetNotFoundException { - RuleSet rs = loadRuleSet(EMPTY_RULESET); - assertNull("RuleSet file name not expected", rs.getFileName()); + public void testRuleSetFileName() { + RuleSet rs = new RuleSetLoader().loadFromString("dummyRuleset.xml", EMPTY_RULESET); + assertEquals("dummyRuleset.xml", rs.getFileName()); rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); assertEquals("wrong RuleSet file name", rs.getFileName(), "net/sourceforge/pmd/TestRuleset1.xml"); } @Test - public void testNoRuleSetFileName() throws RuleSetNotFoundException { - RuleSet rs = loadRuleSet(EMPTY_RULESET); - assertNull("RuleSet file name not expected", rs.getFileName()); - } - - @Test - public void testRefs() throws Exception { + public void testRefs() { RuleSet rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); assertNotNull(rs.getRuleByName("TestRuleRef")); } @@ -88,7 +86,7 @@ public class RuleSetFactoryTest { // assert that MockRule2 is only once added to the ruleset, so that it // really // overwrites the configuration inherited from TestRuleset1.xml - assertEquals(1, countRule(rs, "MockRule2")); + assertNotNull(rs.getRuleByName("MockRule2")); Rule mockRule1 = rs.getRuleByName("MockRule1"); assertNotNull(mockRule1); @@ -105,38 +103,27 @@ public class RuleSetFactoryTest { Rule ruleset4Rule1 = rs.getRuleByName("Ruleset4Rule1"); assertNotNull(ruleset4Rule1); assertEquals(5, ruleset4Rule1.getPriority().getPriority()); - assertEquals(1, countRule(rs, "Ruleset4Rule1")); + assertNotNull(rs.getRuleByName("Ruleset4Rule1")); // priority overridden for whole TestRuleset4 group Rule ruleset4Rule2 = rs.getRuleByName("Ruleset4Rule2"); assertNotNull(ruleset4Rule2); assertEquals(2, ruleset4Rule2.getPriority().getPriority()); } - private int countRule(RuleSet rs, String ruleName) { - int count = 0; - for (Rule r : rs.getRules()) { - if (ruleName.equals(r.getName())) { - count++; - } - } - return count; - } - - @Test(expected = RuleSetNotFoundException.class) - public void testRuleSetNotFound() throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - rsf.createRuleSet("fooooo"); + @Test + public void testRuleSetNotFound() { + assertThrows(RuleSetLoadException.class, () -> new RuleSetLoader().loadFromResource("fooooo")); } @Test - public void testCreateEmptyRuleSet() throws RuleSetNotFoundException { + public void testCreateEmptyRuleSet() { RuleSet rs = loadRuleSet(EMPTY_RULESET); assertEquals("test", rs.getName()); assertEquals(0, rs.size()); } @Test - public void testSingleRule() throws RuleSetNotFoundException { + public void testSingleRule() { RuleSet rs = loadRuleSet(SINGLE_RULE); assertEquals(1, rs.size()); Rule r = rs.getRules().iterator().next(); @@ -146,7 +133,7 @@ public class RuleSetFactoryTest { } @Test - public void testMultipleRules() throws RuleSetNotFoundException { + public void testMultipleRules() { RuleSet rs = loadRuleSet(MULTIPLE_RULES); assertEquals(2, rs.size()); Set expected = new HashSet<>(); @@ -158,58 +145,59 @@ public class RuleSetFactoryTest { } @Test - public void testSingleRuleWithPriority() throws RuleSetNotFoundException { + public void testSingleRuleWithPriority() { assertEquals(RulePriority.MEDIUM, loadFirstRule(PRIORITY).getPriority()); } @Test - @SuppressWarnings("unchecked") - public void testProps() throws RuleSetNotFoundException { + public void testProps() { Rule r = loadFirstRule(PROPERTIES); - assertEquals("bar", r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooString"))); - assertEquals(new Integer(3), r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooInt"))); - assertTrue(r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooBoolean"))); - assertEquals(3.0d, r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooDouble")), 0.05); + assertEquals("bar", r.getProperty(r.getPropertyDescriptor("fooString"))); + assertEquals(3, r.getProperty(r.getPropertyDescriptor("fooInt"))); + assertEquals(true, r.getProperty(r.getPropertyDescriptor("fooBoolean"))); + assertEquals(3.0d, (Double) r.getProperty(r.getPropertyDescriptor("fooDouble")), 0.05); assertNull(r.getPropertyDescriptor("BuggleFish")); assertNotSame(r.getDescription().indexOf("testdesc2"), -1); } @Test - public void testStringMultiPropertyDefaultDelimiter() throws Exception { - Rule r = loadFirstRule("\n\n Desc\n" - + " \n" - + " Please move your class to the right folder(rest \nfolder)\n" - + " 2\n \n \n "); - PropertyDescriptor prop = r.getPropertyDescriptor("packageRegEx"); - Object values = r.getProperty(prop); - assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); + public void testStringMultiPropertyDefaultDelimiter() { + Rule r = loadFirstRule( + "\n\n Desc\n" + + " \n" + + " Please move your class to the right folder(rest \nfolder)\n" + + " 2\n \n \n "); + Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx")); + + assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue); } @Test - public void testStringMultiPropertyDelimiter() throws Exception { + public void testStringMultiPropertyDelimiter() { Rule r = loadFirstRule("\n" + "\n " + " ruleset desc\n " + "\n" + + " instead.\" \n" + + "class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">\n" + " Please move your class to the right folder(rest \nfolder)\n" + " 2\n \n \n" + " " + ""); - PropertyDescriptor prop = r.getPropertyDescriptor("packageRegEx"); - Object values = r.getProperty(prop); - assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); + + Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx")); + assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue); } @Test - public void testRuleSetWithDeprecatedRule() throws Exception { + public void testRuleSetWithDeprecatedRule() { RuleSet rs = loadRuleSet("\n" + "\n" - + " ruleset desc\n" - + " " - + ""); + + " ruleset desc\n" + + " " + + ""); assertEquals(1, rs.getRules().size()); Rule rule = rs.getRuleByName("DummyBasicMockRule"); assertNotNull(rule); @@ -224,11 +212,11 @@ public class RuleSetFactoryTest { * rule reference should be ignored, so at the end, we only have the new rule name in the ruleset. * This is because the deprecated reference points to a rule in the same ruleset. * - * @throws Exception */ @Test - public void testRuleSetWithDeprecatedButRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetWithDeprecatedButRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + " " @@ -250,18 +238,19 @@ public class RuleSetFactoryTest { *

When loading this ruleset at a whole for generating the documentation, we should still * include the deprecated rule reference, so that we can create a nice documentation. * - * @throws Exception */ @Test - public void testRuleSetWithDeprecatedRenamedRuleForDoc() throws Exception { - RuleSetLoader parser = new RuleSetLoader().warnDeprecated(false).includeDeprecatedRuleReferences(true); - RuleSet rs = parser.loadFromResource(createRuleSetReferenceId( - "\n" + "\n" - + " ruleset desc\n" - + " " - + " " - + " d\n" + " 2\n" + " " - + "")); + public void testRuleSetWithDeprecatedRenamedRuleForDoc() { + RuleSetLoader loader = new RuleSetLoader().includeDeprecatedRuleReferences(true); + RuleSet rs = loader.loadFromString("", + "\n" + "\n" + + " ruleset desc\n" + + " " + + " " + + " d\n" + + " 2\n" + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("NewName")); assertNotNull(rs.getRuleByName("OldName")); @@ -272,8 +261,9 @@ public class RuleSetFactoryTest { * The user should get a deprecation warning. */ @Test - public void testRuleSetReferencesADeprecatedRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesADeprecatedRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(1, rs.getRules().size()); @@ -281,8 +271,8 @@ public class RuleSetFactoryTest { assertNotNull(rule); assertEquals(1, - StringUtils.countMatches(logging.getLog(), - "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule.")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule.")); } /** @@ -298,11 +288,11 @@ public class RuleSetFactoryTest { *

* In the end, we should get all non-deprecated rules of the referenced ruleset. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithADeprecatedRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithADeprecatedRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(2, rs.getRules().size()); @@ -326,13 +316,14 @@ public class RuleSetFactoryTest { *

* In the end, we should get all non-deprecated rules of the referenced ruleset. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithAExcludedDeprecatedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAExcludedDeprecatedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" - + " " + ""); + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); @@ -350,20 +341,21 @@ public class RuleSetFactoryTest { * since not all rules are deprecated in the referenced ruleset. * Since the rule to be excluded doesn't exist, there should be a warning about that. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithAExcludedNonExistingRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAExcludedNonExistingRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" - + " " + ""); + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); assertEquals(0, - StringUtils.countMatches(logging.getLog(), - "WARNING: Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule as it is scheduled for removal from PMD.")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule as it is scheduled for removal from PMD.")); assertEquals(1, StringUtils.countMatches(logging.getLog(), "WARNING: Unable to exclude rules [NonExistingRule] from ruleset reference rulesets/dummy/basic.xml; perhaps the rule name is misspelled or the rule doesn't exist anymore?")); @@ -374,8 +366,9 @@ public class RuleSetFactoryTest { * considered deprecated and the user should get a deprecation warning for the ruleset. */ @Test - public void testRuleSetReferencesDeprecatedRuleset() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesDeprecatedRuleset() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(2, rs.getRules().size()); @@ -383,8 +376,8 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("SampleXPathRule")); assertEquals(1, - StringUtils.countMatches(logging.getLog(), - "WARNING: The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD")); + StringUtils.countMatches(logging.getLog(), + "WARNING: The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD")); } /** @@ -393,21 +386,22 @@ public class RuleSetFactoryTest { * no warning about deprecation - since the deprecated rules are not used. */ @Test - public void testRuleSetReferencesRulesetWithAMovedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAMovedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(1, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasic2MockRule")); assertEquals(0, - StringUtils.countMatches(logging.getLog(), - "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule. PMD")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule. PMD")); } @Test @SuppressWarnings("unchecked") - public void testXPath() throws RuleSetNotFoundException { + public void testXPath() { Rule r = loadFirstRule(XPATH); PropertyDescriptor xpathProperty = (PropertyDescriptor) r.getPropertyDescriptor("xpath"); assertNotNull("xpath property descriptor", xpathProperty); @@ -415,7 +409,7 @@ public class RuleSetFactoryTest { } @Test - public void testExternalReferenceOverride() throws RuleSetNotFoundException { + public void testExternalReferenceOverride() { Rule r = loadFirstRule(REF_OVERRIDE); assertEquals("TestNameOverride", r.getName()); assertEquals("Test message override", r.getMessage()); @@ -432,14 +426,18 @@ public class RuleSetFactoryTest { } @Test - public void testExternalReferenceOverrideNonExistent() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage("Cannot set non-existent property 'test4' on Rule TestNameOverride"); - loadFirstRule(REF_OVERRIDE_NONEXISTENT); + public void testExternalReferenceOverrideNonExistent() { + RuleSetLoadException ex = assertCannotParse(REF_OVERRIDE_NONEXISTENT); + + assertThat(ex.getCause().getMessage(), containsString("Cannot set non-existent property 'test4' on Rule TestNameOverride")); + } + + private RuleSetLoadException assertCannotParse(String xmlContent) { + return assertThrows(RuleSetLoadException.class, () -> loadFirstRule(xmlContent)); } @Test - public void testReferenceInternalToInternal() throws RuleSetNotFoundException { + public void testReferenceInternalToInternal() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL); Rule rule = ruleSet.getRuleByName("MockRuleName"); @@ -450,7 +448,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToInternalChain() throws RuleSetNotFoundException { + public void testReferenceInternalToInternalChain() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL_CHAIN); Rule rule = ruleSet.getRuleByName("MockRuleName"); @@ -464,7 +462,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToExternal() throws RuleSetNotFoundException { + public void testReferenceInternalToExternal() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL); Rule rule = ruleSet.getRuleByName("ExternalRefRuleName"); @@ -475,7 +473,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToExternalChain() throws RuleSetNotFoundException { + public void testReferenceInternalToExternalChain() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL_CHAIN); Rule rule = ruleSet.getRuleByName("ExternalRefRuleName"); @@ -489,51 +487,50 @@ public class RuleSetFactoryTest { } @Test - public void testReferencePriority() throws RuleSetNotFoundException { - RuleSetLoader parser = new RuleSetLoader().warnDeprecated(false).enableCompatibility(true); + public void testReferencePriority() { + RuleSetLoader config = new RuleSetLoader().warnDeprecated(false).enableCompatibility(true); - RuleSet ruleSet; - { - final RuleSetReferenceId refInternalInternal = createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN); + RuleSetLoader rsf = config.filterAbovePriority(RulePriority.LOW); + RuleSet ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_INTERNAL_CHAIN); + assertEquals("Number of Rules", 3, ruleSet.getRules().size()); + assertNotNull(ruleSet.getRuleByName("MockRuleName")); + assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); + assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - ruleSet = parser.filterAbovePriority(RulePriority.LOW).loadFromResource(refInternalInternal); - assertEquals("Number of Rules", 3, ruleSet.getRules().size()); - assertNotNull(ruleSet.getRuleByName("MockRuleName")); - assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); - assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_INTERNAL_CHAIN); + assertEquals("Number of Rules", 2, ruleSet.getRules().size()); + assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); + assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - ruleSet = parser.filterAbovePriority(RulePriority.MEDIUM_HIGH).loadFromResource(refInternalInternal); - assertEquals("Number of Rules", 2, ruleSet.getRules().size()); - assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); - assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); + rsf = config.filterAbovePriority(RulePriority.HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_INTERNAL_CHAIN); + assertEquals("Number of Rules", 1, ruleSet.getRules().size()); + assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - ruleSet = parser.filterAbovePriority(RulePriority.HIGH).loadFromResource(refInternalInternal); - assertEquals("Number of Rules", 1, ruleSet.getRules().size()); - assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - } - - final RuleSetReferenceId refInternalToExternal = createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN); - - ruleSet = parser.filterAbovePriority(RulePriority.LOW).loadFromResource(refInternalToExternal); + rsf = config.filterAbovePriority(RulePriority.LOW); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_EXTERNAL_CHAIN); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleName")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - ruleSet = parser.filterAbovePriority(RulePriority.MEDIUM_HIGH).loadFromResource(refInternalToExternal); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_EXTERNAL_CHAIN); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - ruleSet = parser.filterAbovePriority(RulePriority.HIGH).loadFromResource(refInternalToExternal); + rsf = config.filterAbovePriority(RulePriority.HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_EXTERNAL_CHAIN); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); } @Test - public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetLoader parser = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true); - RuleSet ruleset = parser.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); + public void testOverridePriorityLoadWithMinimum() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true); + RuleSet ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -544,23 +541,23 @@ public class RuleSetFactoryTest { assertNotNull(ruleset.getRuleByName("SampleXPathRule")); // now, load with default minimum priority - parser = new RuleSetLoader(); - ruleset = parser.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); + rsf = new RuleSetLoader(); + ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); assertEquals("Number of Rules", 2, ruleset.getRules().size()); Rule dummyBasicMockRule = ruleset.getRuleByName("DummyBasicMockRule"); assertEquals("Wrong Priority", RulePriority.LOW, dummyBasicMockRule.getPriority()); } @Test - public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetLoader parser = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH); - RuleSet ruleset = parser.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); + public void testExcludeWithMinimumPriority() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH); + RuleSet ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - parser.filterAbovePriority(RulePriority.LOW).toFactory(); - ruleset = parser.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.LOW); + ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); // rule is excluded @@ -570,100 +567,101 @@ public class RuleSetFactoryTest { } @Test - public void testOverrideMessage() throws RuleSetNotFoundException { + public void testOverrideMessage() { Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME); assertEquals("TestMessageOverride", r.getMessage()); } @Test - public void testOverrideMessageOneElem() throws RuleSetNotFoundException { + public void testOverrideMessageOneElem() { Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM); assertEquals("TestMessageOverride", r.getMessage()); } - @Test(expected = IllegalArgumentException.class) - public void testIncorrectExternalRef() throws IllegalArgumentException, RuleSetNotFoundException { - loadFirstRule(REF_MISSPELLED_XREF); + @Test + public void testIncorrectExternalRef() { + assertCannotParse(REF_MISSPELLED_XREF); } @Test - public void testSetPriority() throws RuleSetNotFoundException { - RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH); - assertEquals(0, rsf.loadFromResource(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf.filterAbovePriority(RulePriority.MEDIUM_LOW); - assertEquals(1, rsf.loadFromResource(createRuleSetReferenceId(SINGLE_RULE)).size()); + public void testSetPriority() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false); + assertEquals(0, rsf.loadFromString("", SINGLE_RULE).size()); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false); + assertEquals(1, rsf.loadFromString("", SINGLE_RULE).size()); } @Test - public void testLanguage() throws RuleSetNotFoundException { + public void testLanguage() { Rule r = loadFirstRule(LANGUAGE); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME), r.getLanguage()); } - @Test(expected = IllegalArgumentException.class) - public void testIncorrectLanguage() throws RuleSetNotFoundException { - loadFirstRule(INCORRECT_LANGUAGE); + @Test + public void testIncorrectLanguage() { + assertCannotParse(INCORRECT_LANGUAGE); } @Test - public void testMinimumLanguageVersion() throws RuleSetNotFoundException { + public void testMinimumLanguageVersion() { Rule r = loadFirstRule(MINIMUM_LANGUAGE_VERSION); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.4"), - r.getMinimumLanguageVersion()); + r.getMinimumLanguageVersion()); } @Test - public void testIncorrectMinimumLanguageVersion() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MINIMUM_LANGUAGE_VERSION); - } + public void testIncorrectMinimumLanguageVersion() { + RuleSetLoadException ex = assertCannotParse(INCORRECT_MINIMUM_LANGUAGE_VERSION); + + assertThat(ex.getCause().getMessage(), containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - @Test(expected = IllegalArgumentException.class) - public void testIncorrectMinimumLanugageVersionWithLanguageSetInJava() throws RuleSetNotFoundException { - loadFirstRule("\n" - + "\n" - + " TODO\n" - + "\n" - + " \n" - + " TODO\n" - + " 2\n" - + " \n" - + "\n" - + ""); } @Test - public void testMaximumLanguageVersion() throws RuleSetNotFoundException { + public void testIncorrectMinimumLanguageVersionWithLanguageSetInJava() { + assertCannotParse("\n" + + "\n" + + " TODO\n" + + "\n" + + " \n" + + " TODO\n" + + " 2\n" + + " \n" + + "\n" + + ""); + } + + @Test + public void testMaximumLanguageVersion() { Rule r = loadFirstRule(MAXIMUM_LANGUAGE_VERSION); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), - r.getMaximumLanguageVersion()); + r.getMaximumLanguageVersion()); } @Test - public void testIncorrectMaximumLanguageVersion() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MAXIMUM_LANGUAGE_VERSION); - } + public void testIncorrectMaximumLanguageVersion() { + RuleSetLoadException ex = assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); - @Test(expected = IllegalArgumentException.class) - public void testInvertedMinimumMaximumLanguageVersions() throws RuleSetNotFoundException { - loadFirstRule(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); + assertThat(ex.getCause().getMessage(), containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." } @Test - public void testDirectDeprecatedRule() throws RuleSetNotFoundException { + public void testInvertedMinimumMaximumLanguageVersions() { + assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + } + + @Test + public void testDirectDeprecatedRule() { Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE); assertNotNull("Direct Deprecated Rule", r); assertTrue(r.isDeprecated()); } @Test - public void testReferenceToDeprecatedRule() throws RuleSetNotFoundException { + public void testReferenceToDeprecatedRule() { Rule r = loadFirstRule(REFERENCE_TO_DEPRECATED_RULE); assertNotNull("Reference to Deprecated Rule", r); assertTrue("Rule Reference", r instanceof RuleReference); @@ -673,7 +671,7 @@ public class RuleSetFactoryTest { } @Test - public void testRuleSetReferenceWithDeprecatedRule() throws RuleSetNotFoundException { + public void testRuleSetReferenceWithDeprecatedRule() { RuleSet ruleSet = loadRuleSet(REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE); assertNotNull("RuleSet", ruleSet); assertFalse("RuleSet empty", ruleSet.getRules().isEmpty()); @@ -687,90 +685,77 @@ public class RuleSetFactoryTest { } @Test - public void testDeprecatedRuleSetReference() throws RuleSetNotFoundException { - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleSet = ruleSetFactory.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-deprecated.xml"); + public void testDeprecatedRuleSetReference() { + RuleSet ruleSet = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/ruleset-deprecated.xml"); assertEquals(2, ruleSet.getRules().size()); } @Test - public void testExternalReferences() throws RuleSetNotFoundException { + public void testExternalReferences() { RuleSet rs = loadRuleSet(EXTERNAL_REFERENCE_RULE_SET); assertEquals(1, rs.size()); assertEquals(MockRule.class.getName(), rs.getRuleByName("MockRule").getRuleClass()); } @Test - public void testIncludeExcludePatterns() throws RuleSetNotFoundException { + public void testIncludeExcludePatterns() { RuleSet ruleSet = loadRuleSet(INCLUDE_EXCLUDE_RULESET); - assertNotNull("Include patterns", ruleSet.getIncludePatterns()); - assertEquals("Include patterns size", 2, ruleSet.getIncludePatterns().size()); - assertEquals("Include pattern #1", "include1", ruleSet.getIncludePatterns().get(0)); - assertEquals("Include pattern #2", "include2", ruleSet.getIncludePatterns().get(1)); + assertNotNull("Include patterns", ruleSet.getFileInclusions()); + assertEquals("Include patterns size", 2, ruleSet.getFileInclusions().size()); + assertEquals("Include pattern #1", "include1", ruleSet.getFileInclusions().get(0).pattern()); + assertEquals("Include pattern #2", "include2", ruleSet.getFileInclusions().get(1).pattern()); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); - assertEquals("Exclude patterns size", 3, ruleSet.getExcludePatterns().size()); - assertEquals("Exclude pattern #1", "exclude1", ruleSet.getExcludePatterns().get(0)); - assertEquals("Exclude pattern #2", "exclude2", ruleSet.getExcludePatterns().get(1)); - assertEquals("Exclude pattern #3", "exclude3", ruleSet.getExcludePatterns().get(2)); + assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); + assertEquals("Exclude patterns size", 3, ruleSet.getFileExclusions().size()); + assertEquals("Exclude pattern #1", "exclude1", ruleSet.getFileExclusions().get(0).pattern()); + assertEquals("Exclude pattern #2", "exclude2", ruleSet.getFileExclusions().get(1).pattern()); + assertEquals("Exclude pattern #3", "exclude3", ruleSet.getFileExclusions().get(2).pattern()); } /** * Rule reference can't be resolved - ref is used instead of class and the * class is old (pmd 4.3 and not pmd 5). - * - * @throws Exception - * any error */ - @Test(expected = RuleSetNotFoundException.class) - public void testBug1202() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" + @Test + public void testBug1202() { + Assert.assertThrows( + RuleSetLoadException.class, + () -> new RuleSetLoader().loadFromString("", "\n" + "\n" + " \n" + " 1\n" + " \n" + " \n" + " \n" + " \n" + " \n" - + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - ruleSetFactory.createRuleSet(ref); + + "\n") + ); } /** * See https://sourceforge.net/p/pmd/bugs/1225/ - * - * @throws Exception - * any error */ @Test - public void testEmptyRuleSetFile() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" - + "\n" - + " PMD Ruleset.\n" + "\n" - + " .*Test.*\n" + "\n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); + public void testEmptyRuleSetFile() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "\n" + + "\n" + + " PMD Ruleset.\n" + "\n" + + " .*Test.*\n" + "\n" + "\n"); assertEquals(0, ruleset.getRules().size()); } /** * See https://github.com/pmd/pmd/issues/782 * Empty ruleset should be interpreted as deprecated. - * - * @throws Exception - * any error */ @Test - public void testEmptyRuleSetReferencedShouldNotBeDeprecated() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" - + "\n" - + " Ruleset which references a empty ruleset\n" + "\n" - + " \n" - + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetLoader().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); + public void testEmptyRuleSetReferencedShouldNotBeDeprecated() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "\n" + + "\n" + + " Ruleset which references a empty ruleset\n" + "\n" + + " \n" + + "\n"); assertEquals(0, ruleset.getRules().size()); assertTrue(logging.getLog().isEmpty()); @@ -778,41 +763,36 @@ public class RuleSetFactoryTest { /** * See https://sourceforge.net/p/pmd/bugs/1231/ - * - * @throws Exception - * any error */ - @Test(expected = IllegalArgumentException.class) - public void testWrongRuleNameReferenced() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" - + "\n" - + " Custom ruleset for tests\n" - + " \n" + "\n"); - new RuleSetLoader().loadFromResource(ref); + @Test + public void testWrongRuleNameReferenced() { + assertCannotParse("\n" + + "\n" + + " Custom ruleset for tests\n" + + " \n" + + "\n"); } /** * Unit test for #1312 see https://sourceforge.net/p/pmd/bugs/1312/ * - * @throws Exception - * any error */ @Test - public void testRuleReferenceWithNameOverridden() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" - + "\n" - + " PMD Plugin preferences rule set\n" + "\n" - + "\n" + "\n" + "\n" + ""); - RuleSet rs = new RuleSetLoader().loadFromResource(ref); + public void testRuleReferenceWithNameOverridden() { + RuleSet rs = loadRuleSet("\n" + + "\n" + + " PMD Plugin preferences rule set\n" + + "\n" + "\n" + "\n" + + ""); - Rule r = rs.getRules().toArray(new Rule[1])[0]; + Rule r = rs.getRules().iterator().next(); assertEquals("OverriddenDummyBasicMockRule", r.getName()); RuleReference ruleRef = (RuleReference) r; assertEquals("DummyBasicMockRule", ruleRef.getRule().getName()); @@ -823,20 +803,17 @@ public class RuleSetFactoryTest { * *

See https://github.com/pmd/pmd/issues/1978 - with that, it should not be an error anymore. * - * @throws Exception - * any error */ @Test - public void testWrongRuleNameExcluded() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" - + " \n" + " \n" + "\n"); - RuleSet ruleset = new RuleSetLoader().loadFromResource(ref); + public void testWrongRuleNameExcluded() { + RuleSet ruleset = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + " \n" + + "\n"); assertEquals(4, ruleset.getRules().size()); } @@ -847,8 +824,6 @@ public class RuleSetFactoryTest { * Currently, if a ruleset is imported twice, the excludes of the first * import are ignored. Duplicated rules are silently ignored. * - * @throws Exception - * any error * @see #1537 Implement * strict ruleset parsing * @see */ @Test - public void testExcludeAndImportTwice() throws Exception { - RuleSetReferenceId ref1 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + "\n"); - RuleSet ruleset = new RuleSetLoader().loadFromResource(ref1); + public void testExcludeAndImportTwice() { + RuleSet ruleset = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + "\n"); assertNull(ruleset.getRuleByName("DummyBasicMockRule")); - RuleSetReferenceId ref2 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + " \n" + "\n"); - RuleSet ruleset2 = new RuleSetLoader().loadFromResource(ref2); + RuleSet ruleset2 = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + " \n" + + "\n"); assertNotNull(ruleset2.getRuleByName("DummyBasicMockRule")); - RuleSetReferenceId ref3 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + " \n" + "\n"); - RuleSet ruleset3 = new RuleSetLoader().loadFromResource(ref3); + RuleSet ruleset3 = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + " \n" + + "\n"); assertNotNull(ruleset3.getRuleByName("DummyBasicMockRule")); } - @org.junit.Rule - public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetFactory.class.getName()); - @Test - public void testMissingRuleSetNameIsWarning() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" - + " \n"); - new RuleSetLoader().loadFromResource(ref); + public void testMissingRuleSetNameIsWarning() { + loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + ); assertTrue(logging.getLog().contains("RuleSet name is missing.")); } @Test - public void testMissingRuleSetDescriptionIsWarning() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " \n" - + " \n"); - new RuleSetLoader().loadFromResource(ref); + public void testMissingRuleSetDescriptionIsWarning() { + loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + + " \n" + + " \n" + ); assertTrue(logging.getLog().contains("RuleSet description is missing.")); } @@ -1255,27 +1226,17 @@ public class RuleSetFactoryTest { + "\n" + ""; - private Rule loadFirstRule(String ruleSetXml) throws RuleSetNotFoundException { + private Rule loadFirstRule(String ruleSetXml) { RuleSet rs = loadRuleSet(ruleSetXml); return rs.getRules().iterator().next(); } - private RuleSet loadRuleSet(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); + private RuleSet loadRuleSet(String ruleSetXml) { + return new RuleSetLoader().loadFromString("dummyRuleset.xml", ruleSetXml); } - private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).toFactory(); - return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); + private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) { + return new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).loadFromString("testRuleset.xml", ruleSetXml); } - private static RuleSetReferenceId createRuleSetReferenceId(final String ruleSetXml) { - return new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleSetXml.getBytes(StandardCharsets.UTF_8)); - } - }; - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java index aeb18f1369..a07b476176 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java @@ -106,7 +106,7 @@ public class RuleSetSchemaTest { } public static class PMDRuleSetEntityResolver implements EntityResolver { - private static URL schema2 = RuleSetFactory.class.getResource("/ruleset_2_0_0.xsd"); + private static URL schema2 = PMDRuleSetEntityResolver.class.getResource("/ruleset_2_0_0.xsd"); private static SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); @Override 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 4b8625f121..8c0d67ec0a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Random; 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; @@ -271,8 +272,8 @@ public class RuleSetTest { createRuleSetBuilder("ruleset1") .withFileExclusions(Pattern.compile(".*")) .build(); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); - assertEquals("Invalid number of patterns", 1, ruleSet.getExcludePatterns().size()); + assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); + assertEquals("Invalid number of patterns", 1, ruleSet.getFileExclusions().size()); } @Test @@ -282,24 +283,28 @@ public class RuleSetTest { .withFileExclusions(Pattern.compile(".*")) .withFileExclusions(Pattern.compile(".*ha")) .build(); - assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha"), ruleSet2.getExcludePatterns()); + assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha"), toStrings(ruleSet2.getFileExclusions())); } @Test public void testIncludePatternsAreOrdered() { RuleSet ruleSet2 = createRuleSetBuilder("ruleset2") - .withFileInclusions(Pattern.compile(".*")) - .withFileInclusions(Arrays.asList(Pattern.compile(".*ha"), Pattern.compile(".*hb"))) - .build(); - assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha", ".*hb"), ruleSet2.getIncludePatterns()); + .withFileInclusions(Pattern.compile(".*")) + .withFileInclusions(Arrays.asList(Pattern.compile(".*ha"), Pattern.compile(".*hb"))) + .build(); + assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha", ".*hb"), toStrings(ruleSet2.getFileInclusions())); + } + + private List toStrings(List strings) { + return strings.stream().map(Pattern::pattern).collect(Collectors.toList()); } @Test public void testAddExcludePatterns() { RuleSet ruleSet = createRuleSetBuilder("ruleset1") - .withFileExclusions(Pattern.compile(".*")) - .build(); + .withFileExclusions(Pattern.compile(".*")) + .build(); assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); assertEquals("Invalid number of patterns", 1, ruleSet.getFileExclusions().size()); @@ -317,7 +322,6 @@ public class RuleSetTest { excludePatterns.add(Pattern.compile("ah*")); excludePatterns.add(Pattern.compile(".*")); RuleSet ruleSet = createRuleSetBuilder("ruleset").replaceFileExclusions(excludePatterns).build(); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); assertEquals("Invalid number of exclude patterns", 2, ruleSet.getFileExclusions().size()); assertEquals("Exclude pattern", "ah*", ruleSet.getFileExclusions().get(0).pattern()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java index 6f6e1643dc..63bc53a909 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java @@ -51,7 +51,7 @@ public class RuleSetWriterTest { */ @Test public void testWrite() throws Exception { - RuleSet braces = RulesetsFactoryUtils.defaultFactory().createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); + RuleSet braces = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); RuleSet ruleSet = new RuleSetBuilder(new Random().nextLong()) .withName("ruleset") .withDescription("ruleset description") @@ -72,13 +72,11 @@ public class RuleSetWriterTest { */ @Test public void testRuleReferenceOverriddenName() throws Exception { - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet rs = ruleSetFactory.createRuleSet("dummy-basic"); - RuleSetReference ruleSetReference = new RuleSetReference("rulesets/dummy/basic.xml"); + RuleSet rs = new RuleSetLoader().loadFromResource("rulesets/dummy/basic.xml"); RuleReference ruleRef = new RuleReference(); ruleRef.setRule(rs.getRuleByName("DummyBasicMockRule")); - ruleRef.setRuleSetReference(ruleSetReference); + ruleRef.setRuleSetReference(new RuleSetReference("rulesets/dummy/basic.xml")); ruleRef.setName("Foo"); // override the name RuleSet ruleSet = RuleSet.forSingleRule(ruleRef); 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 2fc62d1892..cfeb29973d 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 @@ -77,7 +77,7 @@ public class PMDTaskTest { String actual = IOUtils.toString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.replaceAll("\n|\r", ""); - Assert.assertEquals("sample.dummy:1:\tTest Rule 2", actual); + Assert.assertEquals("sample.dummy:1:\tSampleXPathRule:\tTest Rule 2", actual); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java index 938b67f8d5..f6e2547601 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java @@ -15,7 +15,6 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -142,9 +141,9 @@ public class StageDependencyTest { private static RuleSets withRules(Rule r, Rule... rs) { List rsets = new ArrayList<>(); - rsets.add(new RuleSetFactory().createSingleRuleRuleSet(r)); + rsets.add(RuleSet.forSingleRule(r)); for (Rule rule : rs) { - rsets.add(new RuleSetFactory().createSingleRuleRuleSet(rule)); + rsets.add(RuleSet.forSingleRule(rule)); } return new RuleSets(rsets); 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 7b532c5e25..46bd2c60c7 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 @@ -50,7 +50,7 @@ public class DummyLanguageModule extends BaseLanguageModule { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { DummyRoot node = new DummyRoot(); node.setCoords(1, 1, 2, 10); @@ -65,7 +65,7 @@ public class DummyLanguageModule extends BaseLanguageModule { public static class HandlerWithParserThatThrows extends Handler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { throw new AssertionError("test error while parsing"); }; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java index 525b23c0f0..ca9f6d8811 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java @@ -7,8 +7,6 @@ package net.sourceforge.pmd.lang.rule.xpath.impl; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; @@ -22,6 +20,7 @@ import org.junit.Test; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.xpath.Attribute; +import net.sourceforge.pmd.util.CollectionUtil; /** @@ -29,6 +28,8 @@ import net.sourceforge.pmd.lang.rule.xpath.Attribute; */ public class AttributeAxisIteratorTest { + private static final Set DEFAULT_ATTRS = setOf("BeginColumn", "BeginLine", "Image", "EndColumn", "EndLine"); + /** * Test hasNext and next. */ @@ -38,14 +39,8 @@ public class AttributeAxisIteratorTest { dummyNode.setCoords(1, 1, 2, 2); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - Set expected = setOf("BeginColumn", - "BeginLine", - "FindBoundary", - "Image", - "EndColumn", - "EndLine"); - assertEquals(expected, atts.keySet()); + + assertEquals(DEFAULT_ATTRS, toMap(it).keySet()); } @Test @@ -53,21 +48,20 @@ public class AttributeAxisIteratorTest { DummyNodeWithEnum dummyNode = new DummyNodeWithEnum(); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - assertEquals(7, atts.size()); - assertTrue(atts.containsKey("Enum")); - assertEquals(DummyNodeWithEnum.MyEnum.FOO, atts.get("Enum").getValue()); + + Set expected = CollectionUtil.setUnion(DEFAULT_ATTRS, "Enum"); + + assertEquals(expected, toMap(it).keySet()); } @Test public void testAttributeAxisIteratorWithList() { + // list attributes are not supported anymore DummyNodeWithList dummyNode = new DummyNodeWithList(); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - assertEquals(6, atts.size()); - assertFalse(atts.containsKey("List")); - assertFalse(atts.containsKey("NodeList")); + + assertEquals(DEFAULT_ATTRS, toMap(it).keySet()); } private Map toMap(AttributeAxisIterator it) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java index dde363e8e1..3a14914401 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java @@ -25,7 +25,6 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.cache.AnalysisCache; import net.sourceforge.pmd.cache.NoopAnalysisCache; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -38,10 +37,6 @@ import net.sourceforge.pmd.util.document.TextFile; public class GlobalListenerTest { - static RuleSet mockRuleset(Rule rule) { - return RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); - } - private final LanguageVersion dummyVersion = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); static final int NUM_DATA_SOURCES = 3; @@ -153,7 +148,7 @@ public class GlobalListenerTest { try { PMD.processTextFiles( config, - listOf(mockRuleset(rule)), + listOf(RuleSet.forSingleRule(rule)), mockDataSources(), listener ); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java index 01510c9032..7d829f88df 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java @@ -15,10 +15,9 @@ import org.junit.Test; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; @@ -35,7 +34,7 @@ public class MultiThreadProcessorTest { private SimpleReportListener reportListener; private PMDConfiguration configuration; - public RuleSets setUpForTest(final String ruleset) throws RuleSetNotFoundException { + public RuleSets setUpForTest(final String ruleset) { configuration = new PMDConfiguration(); configuration.setThreads(2); LanguageVersion lv = LanguageRegistry.getDefaultLanguage().getDefaultVersion(); @@ -50,7 +49,7 @@ public class MultiThreadProcessorTest { reportListener )); - return new RuleSets(RulesetsFactoryUtils.defaultFactory().createRuleSets(ruleset)); + return new RuleSets(new RuleSetLoader().loadFromResource(ruleset)); } // Dysfunctional rules are pruned upstream of the processor. diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java index dcb68112b9..b97d81e0e2 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java @@ -15,18 +15,18 @@ import org.junit.runners.Suite.SuiteClasses; */ @RunWith(Suite.class) @SuiteClasses({ - CodeClimateRendererTest.class, - CSVRendererTest.class, - EmacsRendererTest.class, - HTMLRendererTest.class, - IDEAJRendererTest.class, - PapariTextRendererTest.class, - SummaryHTMLRendererTest.class, - TextPadRendererTest.class, - VBHTMLRendererTest.class, - XMLRendererTest.class, - XSLTRendererTest.class, - YAHTMLRendererTest.class + CodeClimateRendererTest.class, + CSVRendererTest.class, + EmacsRendererTest.class, + HTMLRendererTest.class, + IDEAJRendererTest.class, + PapariTextRendererTest.class, + SummaryHTMLRendererTest.class, + TextPadRendererTest.class, + VBHTMLRendererTest.class, + XMLRendererTest.class, + XSLTRendererTest.class, + YAHTMLRendererTest.class }) public class RenderersTests { } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java index e5118ab038..69983b5f85 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java @@ -17,7 +17,7 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpected() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override @@ -27,8 +27,8 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpectedMultiple() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL - + getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL + + getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override 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 23bb4a677b..7d4e10c297 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 @@ -116,6 +116,12 @@ public class XMLRendererTest extends AbstractRendererTest { verifyXmlEscaping(renderer, "\ud801\udc1c", StandardCharsets.UTF_8); } + @Test + public void testXMLEscapingWithUTF16() throws Exception { + Renderer renderer = getRenderer(); + verifyXmlEscaping(renderer, "𐐜", StandardCharsets.UTF_16); + } + @Test public void testXMLEscapingWithoutUTF8() throws Exception { Renderer renderer = getRenderer(); diff --git a/pmd-cpp/etc/grammar/Cpp.jj b/pmd-cpp/etc/grammar/Cpp.jj index 450f772c41..95640555eb 100644 --- a/pmd-cpp/etc/grammar/Cpp.jj +++ b/pmd-cpp/etc/grammar/Cpp.jj @@ -193,12 +193,14 @@ TOKEN : TOKEN: { - < #DECIMALDIGIT: ["0"-"9"] > + < #BINARYDIGIT: ["0"-"1"] > | < #OCTALDIGIT: ["0"-"7"] > +| < #DECIMALDIGIT: ["0"-"9"] > | < #HEXDIGIT: ["a"-"f", "A"-"F", "0"-"9"] > | < #INT_SUFFIX: ["u", "U", "l", "L"] | "uL" | "Ul" | "UL" | "ul" | "lu" | "Lu" | "lU" | "LU" > | < ZERO: "0" > +| < BINARY_INT_LITERAL: "0" ["b", "B"] ("'" | )+ ()? > | < OCTAL_INT_LITERAL: "0" ("'" | )+ ()? > | < DECIMAL_INT_LITERAL: ["1"-"9"] ("'" | )* ()? > | < HEXADECIMAL_INT_LITERAL: "0" ["x", "X"] ("'" | )+ ()? > diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp index 9f0d38b06f..cbae7336ba 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp @@ -37,4 +37,7 @@ auto hex_literal = 0x0F00'abcd'6f3d; auto silly_example = 1'0'0'000'00; + // boolean literals + int b1 = 0B001101; // C++ 14 binary literal + int b2 = 0b000001; // C++ 14 binary literal } \ No newline at end of file diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt index 3a0185425f..79afe0b26c 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt @@ -118,6 +118,18 @@ L38 [=] 24 25 [1'0'0'000'00] 26 38 [;] 38 39 -L40 +L41 + [int] 5 8 + [b1] 9 11 + [=] 12 13 + [0B001101] 14 22 + [;] 22 23 +L42 + [int] 5 8 + [b2] 9 11 + [=] 12 13 + [0b000001] 14 22 + [;] 22 23 +L43 [}] 1 2 EOF 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 5e5cd433ec..86b094f58a 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 @@ -21,17 +21,21 @@ import net.sourceforge.pmd.lang.cs.ast.CSharpLexer; public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; + private boolean ignoreLiteralSequences = false; public void setProperties(Properties properties) { - if (properties.containsKey(IGNORE_USINGS)) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); - } + ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); + ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, "false")); } public void setIgnoreUsings(boolean ignoreUsings) { this.ignoreUsings = ignoreUsings; } + public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + @Override protected Lexer getLexerForSource(final CharStream charStream) { return new CSharpLexer(charStream); @@ -39,7 +43,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new CsTokenFilter(tokenManager, ignoreUsings); + return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences); } /** @@ -57,13 +61,16 @@ public class CsTokenizer extends AntlrTokenizer { } private final boolean ignoreUsings; + private final boolean ignoreLiteralSequences; private boolean discardingUsings = false; private boolean discardingNL = false; + private boolean discardingLiterals = false; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings) { + CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { super(tokenManager); this.ignoreUsings = ignoreUsings; + this.ignoreLiteralSequences = ignoreLiteralSequences; } @Override @@ -75,6 +82,7 @@ public class CsTokenizer extends AntlrTokenizer { protected void analyzeTokens(final AntlrToken currentToken, final Iterable remainingTokens) { discardCurrent = false; skipUsingDirectives(currentToken, remainingTokens); + skipLiteralSequences(currentToken, remainingTokens); } private void skipUsingDirectives(final AntlrToken currentToken, final Iterable remainingTokens) { @@ -151,9 +159,44 @@ public class CsTokenizer extends AntlrTokenizer { discardingNL = currentToken.getKind() == CSharpLexer.NL; } + private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { + if (ignoreLiteralSequences) { + final int type = currentToken.getKind(); + if (type == CSharpLexer.OPEN_BRACE && isSequenceOfLiterals(remainingTokens)) { + discardingLiterals = true; + } else if (type == CSharpLexer.CLOSE_BRACE && discardingLiterals) { + discardingLiterals = false; + discardCurrent = true; + } + } + } + + private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + boolean seenLiteral = false; + for (final AntlrToken token : remainingTokens) { + switch (token.getKind()) { + case CSharpLexer.CHARACTER_LITERAL: + case CSharpLexer.HEX_INTEGER_LITERAL: + case CSharpLexer.INTEGER_LITERAL: + case CSharpLexer.REAL_LITERAL: + seenLiteral = true; + break; // can be skipped; continue to the next token + case CSharpLexer.COMMA: + break; // can be skipped; continue to the next token + case CSharpLexer.CLOSE_BRACE: + // end of the list; skip all contents + return seenLiteral; + default: + // some other token than the expected ones; this is not a sequence of literals + return false; + } + } + return false; + } + @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || discardCurrent; + return discardingUsings || discardingNL || discardingLiterals || 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 6bc536a4c9..6b61c658bb 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 @@ -90,13 +90,28 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("tabWidth"); } - private Properties ignoreUsings() { - return properties(true); + @Test + public void testLongListsOfNumbersAreNotIgnored() { + doTest("listOfNumbers"); } - private Properties properties(boolean ignoreUsings) { + @Test + public void testLongListsOfNumbersAreIgnored() { + doTest("listOfNumbers", "_ignored", skipLiteralSequences()); + } + + private Properties ignoreUsings() { + return properties(true, false); + } + + private Properties skipLiteralSequences() { + return properties(false, true); + } + + private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) { Properties properties = new Properties(); properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); + properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); return properties; } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs new file mode 100644 index 0000000000..a3ceee1dca --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections; +using System.Collections.Generic; +public class LongLists { + List l = new List { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + }; +} diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt new file mode 100644 index 0000000000..f5024fc29c --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt @@ -0,0 +1,344 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L2 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [;] 25 26 +L3 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [.] 25 26 + [Generic] 26 33 + [;] 33 34 +L4 + [public] 1 7 + [class] 8 13 + [LongLists] 14 23 + [{] 24 25 +L5 + [List] 5 9 + [<] 9 10 + [byte] 10 14 + [>] 14 15 + [l] 16 17 + [=] 18 19 + [new] 20 23 + [List] 24 28 + [<] 28 29 + [byte] 29 33 + [>] 33 34 + [{] 35 36 +L6 + [0] 6 7 + [,] 7 8 + [0] 8 9 + [,] 9 10 + [0] 10 11 + [,] 11 12 + [0] 12 13 + [,] 13 14 + [0] 14 15 + [,] 15 16 + [0] 16 17 + [,] 17 18 + [0] 18 19 + [,] 19 20 + [0] 20 21 + [,] 21 22 + [0] 22 23 + [,] 23 24 + [0] 24 25 + [,] 25 26 + [0] 26 27 + [,] 27 28 + [0] 28 29 + [,] 29 30 + [0] 30 31 + [,] 31 32 + [0] 32 33 + [,] 33 34 + [0] 34 35 + [,] 35 36 + [0] 36 37 + [,] 37 38 + [0] 38 39 + [,] 39 40 + [0] 40 41 + [,] 41 42 + [0] 42 43 + [,] 43 44 + [0] 44 45 + [,] 45 46 + [0] 46 47 + [,] 47 48 + [0] 48 49 + [,] 49 50 + [0] 50 51 + [,] 51 52 + [0] 52 53 + [,] 53 54 + [0] 54 55 + [,] 55 56 + [0] 56 57 + [,] 57 58 + [0] 58 59 + [,] 59 60 + [0] 60 61 + [,] 61 62 + [0] 62 63 + [,] 63 64 + [0] 64 65 + [,] 65 66 + [0] 66 67 + [,] 67 68 + [0] 68 69 + [,] 69 70 + [0] 70 71 + [,] 71 72 + [0] 72 73 + [,] 73 74 + [0] 74 75 + [,] 75 76 + [0] 76 77 + [,] 77 78 + [0] 78 79 + [,] 79 80 + [0] 80 81 + [,] 81 82 + [0] 82 83 + [,] 83 84 + [0] 84 85 + [,] 85 86 + [0] 86 87 + [,] 87 88 + [0] 88 89 + [,] 89 90 + [0] 90 91 + [,] 91 92 + [0] 92 93 + [,] 93 94 + [0] 94 95 + [,] 95 96 + [0] 96 97 + [,] 97 98 + [0] 98 99 + [,] 99 100 + [0] 100 101 + [,] 101 102 + [0] 102 103 + [,] 103 104 + [0] 104 105 + [,] 105 106 + [0] 106 107 + [,] 107 108 + [0] 108 109 + [,] 109 110 + [0] 110 111 + [,] 111 112 + [0] 112 113 + [,] 113 114 + [0] 114 115 + [,] 115 116 + [0] 116 117 + [,] 117 118 + [0] 118 119 + [,] 119 120 + [0] 120 121 + [,] 121 122 + [0] 122 123 + [,] 123 124 + [0] 124 125 + [,] 125 126 + [0] 126 127 + [,] 127 128 + [0] 128 129 + [,] 129 130 + [0] 130 131 + [,] 131 132 + [0] 132 133 + [,] 133 134 + [0] 134 135 + [,] 135 136 + [0] 136 137 + [,] 137 138 + [0] 138 139 + [,] 139 140 + [0] 140 141 + [,] 141 142 + [0] 142 143 + [,] 143 144 + [0] 144 145 + [,] 145 146 + [0] 146 147 + [,] 147 148 + [0] 148 149 + [,] 149 150 + [0] 150 151 + [,] 151 152 + [0] 152 153 + [,] 153 154 + [0] 154 155 + [,] 155 156 + [0] 156 157 + [,] 157 158 + [0] 158 159 + [,] 159 160 + [0] 160 161 + [,] 161 162 + [0] 162 163 + [,] 163 164 + [0] 164 165 + [,] 165 166 + [0] 166 167 + [,] 167 168 + [0] 168 169 + [,] 169 170 + [0] 170 171 + [,] 171 172 + [0] 172 173 + [,] 173 174 + [0] 174 175 + [,] 175 176 + [0] 176 177 + [,] 177 178 + [0] 178 179 + [,] 179 180 + [0] 180 181 + [,] 181 182 + [0] 182 183 + [,] 183 184 + [0] 184 185 + [,] 185 186 + [0] 186 187 + [,] 187 188 + [0] 188 189 + [,] 189 190 + [0] 190 191 + [,] 191 192 + [0] 192 193 + [,] 193 194 + [0] 194 195 + [,] 195 196 + [0] 196 197 + [,] 197 198 + [0] 198 199 + [,] 199 200 + [0] 200 201 + [,] 201 202 + [0] 202 203 + [,] 203 204 + [0] 204 205 + [,] 205 206 + [0] 206 207 + [,] 207 208 + [0] 208 209 + [,] 209 210 + [0] 210 211 + [,] 211 212 + [0] 212 213 + [,] 213 214 + [0] 214 215 + [,] 215 216 + [0] 216 217 + [,] 217 218 + [0] 218 219 + [,] 219 220 + [0] 220 221 + [,] 221 222 + [0] 222 223 + [,] 223 224 + [0] 224 225 + [,] 225 226 + [0] 226 227 + [,] 227 228 + [0] 228 229 + [,] 229 230 + [0] 230 231 + [,] 231 232 + [0] 232 233 + [,] 233 234 + [0] 234 235 + [,] 235 236 + [0] 236 237 + [,] 237 238 + [0] 238 239 + [,] 239 240 + [0] 240 241 + [,] 241 242 + [0] 242 243 + [,] 243 244 + [0] 244 245 + [,] 245 246 + [0] 246 247 + [,] 247 248 + [0] 248 249 + [,] 249 250 + [0] 250 251 + [,] 251 252 + [0] 252 253 + [,] 253 254 + [0] 254 255 + [,] 255 256 + [0] 256 257 + [,] 257 258 + [0] 258 259 + [,] 259 260 + [0] 260 261 + [,] 261 262 + [0] 262 263 + [,] 263 264 + [0] 264 265 + [,] 265 266 + [0] 266 267 + [,] 267 268 + [0] 268 269 + [,] 269 270 + [0] 270 271 + [,] 271 272 + [0] 272 273 + [,] 273 274 + [0] 274 275 + [,] 275 276 + [0] 276 277 + [,] 277 278 + [0] 278 279 + [,] 279 280 + [0] 280 281 + [,] 281 282 + [0] 282 283 + [,] 283 284 + [0] 284 285 + [,] 285 286 + [0] 286 287 + [,] 287 288 + [0] 288 289 + [,] 289 290 + [0] 290 291 + [,] 291 292 + [0] 292 293 + [,] 293 294 + [0] 294 295 + [,] 295 296 + [0] 296 297 + [,] 297 298 + [0] 298 299 + [,] 299 300 + [0] 300 301 + [,] 301 302 + [0] 302 303 + [,] 303 304 + [0] 304 305 + [,] 305 306 +L7 + [}] 5 6 + [;] 6 7 +L8 + [}] 1 2 +EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt new file mode 100644 index 0000000000..811acd84ec --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt @@ -0,0 +1,41 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [using] 1 6 + [System] 7 13 + [;] 13 14 +L2 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [;] 25 26 +L3 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [.] 25 26 + [Generic] 26 33 + [;] 33 34 +L4 + [public] 1 7 + [class] 8 13 + [LongLists] 14 23 + [{] 24 25 +L5 + [List] 5 9 + [<] 9 10 + [byte] 10 14 + [>] 14 15 + [l] 16 17 + [=] 18 19 + [new] 20 23 + [List] 24 28 + [<] 28 29 + [byte] 29 33 + [>] 33 34 +L7 + [;] 6 7 +L8 + [}] 1 2 +EOF diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 89038c57e5..1663f9d031 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -153,11 +153,6 @@ pmd-jsp ${project.version} - - net.sourceforge.pmd - pmd-visualforce - ${project.version} - net.sourceforge.pmd pmd-kotlin @@ -218,6 +213,11 @@ pmd-ui ${pmd-designer.version} + + net.sourceforge.pmd + pmd-visualforce + ${project.version} + net.sourceforge.pmd pmd-vm 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 e65bb8d4ff..aa1288abe0 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 @@ -6,7 +6,6 @@ package net.sourceforge.pmd.docs; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -14,29 +13,21 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.Properties; -import java.util.logging.Logger; 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.RuleSetNotFoundException; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; public final class GenerateRuleDocsCmd { - private static final Logger LOG = Logger.getLogger(GenerateRuleDocsCmd.class.getName()); - private GenerateRuleDocsCmd() { // Utility class } - public static void main(String[] args) throws RuleSetNotFoundException { + public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("One argument is required: The base directory of the module pmd-doc."); System.exit(1); @@ -47,7 +38,7 @@ public final class GenerateRuleDocsCmd { System.out.println("Generating docs into " + output); // important: use a RuleSetFactory that includes all rules, e.g. deprecated rule references - Iterator registeredRuleSets = getRegisteredRuleSets().iterator(); + List registeredRuleSets = new RuleSetLoader().getStandardRuleSets(); List additionalRulesets = findAdditionalRulesets(output); RuleDocGenerator generator = new RuleDocGenerator(new DefaultFileWriter(), output); @@ -57,47 +48,6 @@ public final class GenerateRuleDocsCmd { } - /** - * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. - * - * @return An Iterator of RuleSet objects. - * - * @throws RuleSetNotFoundException if the ruleset file could not be found - */ - private static Iterable getRegisteredRuleSets() throws RuleSetNotFoundException { - String rulesetsProperties; - RuleSetLoader parser = new RuleSetLoader().warnDeprecated(false).includeDeprecatedRuleReferences(true); - - List ruleSets = new ArrayList<>(); - for (Language language : LanguageRegistry.getLanguages()) { - Properties props = new Properties(); - rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; - - InputStream resource = GenerateRuleDocsCmd.class.getResourceAsStream(rulesetsProperties); - if (resource == null) { - LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - continue; - } - - try (InputStream inputStream = resource) { - props.load(inputStream); - String rulesetFilenames = props.getProperty("rulesets.filenames"); - - if (rulesetFilenames != null) { - - ruleSets.addAll(parser.loadFromResources(rulesetFilenames.split(","))); - - } - } catch (IOException ioe) { - throw new RuntimeException("Couldn't find " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path")); - } - } - return ruleSets; - } - public static List findAdditionalRulesets(Path basePath) { try { List additionalRulesets = new ArrayList<>(); 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 f62400b1a9..6f7c7e60ce 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 @@ -17,7 +17,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -29,6 +28,7 @@ import java.util.logging.Level; import java.util.logging.Logger; 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; @@ -36,9 +36,8 @@ import org.apache.commons.text.StringEscapeUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoadException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -91,21 +90,16 @@ public class RuleDocGenerator { } } - public void generate(Iterator registeredRulesets, List additionalRulesets) { + public void generate(List registeredRulesets, List additionalRulesets) throws IOException { Map> sortedRulesets; Map> sortedAdditionalRulesets; - try { - sortedRulesets = sortRulesets(registeredRulesets); - sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); - determineRuleClassSourceFiles(sortedRulesets); - generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); - generateRuleSetIndex(sortedRulesets); + sortedRulesets = sortRulesets(registeredRulesets); + sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); + determineRuleClassSourceFiles(sortedRulesets); + generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); + generateRuleSetIndex(sortedRulesets); - generateSidebar(sortedRulesets); - - } catch (RuleSetNotFoundException | IOException e) { - throw new RuntimeException(e); - } + generateSidebar(sortedRulesets); } private void generateSidebar(Map> sortedRulesets) throws IOException { @@ -113,53 +107,40 @@ public class RuleDocGenerator { generator.generateSidebar(sortedRulesets); } - private Iterator resolveAdditionalRulesets(List additionalRulesets) throws RuleSetNotFoundException { + private List resolveAdditionalRulesets(List additionalRulesets) { if (additionalRulesets == null) { - return Collections.emptyIterator(); + return Collections.emptyList(); } List rulesets = new ArrayList<>(); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); + RuleSetLoader ruleSetLoader = new RuleSetLoader(); for (String filename : additionalRulesets) { try { // do not take rulesets from pmd-test or pmd-core if (!filename.contains("pmd-test") && !filename.contains("pmd-core")) { - rulesets.add(ruleSetFactory.createRuleSet(filename)); + rulesets.add(ruleSetLoader.loadFromResource(filename)); } else { LOG.fine("Ignoring ruleset " + filename); } - } catch (IllegalArgumentException e) { + } catch (RuleSetLoadException e) { // ignore rulesets, we can't read LOG.log(Level.WARNING, "ruleset file " + filename + " ignored (" + e.getMessage() + ")", e); } } - return rulesets.iterator(); + return rulesets; } private Path getAbsoluteOutputPath(String filename) { return root.resolve(FilenameUtils.normalize(filename)); } - private Map> sortRulesets(Iterator rulesets) throws RuleSetNotFoundException { - SortedMap> rulesetsByLanguage = new TreeMap<>(); - - while (rulesets.hasNext()) { - RuleSet ruleset = rulesets.next(); - Language language = getRuleSetLanguage(ruleset); - - if (!rulesetsByLanguage.containsKey(language)) { - rulesetsByLanguage.put(language, new ArrayList()); - } - rulesetsByLanguage.get(language).add(ruleset); - } + private Map> sortRulesets(List rulesets) { + SortedMap> rulesetsByLanguage = rulesets.stream().collect(Collectors.groupingBy(RuleDocGenerator::getRuleSetLanguage, + TreeMap::new, + Collectors.toCollection(ArrayList::new))); for (List rulesetsOfOneLanguage : rulesetsByLanguage.values()) { - Collections.sort(rulesetsOfOneLanguage, new Comparator() { - @Override - public int compare(RuleSet o1, RuleSet o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + rulesetsOfOneLanguage.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); } return rulesetsByLanguage; } 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 5e7561cdf3..23114c6e53 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 @@ -21,11 +21,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; public class RuleDocGeneratorTest { @@ -59,13 +56,13 @@ public class RuleDocGeneratorTest { } @Test - public void testSingleRuleset() throws RuleSetNotFoundException, IOException { + public void testSingleRuleset() throws IOException { RuleDocGenerator generator = new RuleDocGenerator(writer, root); - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); - RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); + RuleSetLoader rsf = new RuleSetLoader().includeDeprecatedRuleReferences(true); + RuleSet ruleset = rsf.loadFromResource("rulesets/ruledoctest/sample.xml"); - generator.generate(Arrays.asList(ruleset).iterator(), + generator.generate(Arrays.asList(ruleset), Arrays.asList( "rulesets/ruledoctest/sample-deprecated.xml", "rulesets/ruledoctest/other-ruleset.xml")); 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 c2c082c44f..8b82683953 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 @@ -4,28 +4,23 @@ package net.sourceforge.pmd.docs; -import static org.junit.Assert.fail; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; import java.nio.file.FileSystems; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.io.FilenameUtils; import org.junit.Test; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; public class RuleSetResolverTest { - private static List excludedRulesets = new ArrayList<>(); - - static { - excludedRulesets.add(FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml")); - } + private static final List EXCLUDED_RULESETS = listOf( + FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml") + ); @Test public void resolveAllRulesets() { @@ -34,13 +29,8 @@ public class RuleSetResolverTest { filterRuleSets(additionalRulesets); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); for (String filename : additionalRulesets) { - try { - ruleSetFactory.createRuleSet(filename); - } catch (RuntimeException | RuleSetNotFoundException e) { - fail("Couldn't load ruleset " + filename + ": " + e.getMessage()); - } + new RuleSetLoader().loadFromResource(filename); // will throw if invalid } } @@ -48,7 +38,7 @@ public class RuleSetResolverTest { Iterator it = additionalRulesets.iterator(); while (it.hasNext()) { String filename = it.next(); - for (String exclusion : excludedRulesets) { + for (String exclusion : EXCLUDED_RULESETS) { if (filename.endsWith(exclusion)) { it.remove(); break; 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 ea01604b3c..97e562e49b 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 @@ -25,7 +25,6 @@ import org.yaml.snakeyaml.DumperOptions.LineBreak; import org.yaml.snakeyaml.Yaml; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -40,8 +39,8 @@ public class SidebarGeneratorTest { @Test public void testSidebar() throws IOException { Map> rulesets = new HashMap<>(); - RuleSet ruleSet1 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - RuleSet ruleSet2 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet1 = RuleSet.create("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet2 = RuleSet.create("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); rulesets.put(LanguageRegistry.findLanguageByTerseName("java"), Arrays.asList(ruleSet1, ruleSet2)); rulesets.put(LanguageRegistry.findLanguageByTerseName("ecmascript"), Arrays.asList(ruleSet1)); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index 4fbb3b45f7..d9c90d568a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.List; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.JavaParser; @@ -63,7 +62,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new JavaParser(levelChecker); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java index 82ea70a122..d0f7856266 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java @@ -5,16 +5,13 @@ package net.sourceforge.pmd.lang.java.multifile.signature; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; /** * Signature for an operation. @@ -105,65 +102,49 @@ public final class JavaOperationSignature extends JavaSignature fieldNames = - node.getEnclosingType() - .getDeclarations() - .filterIs(ASTFieldDeclaration.class) - .flatMap(ASTFieldDeclaration::getVarIds) - .collect(Collectors.toMap( - f -> { - Matcher matcher = FIELD_NAME_PATTERN.matcher(f.getVariableName()); - return matcher.find() ? matcher.group(1) : f.getVariableName(); - }, - f -> f.getTypeNode().getTypeImage() - )); - - return isGetter(node, fieldNames) || isSetter(node, fieldNames); + private static boolean containerHasFieldNamed(ASTMethodDeclaration m, String nameIgnoringCase) { + return m.getEnclosingType() + .getSymbol() + .getDeclaredFields() + .stream() + .map(JElementSymbol::getSimpleName) + .anyMatch(nameIgnoringCase::equalsIgnoreCase); } /** Attempts to determine if the method is a getter. */ - private static boolean isGetter(ASTMethodDeclaration node, Map fieldNames) { + private static boolean isGetter(ASTMethodDeclaration node) { if (node.getArity() != 0 || node.isVoid()) { return false; } if (node.getName().startsWith("get")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(3)); + return containerHasFieldNamed(node, node.getName().substring(3)); } else if (node.getName().startsWith("is")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(2)); + return containerHasFieldNamed(node, node.getName().substring(2)); } - return fieldNames.containsKey(node.getName()); + return containerHasFieldNamed(node, node.getName()); } /** Attempts to determine if the method is a setter. */ - private static boolean isSetter(ASTMethodDeclaration node, Map fieldNames) { + private static boolean isSetter(ASTMethodDeclaration node) { if (node.getArity() != 1 || !node.isVoid()) { return false; } if (node.getName().startsWith("set")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(3)); + return containerHasFieldNamed(node, node.getName().substring(3)); } - return fieldNames.containsKey(node.getName()); - } - - - private static boolean containsIgnoreCase(Set set, String str) { - for (String s : set) { - if (str.equalsIgnoreCase(s)) { - return true; - } - } - return false; + return containerHasFieldNamed(node, node.getName()); } } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java deleted file mode 100644 index 499e5efc9f..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; -import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; - -/** - * This is an abstract rule for patterns which compare a method invocation to 0. - * It could be further abstracted to find code that compares something to - * another definable pattern - * - * @author acaplan - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule { - - private static Map inverse = new HashMap<>(); - - public abstract boolean appliesToClassName(String name); - - public abstract boolean isTargetMethod(JavaNameOccurrence occ); - - /** - * For each relation/equality operator, comparison targets need to define. - * - * @return map - */ - public Map> getComparisonTargets() { - Map> rules = new HashMap<>(); - rules.put("==", Arrays.asList("0")); - rules.put("!=", Arrays.asList("0")); - rules.put(">", Arrays.asList("0")); - rules.put("<", Arrays.asList("0")); - return rules; - } - - static { - inverse.put("<", ">"); - inverse.put(">", "<"); - inverse.put("<=", ">="); - inverse.put(">=", "<="); - inverse.put("==", "=="); - inverse.put("!=", "!="); - } - - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - Node nameNode = node.getTypeNameNode(); - if (nameNode == null || nameNode instanceof ASTPrimitiveType - || node.getNameDeclaration() == null - || !appliesToClassName(node.getNameDeclaration().getTypeImage())) { - return data; - } - - List declars = node.getUsages(); - for (NameOccurrence occ : declars) { - JavaNameOccurrence jocc = (JavaNameOccurrence) occ; - if (!isTargetMethod(jocc)) { - continue; - } - Node expr = jocc.getLocation().getParent().getParent().getParent(); - checkNodeAndReport(data, jocc.getLocation(), expr); - } - return data; - } - - /** - * Checks whether the given expression is a equality/relation expression - * that compares with a size() call. - * - * @param data - * the rule context - * @param location - * the node location to report - * @param expr - * the ==, <, > expression - */ - protected void checkNodeAndReport(Object data, Node location, Node expr) { - if ((expr instanceof ASTEqualityExpression - || expr instanceof ASTRelationalExpression && getComparisonTargets().containsKey(expr.getImage())) - && isCompare(expr)) { - addViolation(data, location); - } - } - - /** - * We only need to report if this is comparing against one of the comparison - * targets - * - * @param equality - * @return true if this is comparing to one of the comparison targets else - * false - * @see #getComparisonTargets() - */ - private boolean isCompare(Node equality) { - if (isLiteralLeftHand(equality)) { - return checkComparison(inverse.get(equality.getImage()), equality, 0); - } else if (isLiteralRightHand(equality)) { - return checkComparison(equality.getImage(), equality, 1); - } - return false; - } - - private boolean isLiteralLeftHand(Node equality) { - return isLiteral(equality, 0); - } - - private boolean isLiteralRightHand(Node equality) { - return isLiteral(equality, 1); - } - - private boolean isLiteral(Node equality, int child) { - Node target = equality.getChild(child); - target = getFirstChildOrThis(target); - target = getFirstChildOrThis(target); - return target instanceof ASTLiteral; - } - - private Node getFirstChildOrThis(Node node) { - if (node.getNumChildren() > 0) { - return node.getChild(0); - } - return node; - } - - /** - * Checks if the equality expression passed in is of comparing against the - * value passed in as i - * - * @param equality - * @param i - * The ordinal in the equality expression to check - * @return true if the value in position i is one of the comparison targets, - * else false - * @see #getComparisonTargets() - */ - private boolean checkComparison(String operator, Node equality, int i) { - Node target = equality.getChild(i).getChild(0).getChild(0); - return target instanceof ASTLiteral && getComparisonTargets().get(operator).contains(target.getImage()); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java deleted file mode 100644 index 874331fbf2..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; - -/** - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractJUnitRule extends AbstractJavaRule { - - protected static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; - protected static final String JUNIT4_CLASS_NAME = "org.junit.Test"; - protected static final String JUNIT5_CLASS_NAME = "org.junit.jupiter.api.Test"; - - protected boolean isJUnit3Class; - protected boolean isJUnit4Class; - protected boolean isJUnit5Class; - - @Override - public Object visit(ASTCompilationUnit node, Object data) { - - isJUnit3Class = false; - isJUnit4Class = false; - isJUnit5Class = false; - - isJUnit3Class = isJUnit3Class(node); - isJUnit4Class = isJUnit4Class(node); - isJUnit5Class = isJUnit5Class(node); - - if (isJUnit4Class && isJUnit5Class) { - isJUnit4Class &= hasImports(node, JUNIT4_CLASS_NAME); - isJUnit5Class &= hasImports(node, JUNIT5_CLASS_NAME); - } - - if (!isTestNgClass(node) && (isJUnit3Class || isJUnit4Class || isJUnit5Class)) { - return super.visit(node, data); - } - return data; - } - - private boolean isTestNgClass(ASTCompilationUnit node) { - List imports = node.findDescendantsOfType(ASTImportDeclaration.class); - for (ASTImportDeclaration i : imports) { - if (i.getImportedName() != null && i.getImportedName().startsWith("org.testng")) { - return true; - } - } - return false; - } - - public boolean isJUnitMethod(ASTMethodDeclaration method, Object data) { - if (method.isAbstract() || method.isNative() || method.isStatic()) { - return false; // skip various inapplicable method variations - } - - if (!isJUnit5Class && !method.isPublic()) { - // junit5 class doesn't require test methods to be public anymore - return false; - } - - boolean result = false; - result |= isJUnit3Method(method); - result |= isJUnit4Method(method); - result |= isJUnit5Method(method); - return result; - } - - private boolean isJUnit4Method(ASTMethodDeclaration method) { - return isJUnit4Class && doesNodeContainJUnitAnnotation(method.getParent(), JUNIT4_CLASS_NAME); - } - - private boolean isJUnit5Method(ASTMethodDeclaration method) { - return isJUnit5Class && doesNodeContainJUnitAnnotation(method.getParent(), JUNIT5_CLASS_NAME); - } - - private boolean isJUnit3Method(ASTMethodDeclaration method) { - return isJUnit3Class && method.isVoid() && method.getName().startsWith("test"); - } - - private boolean isJUnit3Class(ASTCompilationUnit node) { - ASTClassOrInterfaceDeclaration cid = node.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class); - return TypeTestUtil.isA(JUNIT3_CLASS_NAME, cid); - } - - private boolean isJUnit4Class(ASTCompilationUnit node) { - return doesNodeContainJUnitAnnotation(node, JUNIT4_CLASS_NAME); - } - - private boolean isJUnit5Class(ASTCompilationUnit node) { - return doesNodeContainJUnitAnnotation(node, JUNIT5_CLASS_NAME); - } - - private boolean doesNodeContainJUnitAnnotation(Node node, String annotationTypeClassName) { - List annotations = node.findDescendantsOfType(ASTAnnotation.class); - for (ASTAnnotation annotation : annotations) { - Node annotationTypeNode = annotation.getChild(0); - TypeNode annotationType = (TypeNode) annotationTypeNode; - if (annotationType.getType() == null) { - ASTName name = annotationTypeNode.getFirstChildOfType(ASTName.class); - if (name != null && (name.hasImageEqualTo("Test") || name.hasImageEqualTo(annotationTypeClassName))) { - return true; - } - } else if (TypeTestUtil.isA(annotationTypeClassName, annotationType)) { - return true; - } - } - return false; - } - - private boolean hasImports(ASTCompilationUnit cu, String className) { - List imports = cu.findDescendantsOfType(ASTImportDeclaration.class); - for (ASTImportDeclaration importDeclaration : imports) { - ASTName name = importDeclaration.getFirstChildOfType(ASTName.class); - if (name != null && name.hasImageEqualTo(className)) { - return true; - } - } - return false; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java deleted file mode 100644 index b869265b19..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; - -/** - * Detects and flags the occurrences of specific method calls against an - * instance of a designated class. I.e. String.indexOf. The goal is to be able - * to suggest more efficient/modern ways of implementing the same function. - * - *

Concrete subclasses are expected to provide the name of the target class and - * an array of method names that we are looking for. We then pass judgment on - * any literal arguments we find in the subclass as well. - * - * @author Brian Remedios - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractPoorMethodCall extends AbstractJavaRule { - // FIXME not sure the abstraction is generic enough to be reused as is. - - /** - * The name of the type the method will be invoked against. - * - * @return String - */ - protected abstract String targetTypename(); - - /** - * Return the names of all the methods we are scanning for, no brackets or - * argument types. - * - * @return String[] - */ - protected abstract String[] methodNames(); - - /** - * Returns whether the node being sent to the method is OK or not. Return - * true if you want to record the method call as a violation. - * - * @param arg - * the node to inspect - * @return boolean - */ - protected abstract boolean isViolationArgument(Node arg); - - /** - * Returns whether the name occurrence is one of the method calls we are - * interested in. - * - * @param occurrence - * NameOccurrence - * @return boolean - */ - private boolean isNotedMethod(NameOccurrence occurrence) { - - if (occurrence == null) { - return false; - } - - String methodCall = occurrence.getImage(); - String[] methodNames = methodNames(); - - for (String element : methodNames) { - if (methodCall.contains(element)) { - return true; - } - } - return false; - } - - /** - * Method visit. - * - * @param node - * ASTVariableDeclaratorId - * @param data - * Object - * @return Object - * @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, - * Object) - */ - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - if (node.getNameDeclaration() == null || !targetTypename().equals(node.getNameDeclaration().getTypeImage())) { - return data; - } - - for (NameOccurrence occ : node.getUsages()) { - JavaNameOccurrence jocc = (JavaNameOccurrence) occ; - if (isNotedMethod(jocc.getNameForWhichThisIsAQualifier())) { - Node parent = jocc.getLocation().getParent().getParent(); - if (parent instanceof ASTPrimaryExpression) { - // bail out if it's something like indexOf("a" + "b") - if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) { - return data; - } - List literals = parent.findDescendantsOfType(ASTLiteral.class); - for (int l = 0; l < literals.size(); l++) { - ASTLiteral literal = literals.get(l); - if (isViolationArgument(literal)) { - addViolation(data, jocc.getLocation()); - } - } - } - } - } - return data; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java deleted file mode 100644 index 9c924643c3..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -class AbstractPositionLiteralsFirstInComparisons extends AbstractJavaRule { - - private final String equalsImage; - - AbstractPositionLiteralsFirstInComparisons(String equalsImage) { - addRuleChainVisit(ASTPrimaryExpression.class); - this.equalsImage = equalsImage; - } - - @Override - public Object visit(ASTPrimaryExpression node, Object data) { - ASTPrimaryPrefix primaryPrefix = node.getFirstChildOfType(ASTPrimaryPrefix.class); - ASTPrimarySuffix primarySuffix = node.getFirstChildOfType(ASTPrimarySuffix.class); - if (primaryPrefix != null && primarySuffix != null) { - ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); - if (name == null || !name.getImage().endsWith(equalsImage)) { - return data; - } - if (!isSingleStringLiteralArgument(primarySuffix)) { - return data; - } - if (isWithinNullComparison(node)) { - return data; - } - addViolation(data, node); - } - return node; - } - - private boolean isWithinNullComparison(ASTPrimaryExpression node) { - for (ASTExpression parentExpr : node.getParentsOfType(ASTExpression.class)) { - if (isComparisonWithNull(parentExpr, "==", ASTConditionalOrExpression.class) - || isComparisonWithNull(parentExpr, "!=", ASTConditionalAndExpression.class)) { - return true; - } - } - return false; - } - - /* - * Expression/ConditionalAndExpression//EqualityExpression(@Image='!=']//NullLiteral - * Expression/ConditionalOrExpression//EqualityExpression(@Image='==']//NullLiteral - */ - private boolean isComparisonWithNull(ASTExpression parentExpr, String equalOperator, Class condition) { - Node condExpr = null; - ASTEqualityExpression eqExpr = null; - if (parentExpr != null) { - condExpr = parentExpr.getFirstChildOfType(condition); - } - if (condExpr != null) { - eqExpr = condExpr.getFirstDescendantOfType(ASTEqualityExpression.class); - } - if (eqExpr != null) { - return eqExpr.hasImageEqualTo(equalOperator) && eqExpr.hasDescendantOfType(ASTNullLiteral.class); - } - return false; - } - - /* - * This corresponds to the following XPath expression: - * (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()]) - * and - * ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) - */ - private boolean isSingleStringLiteralArgument(ASTPrimarySuffix primarySuffix) { - if (!primarySuffix.isArguments() || primarySuffix.getArgumentCount() != 1) { - return false; - } - Node node = primarySuffix; - node = node.getFirstChildOfType(ASTArguments.class); - if (node != null) { - node = node.getFirstChildOfType(ASTArgumentList.class); - if (node.getNumChildren() != 1) { - return false; - } - } - if (node != null) { - node = node.getFirstChildOfType(ASTExpression.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTPrimaryExpression.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTPrimaryPrefix.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTLiteral.class); - } - if (node != null) { - ASTLiteral literal = (ASTLiteral) node; - if (literal.isStringLiteral()) { - return true; - } - } - return false; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java index 79a348e7a2..2828b0d36a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java @@ -4,18 +4,14 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** * 1. Note all private constructors. 2. Note all instantiations from outside of @@ -32,67 +28,33 @@ import net.sourceforge.pmd.lang.java.symboltable.ClassScope; * @author Romain PELISSE, belaran@gmail.com, patch bug#1807370 * @author Juan Martin Sotuyo Dodero (juansotuyo@gmail.com), complete rewrite */ -public class AccessorClassGenerationRule extends AbstractJavaRule { +public class AccessorClassGenerationRule extends AbstractJavaRulechainRule { - private Map> privateConstructors = new HashMap<>(); + private final Set reportedNodes = new HashSet<>(); public AccessorClassGenerationRule() { - super(); - /* - * Order is important. Visit constructors first to find the private - * ones, then visit allocations to find violations - */ - addRuleChainVisit(ASTConstructorDeclaration.class); - addRuleChainVisit(ASTAllocationExpression.class); + super(ASTConstructorCall.class, ASTExplicitConstructorInvocation.class); } @Override - public void end(final RuleContext ctx) { + public void end(RuleContext ctx) { super.end(ctx); - // Clean up all references to the AST - privateConstructors.clear(); + reportedNodes.clear(); } @Override - public Object visit(final ASTConstructorDeclaration node, final Object data) { - if (node.isPrivate()) { - final String className = node.getParent().getParent().getParent().getImage(); - if (!privateConstructors.containsKey(className)) { - privateConstructors.put(className, new ArrayList()); - } - privateConstructors.get(className).add(node); + public Object visit(ASTConstructorCall node, Object data) { + if (!node.isAnonymousClass()) { + AccessorMethodGenerationRule.checkMemberAccess(this, (RuleContext) data, node, node.getMethodType().getSymbol(), this.reportedNodes); } - return data; + return null; } @Override - public Object visit(final ASTAllocationExpression node, final Object data) { - if (node.getChild(0) instanceof ASTClassOrInterfaceType) { // Ignore primitives - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.getChild(0); - final List constructors = privateConstructors.get(type.getImage()); - - if (constructors != null) { - final ASTArguments callArguments = node.getFirstChildOfType(ASTArguments.class); - // Is this really a constructor call and not an array? - if (callArguments != null) { - final ClassScope enclosingScope = node.getScope().getEnclosingScope(ClassScope.class); - - for (final ASTConstructorDeclaration cd : constructors) { - // Are we within the same class scope? - if (cd.getScope().getEnclosingScope(ClassScope.class) == enclosingScope) { - break; - } - - if (cd.getArity() == callArguments.size()) { - // TODO : Check types - addViolation(data, node); - break; - } - } - } - } + public Object visit(ASTExplicitConstructorInvocation node, Object data) { + if (node.isSuper()) { + AccessorMethodGenerationRule.checkMemberAccess(this, (RuleContext) data, node, node.getMethodType().getSymbol(), this.reportedNodes); } - - return data; + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java index 6917760242..f73beab33a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java @@ -4,135 +4,97 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; -import net.sourceforge.pmd.lang.java.ast.AccessNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.AbstractJavaScope; -import net.sourceforge.pmd.lang.java.symboltable.ClassNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; -import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JAccessibleElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; +import net.sourceforge.pmd.lang.rule.AbstractRule; -public class AccessorMethodGenerationRule extends AbstractJavaRule { +public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { - private List cache = new ArrayList<>(); + private final Set reportedNodes = new HashSet<>(); - @Override - public Object visit(final ASTCompilationUnit node, final Object data) { - final SourceFileScope file = node.getScope().getEnclosingScope(SourceFileScope.class); - analyzeScope(file, data); - - return data; // Stop tree navigation - } - - private void analyzeScope(final AbstractJavaScope file, final Object data) { - for (final ClassNameDeclaration classDecl : file.getDeclarations(ClassNameDeclaration.class).keySet()) { - final ClassScope classScope = (ClassScope) classDecl.getScope(); - - // Check fields - for (final Map.Entry> varDecl : classScope.getVariableDeclarations().entrySet()) { - final ASTFieldDeclaration field = varDecl.getKey().getNode().getFirstParentOfType(ASTFieldDeclaration.class); - analyzeMember(field, varDecl.getValue(), classScope, data); - } - - // Check methods - for (final Map.Entry> methodDecl : classScope.getMethodDeclarations().entrySet()) { - final ASTMethodDeclaration method = methodDecl.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class); - analyzeMember(method, methodDecl.getValue(), classScope, data); - } - - // Check inner classes - analyzeScope(classScope, data); - } - } - - public void analyzeMember(final AccessNode node, final List occurrences, - final ClassScope classScope, final Object data) { - if (!node.isPrivate()) { - return; - } - - if (node.isFinal()) { - for (final ASTVariableDeclarator varDecl: node.findChildrenOfType(ASTVariableDeclarator.class)) { - if (varDecl.hasInitializer()) { - ASTExpression varInit = varDecl.getInitializer(); - List initExpression = varInit.findDescendantsOfType(ASTExpression.class); - boolean isConstantExpression = true; - constantCheck: - for (ASTExpression exp: initExpression) { - List primaryExpressions = exp.findDescendantsOfType(ASTPrimaryExpression.class); - for (ASTPrimaryExpression expression: primaryExpressions) { - if (!isCompileTimeConstant(expression)) { - isConstantExpression = false; - break constantCheck; - } - } - } - if (isConstantExpression) { - cache.add(varDecl.getName()); - return; - } - } - } - } - - for (final NameOccurrence no : occurrences) { - ClassScope usedAtScope = no.getLocation().getScope().getEnclosingScope(ClassScope.class); - - // Are we within the same class that defines the field / method? - if (!classScope.equals(usedAtScope)) { - addViolation(data, no.getLocation()); - } - } - } - - public boolean isCompileTimeConstant(ASTPrimaryExpression expressions) { - // function call detected - List suffix = expressions.findDescendantsOfType(ASTPrimarySuffix.class); - if (!suffix.isEmpty()) { - return false; - } - - // single node expression - List nameNodes = expressions.findDescendantsOfType(ASTName.class); - List literalNodes = expressions.findDescendantsOfType(ASTLiteral.class); - if (nameNodes.size() + literalNodes.size() < 2) { - for (ASTName node: nameNodes) { - // TODO : use the symbol table to get the declaration of the referenced var and check it - if (!cache.contains(node.getImage())) { - return false; - } - } - return true; - } - - // multiple node expression - List subExpressions = expressions.findDescendantsOfType(ASTPrimaryExpression.class); - for (ASTPrimaryExpression exp: subExpressions) { - if (!isCompileTimeConstant(exp)) { - return false; - } - } - return true; + public AccessorMethodGenerationRule() { + super(ASTFieldAccess.class, ASTVariableAccess.class, ASTMethodCall.class); } @Override public void end(RuleContext ctx) { - cache.clear(); + super.end(ctx); + reportedNodes.clear(); + } + + @Override + public Object visit(ASTFieldAccess node, Object data) { + JFieldSymbol sym = node.getReferencedSym(); + if (sym != null && sym.getConstValue() == null) { + checkMemberAccess((RuleContext) data, node, sym); + } + return null; + } + + @Override + public Object visit(ASTVariableAccess node, Object data) { + JVariableSymbol sym = node.getReferencedSym(); + if (sym instanceof JFieldSymbol) { + JFieldSymbol fieldSym = (JFieldSymbol) sym; + if (((JFieldSymbol) sym).getConstValue() == null) { + checkMemberAccess((RuleContext) data, node, fieldSym); + } + } + return null; + } + + @Override + public Object visit(ASTMethodCall node, Object data) { + JMethodSymbol symbol = (JMethodSymbol) node.getMethodType().getSymbol(); + checkMemberAccess((RuleContext) data, node, symbol); + return null; + } + + private void checkMemberAccess(RuleContext data, ASTExpression node, JAccessibleElementSymbol symbol) { + checkMemberAccess(this, data, node, symbol, this.reportedNodes); + } + + static void checkMemberAccess(AbstractRule rule, RuleContext data, JavaNode refExpr, JAccessibleElementSymbol sym, Set reportedNodes) { + if (Modifier.isPrivate(sym.getModifiers()) + && !Objects.equals(sym.getEnclosingClass(), + refExpr.getEnclosingType().getSymbol())) { + + JavaNode node = sym.tryGetNode(); + assert node != null : "Node should be in the same compilation unit"; + if (reportedNodes.add(node)) { + rule.addViolation(data, node, new String[] {stripPackageName(refExpr.getEnclosingType().getSymbol())}); + } + } + } + + /** + * Returns the canonical name without the package name. Eg for a + * canonical name {@code com.github.Outer.Inner}, returns {@code Outer.Inner}. + */ + private static String stripPackageName(JClassSymbol symbol) { + String p = symbol.getPackageName(); + String canoName = symbol.getCanonicalName(); + if (canoName == null) { + return symbol.getSimpleName(); + } + if (p.isEmpty()) { + return canoName; + } + return canoName.substring(p.length() + 1); //+1 for the dot } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index 6ae4943d11..1148593bb0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -4,93 +4,47 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + import java.util.List; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher; -public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJUnitRule { +public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulechainRule { - private class AssertionCall { - private final int argumentsCount; - private final String assertionName; - - AssertionCall(String assertionName, int argumentsCount) { - this.argumentsCount = argumentsCount; - this.assertionName = assertionName; - } - - public void check(Object ctx, ASTArguments node) { - if (node.size() == argumentsCount - && node.getNthParent(2) instanceof ASTPrimaryExpression) { - ASTPrimaryPrefix primaryPrefix = node.getNthParent(2).getFirstChildOfType(ASTPrimaryPrefix.class); - - if (primaryPrefix != null) { - ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); - - if (name != null && name.hasImageEqualTo(this.assertionName)) { - if (isException(node)) { - return; - } - JUnitAssertionsShouldIncludeMessageRule.this.addViolation(ctx, name); - } - } - } - } - - protected boolean isException(ASTArguments node) { - return false; - } - } - - private List checks = new ArrayList<>(); + private final List checks = + listOf( + InvocationMatcher.parse("_#assertEquals(_,_)"), + InvocationMatcher.parse("_#assertTrue(_)"), + InvocationMatcher.parse("_#assertFalse(_)"), + InvocationMatcher.parse("_#assertSame(_,_)"), + InvocationMatcher.parse("_#assertNotSame(_,_)"), + InvocationMatcher.parse("_#assertNull(_)"), + InvocationMatcher.parse("_#assertNotNull(_)"), + InvocationMatcher.parse("_#assertArrayEquals(_,_)"), + InvocationMatcher.parse("_#assertThat(_,_)"), + InvocationMatcher.parse("_#fail()"), + InvocationMatcher.parse("_#assertEquals(float,float,float)"), + InvocationMatcher.parse("_#assertEquals(double,double,double)") + ); public JUnitAssertionsShouldIncludeMessageRule() { - checks.add(new AssertionCall("assertArrayEquals", 2)); - checks.add(new AssertionCall("assertEquals", 2)); - checks.add(new AssertionCall("assertFalse", 1)); - checks.add(new AssertionCall("assertNotNull", 1)); - checks.add(new AssertionCall("assertNotSame", 2)); - checks.add(new AssertionCall("assertNull", 1)); - checks.add(new AssertionCall("assertSame", 2)); - checks.add(new AssertionCall("assertThat", 2)); - checks.add(new AssertionCall("assertTrue", 1)); - checks.add(new AssertionCall("fail", 0)); - - checks.add(new AssertionCall("assertEquals", 3) { - @Override - protected boolean isException(ASTArguments node) { - // consider the top-level expressions of the arguments: Arguments/ArgumentList/Expression - ASTArgumentList argumentList = node.getFirstChildOfType(ASTArgumentList.class); - List arguments = argumentList.findChildrenOfType(ASTExpression.class); - boolean isExceptionJunit4 = isStringTypeOrNull(arguments.get(0)); - boolean isExceptionJunit5 = isStringTypeOrNull(arguments.get(2)); - - return isExceptionJunit4 || isExceptionJunit5; - } - }); + super(ASTMethodCall.class); } @Override - public Object visit(ASTArguments node, Object data) { - for (AssertionCall call : checks) { - call.check(data, node); + public Object visit(ASTMethodCall node, Object data) { + if (TestFrameworksUtil.isCallOnAssertionContainer(node)) { + for (InvocationMatcher check : checks) { + if (check.matchesCall(node)) { + addViolation(data, node); + break; + } + } } - return super.visit(node, data); - } - - /** - * @param node - * the node to check - * @return {@code true} if node's type is String or null, otherwise {@code false} - */ - private boolean isStringTypeOrNull(ASTExpression node) { - return node.getType() == String.class || node.getType() == null; + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java new file mode 100644 index 0000000000..33d578d98a --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.constraints.NumericConstraints; + +public class JUnitTestContainsTooManyAssertsRule extends AbstractJavaRulechainRule { + + private static final PropertyDescriptor MAX_ASSERTS = + PropertyFactory.intProperty("maximumAsserts") + .desc("Maximum number of assert calls in a test method") + .require(NumericConstraints.positive()) + .defaultValue(1) + .build(); + + + public JUnitTestContainsTooManyAssertsRule() { + super(ASTMethodDeclaration.class); + definePropertyDescriptor(MAX_ASSERTS); + } + + @Override + public Object visit(ASTMethodDeclaration method, Object data) { + ASTBlock body = method.getBody(); + if (body != null && TestFrameworksUtil.isTestMethod(method)) { + int assertCount = body.descendants(ASTMethodCall.class) + .filter(TestFrameworksUtil::isProbableAssertCall) + .count(); + if (assertCount > getProperty(MAX_ASSERTS)) { + addViolation(data, method); + } + } + return data; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 385509d3e9..b00a712ff4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -4,216 +4,30 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -import net.sourceforge.pmd.lang.symboltable.Scope; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; -public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { +public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isInterface()) { - return data; - } - return super.visit(node, data); + + public JUnitTestsShouldIncludeAssertRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration method, Object data) { - if (isJUnitMethod(method, data)) { - if (!isExpectAnnotated(method)) { - Map variables = getVariables(method); - - Scope classScope = method.getScope().getParent(); - Map> expectables = getRuleAnnotatedExpectedExceptions(classScope); - - if (!containsExpectOrAssert(method.getBody(), expectables, variables)) { - addViolation(data, method); - } - } + ASTBlock body = method.getBody(); + if (body != null + && TestFrameworksUtil.isJUnitMethod(method) + && !TestFrameworksUtil.isExpectAnnotated(method) + && body.descendants(ASTMethodCall.class) + .none(TestFrameworksUtil::isProbableAssertCall)) { + addViolation(data, method); } return data; } - private boolean containsExpectOrAssert(Node n, - Map> expectables, - Map variables) { - if (n instanceof ASTStatementExpression) { - if (isExpectStatement((ASTStatementExpression) n, expectables) - || isAssertOrFailStatement((ASTStatementExpression) n) - || isHamcrestAssert((ASTStatementExpression) n) - || isVerifyStatement((ASTStatementExpression) n) - || isSoftAssertionStatement((ASTStatementExpression) n, variables)) { - return true; - } - } else { - for (int i = 0; i < n.getNumChildren(); i++) { - Node c = n.getChild(i); - if (containsExpectOrAssert(c, expectables, variables)) { - return true; - } - } - } - return false; - } - - private Map getVariables(ASTMethodDeclaration method) { - Map variables = new HashMap<>(); - for (VariableNameDeclaration vnd : method.getScope().getDeclarations(VariableNameDeclaration.class).keySet()) { - variables.put(vnd.getName(), vnd); - } - return variables; - } - - /** - * Gets a list of NameDeclarations for all the fields that have type - * ExpectedException and have a Rule annotation. - * - * @param classScope - * The class scope to search for - * @return See description - */ - private Map> getRuleAnnotatedExpectedExceptions(Scope classScope) { - Map> result = new HashMap<>(); - Map> decls = classScope.getDeclarations(); - - for (Map.Entry> entry : decls.entrySet()) { - Node parent = entry.getKey().getNode().getParent().getParent().getParent(); - if (parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { - ASTAnnotation annot = parent.getFirstDescendantOfType(ASTAnnotation.class); - if (annot == null || !TypeTestUtil.isA("org.junit.Rule", annot)) { - continue; - } - - Node type = parent.getFirstDescendantOfType(ASTReferenceType.class); - if (!"ExpectedException".equals(type.getChild(0).getImage())) { - continue; - } - result.put(entry.getKey().getName(), entry.getValue()); - } - } - return result; - } - - /** - * Tells if the node contains a Test annotation with an expected exception. - */ - private boolean isExpectAnnotated(ASTMethodDeclaration method) { - return method.getDeclaredAnnotations() - .filter(it -> TypeTestUtil.isA(JUNIT4_CLASS_NAME, it)) - .flatMap(ASTAnnotation::getMembers) - .any(it -> "expected".equals(it.getName())); - - } - - private String getMethodCallNameOrNull(ASTStatementExpression expression) { - if (expression != null) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (name != null) { - return name.getImage(); - } - } - } - return null; - } - - /** - * Tells if the expression is an Hamcrest assert - */ - private boolean isHamcrestAssert(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return "assertThat".equals(img) || "MatcherAssert.assertThat".equals(img); - } - - /** - * Tells if the expression is an assert statement or not. - */ - private boolean isAssertOrFailStatement(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return img != null && (img.startsWith("assert") || img.startsWith("fail") - || img.startsWith("Assert.assert") || img.startsWith("Assert.fail")); - } - - /** - * Tells if the expression is verify statement or not - */ - private boolean isVerifyStatement(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return img != null && (img.startsWith("verify") || img.startsWith("Mockito.verify")); - } - - private boolean isExpectStatement(ASTStatementExpression expression, - Map> expectables) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - ASTPrimaryPrefix primaryPrefix = pe.getFirstChildOfType(ASTPrimaryPrefix.class); - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (!primaryPrefix.usesThisModifier() && name != null) { - String[] parts = name.getImage().split("\\."); - if (parts.length >= 2) { - String varname = parts[0]; - String methodName = parts[1]; - if (expectables.containsKey(varname) && "expect".equals(methodName)) { - return true; - } - } - } else if (primaryPrefix.usesThisModifier()) { - List primarySuffixes = pe.findChildrenOfType(ASTPrimarySuffix.class); - if (primarySuffixes.size() >= 2) { - String varname = primarySuffixes.get(0).getImage(); - String methodName = primarySuffixes.get(1).getImage(); - if (expectables.containsKey(varname) && "expect".equals(methodName)) { - return true; - } - } - } - } - return false; - } - - private boolean isSoftAssertionStatement(ASTStatementExpression expression, - Map variables) { - if (expression != null) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (name != null) { - String img = name.getImage(); - if (!img.contains(".")) { - return false; - } - String[] tokens = img.split("\\."); - String methodName = tokens[1]; - boolean methodIsAssertAll = "assertAll".equals(methodName); - - String varName = tokens[0]; - boolean variableTypeIsSoftAssertion = variables.containsKey(varName) - && TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", variables.get(varName).getDeclaratorId()); - - return methodIsAssertAll && variableTypeIsSoftAssertion; - } - } - } - return false; - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index f4c80dee9e..b62bffb77f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -4,20 +4,16 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.List; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnitMethod; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTBlock; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; -import net.sourceforge.pmd.lang.java.ast.ASTCatchClause; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** * This rule finds code like this: @@ -41,75 +37,36 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; * @author acaplan * */ -public class JUnitUseExpectedRule extends AbstractJUnitRule { +public class JUnitUseExpectedRule extends AbstractJavaRulechainRule { - @Override - public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data) { - boolean inAnnotation = false; - for (int i = 0; i < node.getNumChildren(); i++) { - Node child = node.getChild(i); - if (child instanceof ASTAnnotation) { - ASTName annotationName = child.getFirstDescendantOfType(ASTName.class); - if ("Test".equals(annotationName.getImage())) { - inAnnotation = true; - continue; - } - } - if (child instanceof ASTMethodDeclaration) { - boolean isJUnitMethod = isJUnitMethod((ASTMethodDeclaration) child, data); - if (inAnnotation || isJUnitMethod) { - List found = new ArrayList<>((List) visit((ASTMethodDeclaration) child, data)); - for (Node name : found) { - addViolation(data, name); - } - } - } - inAnnotation = false; - } - - return super.visit(node, data); + public JUnitUseExpectedRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration node, Object data) { - List catches = node.findDescendantsOfType(ASTTryStatement.class); - List found = new ArrayList<>(); - if (catches.isEmpty()) { - return found; - } - for (ASTTryStatement trySt : catches) { - ASTCatchClause cStatement = getCatch(trySt); - if (cStatement != null) { - ASTBlock block = (ASTBlock) cStatement.getChild(1); - if (block.getNumChildren() != 0) { - continue; - } - List blocks = trySt.getChild(0).findDescendantsOfType(ASTBlockStatement.class); - if (blocks.isEmpty()) { - continue; - } - ASTBlockStatement st = blocks.get(blocks.size() - 1); - ASTName name = st.getFirstDescendantOfType(ASTName.class); - if (name != null && st.equals(name.getNthParent(5)) && "fail".equals(name.getImage())) { - found.add(name); - continue; - } - ASTThrowStatement th = st.getFirstDescendantOfType(ASTThrowStatement.class); - if (th != null && st.equals(th.getNthParent(2))) { - found.add(th); - continue; - } - } - } - return found; - } - - private ASTCatchClause getCatch(Node n) { - for (int i = 0; i < n.getNumChildren(); i++) { - if (n.getChild(i) instanceof ASTCatchClause) { - return (ASTCatchClause) n.getChild(i); - } + ASTBlock body = node.getBody(); + if (body != null && isJUnitMethod(node)) { + body.descendants(ASTTryStatement.class) + .filter(this::isWeirdTry) + .forEach(it -> addViolation(data, it)); } return null; } + + private boolean isWeirdTry(ASTTryStatement tryStmt) { + ASTStatement lastStmt = tryStmt.getBody().getLastChild(); + return (lastStmt instanceof ASTThrowStatement + || lastStmt instanceof ASTExpressionStatement && isFailStmt((ASTExpressionStatement) lastStmt)) + && tryStmt.getCatchClauses().any(it -> it.getBody().size() == 0); + } + + private boolean isFailStmt(ASTExpressionStatement stmt) { + if (stmt.getExpr() instanceof ASTMethodCall) { + ASTMethodCall expr = (ASTMethodCall) stmt.getExpr(); + return "fail".equals(expr.getMethodName()); + } + return false; + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 3419dd8d65..009f1a6376 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -4,187 +4,56 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.List; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import java.util.Set; + +import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { +public class LiteralsFirstInComparisonsRule extends AbstractJavaRulechainRule { - private static final String[] COMPARISON_OPS = {".equals", ".equalsIgnoreCase", ".compareTo", ".compareToIgnoreCase", ".contentEquals"}; + private static final Set STRING_COMPARISONS = + setOf("equalsIgnoreCase", + "compareTo", + "compareToIgnoreCase", + "contentEquals"); public LiteralsFirstInComparisonsRule() { - addRuleChainVisit(ASTPrimaryExpression.class); + super(ASTMethodCall.class); } @Override - public Object visit(ASTPrimaryExpression expression, Object data) { - if (violatesLiteralsFirstInComparisonsRule(expression)) { - addViolation(data, expression); + public Object visit(ASTMethodCall call, Object data) { + if ("equals".equals(call.getMethodName()) + && call.getArguments().size() == 1 + && isEqualsObjectAndNotAnOverload(call)) { + checkArgs((RuleContext) data, call); + } else if (STRING_COMPARISONS.contains(call.getMethodName()) + && call.getArguments().size() == 1 + && TypeTestUtil.isDeclaredInClass(String.class, call.getMethodType())) { + checkArgs((RuleContext) data, call); } return data; } - private boolean violatesLiteralsFirstInComparisonsRule(ASTPrimaryExpression expression) { - return !hasStringLiteralFirst(expression) && isNullableComparisonWithStringLiteral(expression); - } - - private boolean hasStringLiteralFirst(ASTPrimaryExpression expression) { - ASTPrimaryPrefix primaryPrefix = expression.getFirstChildOfType(ASTPrimaryPrefix.class); - ASTLiteral firstLiteral = primaryPrefix.getFirstDescendantOfType(ASTLiteral.class); - return firstLiteral != null && firstLiteral.isStringLiteral(); - } - - private boolean isNullableComparisonWithStringLiteral(ASTPrimaryExpression expression) { - String opName = getOperationName(expression); - ASTPrimarySuffix argsSuffix = getSuffixOfArguments(expression); - return opName != null && argsSuffix != null && isStringLiteralComparison(opName, argsSuffix) - && isNotWithinNullComparison(expression); - } - - private String getOperationName(ASTPrimaryExpression primaryExpression) { - return isMethodsChain(primaryExpression) ? getOperationNameBySuffix(primaryExpression) - : getOperationNameByPrefix(primaryExpression); - } - - private boolean isMethodsChain(ASTPrimaryExpression primaryExpression) { - return primaryExpression.getNumChildren() > 2; - } - - private String getOperationNameBySuffix(ASTPrimaryExpression primaryExpression) { - ASTPrimarySuffix opAsSuffix = getPrimarySuffixAtIndexFromEnd(primaryExpression, 1); - if (opAsSuffix != null) { - String opName = opAsSuffix.getImage(); // name of pattern "operation" - return "." + opName; + private boolean isEqualsObjectAndNotAnOverload(ASTMethodCall call) { + if (call.getOverloadSelectionInfo().isFailed()) { + return true; // failed selection is considered probably equals(Object) } - return null; + return call.getMethodType().getFormalParameters().equals(listOf(call.getTypeSystem().OBJECT)); } - private String getOperationNameByPrefix(ASTPrimaryExpression primaryExpression) { - ASTPrimaryPrefix opAsPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - if (opAsPrefix != null) { - ASTName opName = opAsPrefix.getFirstChildOfType(ASTName.class); // name of pattern "*.operation" - return opName != null ? opName.getImage() : null; + private void checkArgs(RuleContext ctx, ASTMethodCall call) { + ASTExpression arg = call.getArguments().get(0); + ASTExpression qualifier = call.getQualifier(); + if (!(qualifier instanceof ASTStringLiteral) && arg instanceof ASTStringLiteral) { + addViolation(ctx, call); } - return null; - } - - private ASTPrimarySuffix getSuffixOfArguments(ASTPrimaryExpression primaryExpression) { - return getPrimarySuffixAtIndexFromEnd(primaryExpression, 0); - } - - private ASTPrimarySuffix getPrimarySuffixAtIndexFromEnd(ASTPrimaryExpression primaryExpression, int indexFromEnd) { - List primarySuffixes = primaryExpression.findChildrenOfType(ASTPrimarySuffix.class); - if (!primarySuffixes.isEmpty()) { - int suffixIndex = primarySuffixes.size() - 1 - indexFromEnd; - return primarySuffixes.get(suffixIndex); - } - return null; - } - - private boolean isStringLiteralComparison(String opName, ASTPrimarySuffix argsSuffix) { - return isComparisonOperation(opName) && isSingleStringLiteralArgument(argsSuffix); - } - - private boolean isComparisonOperation(String op) { - for (String comparisonOp : COMPARISON_OPS) { - if (op.endsWith(comparisonOp)) { - return true; - } - } - return false; - } - - /* - * This corresponds to the following XPath expression: - * (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()]) - * and - * ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) - */ - private boolean isSingleStringLiteralArgument(ASTPrimarySuffix primarySuffix) { - return isSingleArgumentSuffix(primarySuffix) && isStringLiteralFirstArgumentOfSuffix(primarySuffix); - } - - private boolean isSingleArgumentSuffix(ASTPrimarySuffix primarySuffix) { - return primarySuffix.getArgumentCount() == 1; - } - - private boolean isStringLiteralFirstArgumentOfSuffix(ASTPrimarySuffix primarySuffix) { - try { - JavaNode firstArg = getFirstArgument(primarySuffix); - return isStringLiteral(firstArg); - } catch (NullPointerException e) { - return false; - } - } - - private JavaNode getFirstArgument(ASTPrimarySuffix primarySuffix) { - ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); - ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); - ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); - ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - return primaryPrefix.getFirstChildOfType(ASTLiteral.class); - } - - private boolean isStringLiteral(JavaNode node) { - if (node instanceof ASTLiteral) { - ASTLiteral literal = (ASTLiteral) node; - return literal.isStringLiteral(); - } - return false; - } - - private boolean isNotWithinNullComparison(ASTPrimaryExpression node) { - return !isWithinNullComparison(node); - } - - /* - * Expression/ConditionalAndExpression//EqualityExpression(@Image='!=']//NullLiteral - * Expression/ConditionalOrExpression//EqualityExpression(@Image='==']//NullLiteral - */ - private boolean isWithinNullComparison(ASTPrimaryExpression node) { - for (ASTExpression parentExpr : node.getParentsOfType(ASTExpression.class)) { - if (isNullComparison(parentExpr)) { - return true; - } - } - return false; - } - - private boolean isNullComparison(ASTExpression expression) { - return isAndNotNullComparison(expression) || isOrNullComparison(expression); - } - - private boolean isAndNotNullComparison(ASTExpression expression) { - ASTConditionalAndExpression andExpression = expression - .getFirstChildOfType(ASTConditionalAndExpression.class); - return andExpression != null && hasEqualityExpressionWithNullLiteral(andExpression, "!="); - } - - private boolean isOrNullComparison(ASTExpression expression) { - ASTConditionalOrExpression orExpression = expression - .getFirstChildOfType(ASTConditionalOrExpression.class); - return orExpression != null && hasEqualityExpressionWithNullLiteral(orExpression, "=="); - } - - private boolean hasEqualityExpressionWithNullLiteral(JavaNode node, String equalityOp) { - ASTEqualityExpression equalityExpression = node.getFirstDescendantOfType(ASTEqualityExpression.class); - if (equalityExpression != null && equalityExpression.hasImageEqualTo(equalityOp)) { - return equalityExpression.hasDescendantOfType(ASTNullLiteral.class); - } - return false; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java index 2ba5f6ff58..a7f3dbef26 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java @@ -55,7 +55,7 @@ public class MissingOverrideRule extends AbstractJavaRule { RelevantMethodSet relevantMethods = new RelevantMethodSet(node.getSymbol()); - for (ASTMethodDeclaration methodDecl : node.getDeclarations().filterIs(ASTMethodDeclaration.class)) { + for (ASTMethodDeclaration methodDecl : node.getDeclarations(ASTMethodDeclaration.class)) { relevantMethods.addIfRelevant(methodDecl); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java deleted file mode 100644 index 9c35fb226b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -public class PositionLiteralsFirstInCaseInsensitiveComparisonsRule extends AbstractPositionLiteralsFirstInComparisons { - - public PositionLiteralsFirstInCaseInsensitiveComparisonsRule() { - super(".equalsIgnoreCase"); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java deleted file mode 100644 index 7ce54ca3df..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -public class PositionLiteralsFirstInComparisonsRule extends AbstractPositionLiteralsFirstInComparisons { - - public PositionLiteralsFirstInComparisonsRule() { - super(".equals"); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java index e1b8bf7d89..9a3bc18c97 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java @@ -4,31 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; - import java.util.Collection; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTResultType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; -import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -import net.sourceforge.pmd.util.CollectionUtil; /** * Detect structures like "foo.size() == 0" and suggest replacing them with @@ -36,109 +18,25 @@ import net.sourceforge.pmd.util.CollectionUtil; * * @author Jason Bennett */ -public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { +public class UseCollectionIsEmptyRule extends AbstractJavaRulechainRule { - @Override - public boolean appliesToClassName(String name) { - return CollectionUtil.isCollectionType(name, true); - } - - /** - * Determine if we're dealing with .size method - * - * @param occ - * The name occurrence - * @return true if it's .size, else false - */ - @Override - public boolean isTargetMethod(JavaNameOccurrence occ) { - return occ.getNameForWhichThisIsAQualifier() != null - && occ.getLocation().getImage().endsWith(".size"); + public UseCollectionIsEmptyRule() { + super(ASTMethodCall.class); } @Override - public Map> getComparisonTargets() { - List zeroAndOne = asList("0", "1"); - List zero = singletonList("0"); - Map> rules = new HashMap<>(); - rules.put("<", zeroAndOne); - rules.put(">", zero); - rules.put("==", zero); - rules.put("!=", zero); - rules.put(">=", zeroAndOne); - rules.put("<=", zero); - return rules; - } - - @Override - public Object visit(ASTPrimarySuffix node, Object data) { - if (isSizeMethodCall(node) && isCalledOnCollection(node)) { - Node expr = node.getParent().getParent(); - checkNodeAndReport(data, node, expr); - } - return data; - } - - private boolean isSizeMethodCall(ASTPrimarySuffix primarySuffix) { - String calledMethodName = primarySuffix.getImage(); - return calledMethodName != null && calledMethodName.endsWith("size"); - } - - private boolean isCalledOnCollection(ASTPrimarySuffix primarySuffix) { - ASTClassOrInterfaceType calledOnType = getTypeOfVariable(primarySuffix); - if (calledOnType == null) { - calledOnType = getTypeOfMethodCall(primarySuffix); - } - return TypeTestUtil.isA(Collection.class, calledOnType); - } - - private ASTClassOrInterfaceType getTypeOfVariable(ASTPrimarySuffix primarySuffix) { - ASTPrimaryExpression primaryExpression = primarySuffix.getFirstParentOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix varPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - if (prefixWithNoModifiers(varPrefix)) { - return varPrefix.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - } - String varName = getVariableNameBySuffix(primaryExpression); - return varName != null ? getTypeOfVariableByName(varName, primaryExpression) : null; - } - - private boolean prefixWithNoModifiers(ASTPrimaryPrefix primaryPrefix) { - return !primaryPrefix.usesSuperModifier() && !primaryPrefix.usesThisModifier(); - } - - private String getVariableNameBySuffix(ASTPrimaryExpression primaryExpression) { - ASTPrimarySuffix varSuffix = primaryExpression - .getFirstChildOfType(ASTPrimarySuffix.class); - return varSuffix.getImage(); - } - - private ASTClassOrInterfaceType getTypeOfVariableByName(String varName, ASTPrimaryExpression expr) { - ASTClassOrInterfaceBody classBody = expr.getFirstParentOfType(ASTClassOrInterfaceBody.class); - List varDeclarators = classBody.findDescendantsOfType(ASTVariableDeclarator.class); - for (ASTVariableDeclarator varDeclarator : varDeclarators) { - if (varDeclarator.getName().equals(varName)) { - return varDeclarator.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - } + public Object visit(ASTMethodCall call, Object data) { + if ((TypeTestUtil.isA(Collection.class, call.getQualifier()) + || TypeTestUtil.isA(Map.class, call.getQualifier())) + && isSizeZeroCheck(call)) { + addViolation(data, call); } return null; } - private ASTClassOrInterfaceType getTypeOfMethodCall(ASTPrimarySuffix node) { - ASTClassOrInterfaceType type = null; - ASTName methodName = node.getParent().getFirstChildOfType(ASTPrimaryPrefix.class) - .getFirstChildOfType(ASTName.class); - if (methodName != null) { - ClassScope classScope = node.getScope().getEnclosingScope(ClassScope.class); - Map> methods = classScope.getMethodDeclarations(); - for (Map.Entry> e : methods.entrySet()) { - if (e.getKey().getName().equals(methodName.getImage())) { - type = e.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class) - .getFirstChildOfType(ASTResultType.class) - .getFirstDescendantOfType(ASTClassOrInterfaceType.class); - break; - } - } - } - return type; + private static boolean isSizeZeroCheck(ASTMethodCall call) { + return "size".equals(call.getMethodName()) + && call.getArguments().size() == 0 + && JavaRuleUtil.isZeroChecked(call); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java index fc1d1e3562..6027e2ebea 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -24,11 +24,16 @@ import net.sourceforge.pmd.util.StringUtil.CaseConvention; * @author Clément Fournier * @since 6.5.0 */ -abstract class AbstractNamingConventionRule extends AbstractJavaRule { +abstract class AbstractNamingConventionRule extends AbstractJavaRulechainRule { static final String CAMEL_CASE = "[a-z][a-zA-Z0-9]*"; static final String PASCAL_CASE = "[A-Z][a-zA-Z0-9]*"; + @SafeVarargs + protected AbstractNamingConventionRule(Class first, Class... visits) { + super(first, visits); + } + /** The argument is interpreted as the display name, and is converted to camel case to get the property name. */ RegexPropertyBuilder defaultProp(String displayName) { return defaultProp(CaseConvention.SPACE_SEPARATED.convertTo(CaseConvention.CAMEL_CASE, displayName), displayName); @@ -49,9 +54,7 @@ abstract class AbstractNamingConventionRule extends Abstract abstract String kindDisplayName(T node, PropertyDescriptor descriptor); /** Extracts the name that should be pattern matched. */ - String nameExtractor(T node) { - return node.getImage(); - } + abstract String nameExtractor(T node); void checkMatches(T node, PropertyDescriptor regex, Object data) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java index ee06ee9951..0f4ea4caf1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java @@ -7,10 +7,11 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.Arrays; import java.util.Collection; +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; import net.sourceforge.pmd.lang.java.rule.design.UseUtilityClassRule; @@ -39,33 +40,18 @@ public class AtLeastOneConstructorRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(final ASTClassOrInterfaceDeclaration node, final Object data) { // Ignore interfaces / static classes / classes that have a constructor / classes ignored through annotations - if (node.isInterface() || node.isStatic() || node.hasDescendantOfType(ASTConstructorDeclaration.class) - || hasIgnoredAnnotation(node)) { + if (!node.isRegularClass() + || node.isStatic() + || node.getDeclarations().any(it -> it instanceof ASTConstructorDeclaration) + || hasIgnoredAnnotation(node)) { return data; } - boolean atLeastOneMember = false; - - // Do we have any non-static methods? - for (final ASTMethodDeclaration m : node.findDescendantsOfType(ASTMethodDeclaration.class)) { - if (!m.isStatic()) { - addViolation(data, node); - return data; - } - atLeastOneMember = true; - } - - // .. or fields? - for (final ASTFieldDeclaration f : node.findDescendantsOfType(ASTFieldDeclaration.class)) { - if (!f.isStatic()) { - addViolation(data, node); - return data; - } - atLeastOneMember = true; - } - - // Class has no declared members - if (!atLeastOneMember) { + NodeStream members = node.getDeclarations() + .filterIs(AccessNode.class) + .filterNot(it -> it instanceof ASTAnyTypeDeclaration); + if (members.isEmpty() || members.any(it -> !it.isStatic())) { + // Do we have any non-static members? addViolation(data, node); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java deleted file mode 100644 index 84aeb53a1f..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class AvoidDollarSignsRule extends AbstractJavaRule { - - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - - @Override - public Object visit(ASTMethodDeclarator node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index b7f547d010..73f57c8050 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -8,14 +8,11 @@ import java.util.regex.Pattern; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -33,82 +30,24 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - return isUtilityClass(node) ? "utility class" : PrettyPrintingUtil.kindName(node); + return JavaRuleUtil.isUtilityClass(node) ? "utility class" : PrettyPrintingUtil.kindName(node); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java index b0ef3262e1..c6bd6593e4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java @@ -6,18 +6,14 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.UnaryOp; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -54,29 +50,24 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; * } * */ -public class ConfusingTernaryRule extends AbstractJavaRule { - private static PropertyDescriptor ignoreElseIfProperty = booleanProperty("ignoreElseIf").desc("Ignore conditions with an else-if case").defaultValue(false).build(); +public class ConfusingTernaryRule extends AbstractJavaRulechainRule { + + private static final PropertyDescriptor IGNORE_ELSE_IF = booleanProperty("ignoreElseIf").desc("Ignore conditions with an else-if case").defaultValue(false).build(); public ConfusingTernaryRule() { - super(); - definePropertyDescriptor(ignoreElseIfProperty); + super(ASTIfStatement.class, ASTConditionalExpression.class); + definePropertyDescriptor(IGNORE_ELSE_IF); } @Override public Object visit(ASTIfStatement node, Object data) { // look for "if (match) ..; else .." - if (node.getNumChildren() == 3) { - JavaNode inode = node.getChild(0); - if (inode instanceof ASTExpression && inode.getNumChildren() == 1) { - JavaNode jnode = inode.getChild(0); - if (isMatch(jnode)) { - - if (!getProperty(ignoreElseIfProperty) - || !(node.getChild(2).getChild(0) instanceof ASTIfStatement) - && !(node.getParent().getParent() instanceof ASTIfStatement)) { - addViolation(data, node); - } - } + if (node.getNumChildren() == 3 + && isMatch(node.getCondition())) { + if (!getProperty(IGNORE_ELSE_IF) + || !(node.getElseBranch() instanceof ASTIfStatement) + && !(node.getParent() instanceof ASTIfStatement)) { + addViolation(data, node); } } return super.visit(node, data); @@ -85,76 +76,42 @@ public class ConfusingTernaryRule extends AbstractJavaRule { @Override public Object visit(ASTConditionalExpression node, Object data) { // look for "match ? .. : .." - if (node.getNumChildren() > 0) { - JavaNode inode = node.getChild(0); - if (isMatch(inode)) { - addViolation(data, node); - } + if (isMatch(node.getCondition())) { + addViolation(data, node); } return super.visit(node, data); } // recursive! - private static boolean isMatch(JavaNode node) { - node = unwrapParentheses(node); + private static boolean isMatch(ASTExpression node) { return isUnaryNot(node) || isNotEquals(node) || isConditionalWithAllMatches(node); } - private static boolean isUnaryNot(Node node) { + private static boolean isUnaryNot(ASTExpression node) { // look for "!x" - return node instanceof ASTUnaryExpressionNotPlusMinus && "!".equals(node.getImage()); + return node instanceof ASTUnaryExpression + && ((ASTUnaryExpression) node).getOperator().equals(UnaryOp.NEGATION); } - private static boolean isNotEquals(Node node) { + private static boolean isNotEquals(ASTExpression node) { // look for "x != y" - return node instanceof ASTEqualityExpression && "!=".equals(node.getImage()); + return node instanceof ASTInfixExpression + && ((ASTInfixExpression) node).getOperator().equals(BinaryOp.NE); } - private static boolean isConditionalWithAllMatches(JavaNode node) { + private static boolean isConditionalWithAllMatches(ASTExpression node) { // look for "match && match" or "match || match" - if (!(node instanceof ASTConditionalAndExpression) && !(node instanceof ASTConditionalOrExpression)) { - return false; - } - int n = node.getNumChildren(); - if (n <= 0) { - return false; - } - for (int i = 0; i < n; i++) { - JavaNode inode = node.getChild(i); - // recurse! - if (!isMatch(inode)) { + if (node instanceof ASTInfixExpression) { + ASTInfixExpression infix = (ASTInfixExpression) node; + if (infix.getOperator() != BinaryOp.CONDITIONAL_AND + && infix.getOperator() != BinaryOp.CONDITIONAL_OR) { return false; } - } - // all match - return true; - } - /** - * Extracts the outermost node that is not a parenthesized - * expression. - * - * @deprecated This is internal API, because it will be removed in PMD 7. - * In PMD 7 there are no additional layers for parentheses in the Java tree. - */ - @Deprecated - public static JavaNode unwrapParentheses(final JavaNode top) { - JavaNode node = top; - // look for "(match)" - if (!(node instanceof ASTPrimaryExpression) || node.getNumChildren() != 1) { - return top; + return isMatch(infix.getLeftOperand()) + && isMatch(infix.getRightOperand()); } - node = node.getChild(0); - if (!(node instanceof ASTPrimaryPrefix) || node.getNumChildren() != 1) { - return top; - } - node = node.getChild(0); - if (!(node instanceof ASTExpression) || node.getNumChildren() != 1) { - return top; - } - node = node.getChild(0); - // recurse to unwrap another layer if possible - return unwrapParentheses(node); + return false; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java deleted file mode 100644 index 9afbe90da8..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class DontImportJavaLangRule extends AbstractJavaRule { - private static final String IMPORT_JAVA_LANG = "java.lang"; - - @Override - public Object visit(ASTImportDeclaration node, Object data) { - - if (node.isStatic()) { - return data; - } - - String img = node.getChild(0).getImage(); - if (img.startsWith(IMPORT_JAVA_LANG)) { - if (!IMPORT_JAVA_LANG.equals(img)) { - if (img.indexOf('.', IMPORT_JAVA_LANG.length() + 1) != -1 || node.isImportOnDemand()) { - // Importing from a subpackage / inner class - return data; - } - } - addViolation(data, node); - } - return data; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java index 904b7dbdd4..ff5a778836 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java @@ -4,6 +4,10 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; +import static net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PUBLIC; +import static net.sourceforge.pmd.lang.java.ast.JModifier.FINAL; +import static net.sourceforge.pmd.lang.java.ast.JModifier.STATIC; + import java.util.List; import java.util.regex.Pattern; @@ -39,6 +43,7 @@ public class FieldNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - ASTFieldDeclaration field = (ASTFieldDeclaration) node.getNthParent(2); - if (field.isFinal() && field.isStatic()) { - return field.isPublic() ? "public constant" : "constant"; - } else if (field.isFinal()) { + boolean isFinal = node.hasModifiers(FINAL); + boolean isStatic = node.hasModifiers(STATIC); + + if (isFinal && isStatic) { + return node.getVisibility() == V_PUBLIC ? "public constant" : "constant"; + } else if (isFinal) { return "final field"; - } else if (field.isStatic()) { + } else if (isStatic) { return "static field"; } else { return "field"; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java index 2ecbfb4a0c..cd57187c70 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java @@ -5,14 +5,9 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - import java.util.regex.Pattern; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -35,17 +30,13 @@ public final class FormalParameterNamingConventionsRule extends AbstractNamingCo public FormalParameterNamingConventionsRule() { + super(ASTVariableDeclaratorId.class); definePropertyDescriptor(formalParamRegex); definePropertyDescriptor(finalFormalParamRegex); definePropertyDescriptor(lambdaParamRegex); definePropertyDescriptor(explicitLambdaParamRegex); } - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(setOf(ASTVariableDeclaratorId.class)); - } - @Override public Object visit(ASTVariableDeclaratorId node, Object data) { @@ -64,6 +55,10 @@ public final class FormalParameterNamingConventionsRule extends AbstractNamingCo return CAMEL_CASE; } + @Override + String nameExtractor(ASTVariableDeclaratorId node) { + return node.getName(); + } @Override String kindDisplayName(ASTVariableDeclaratorId node, PropertyDescriptor descriptor) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java index cc4ea66a10..d4b8a2adb4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java @@ -27,6 +27,8 @@ public final class LocalVariableNamingConventionsRule extends AbstractNamingConv public LocalVariableNamingConventionsRule() { + super(ASTVariableDeclaratorId.class); + definePropertyDescriptor(localVarRegex); definePropertyDescriptor(finalVarRegex); definePropertyDescriptor(exceptionBlockParameterRegex); @@ -55,6 +57,12 @@ public final class LocalVariableNamingConventionsRule extends AbstractNamingConv } + @Override + String nameExtractor(ASTVariableDeclaratorId node) { + return node.getName(); + } + + @Override String kindDisplayName(ASTVariableDeclaratorId node, PropertyDescriptor descriptor) { if (node.isExceptionBlockParameter()) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java index f921900586..b25e691c11 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java @@ -4,19 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; -import static net.sourceforge.pmd.lang.ast.NodeStream.asInstanceOf; - import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -34,6 +28,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule annotationProperties = node.findDescendantsOfType(ASTMemberValuePair.class); - // all that needs to be done is check to if there's a single property in the annotation and if if that property is value - // then it's a violation and it should be resolved. - if (annotationProperties.size() == 1 && "value".equals(annotationProperties.get(0).getImage())) { - addViolation(data, node); - } - - return data; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java index 37e420ee19..f3e9e4a253 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java @@ -8,14 +8,18 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation; -import net.sourceforge.pmd.lang.java.ast.ASTNameList; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * This rule detects when a constructor is not necessary; @@ -24,6 +28,11 @@ import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; */ public class UnnecessaryConstructorRule extends AbstractIgnoredAnnotationRule { + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTEnumDeclaration.class, ASTClassOrInterfaceDeclaration.class); + } + @Override protected Collection defaultSuppressionAnnotations() { return Collections.singletonList("javax.inject.Inject"); @@ -31,72 +40,54 @@ public class UnnecessaryConstructorRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - - ASTConstructorDeclaration cons = node.getFirstDescendantOfType(ASTConstructorDeclaration.class); - if (isExplicitDefaultConstructor(node) - && haveSameAccessModifier(node, cons)) { - addViolation(data, cons); + if (node.isRegularClass()) { + checkClassOrEnum(node, data); } - - return super.visit(node, data); + return data; } @Override public Object visit(ASTEnumDeclaration node, Object data) { + checkClassOrEnum(node, data); + return data; + } - ASTConstructorDeclaration cons = node.getFirstDescendantOfType(ASTConstructorDeclaration.class); - if (isExplicitDefaultConstructor(node) && cons.isPrivate()) { - addViolation(data, cons); + private void checkClassOrEnum(ASTAnyTypeDeclaration node, Object data) { + List ctors = node.getDeclarations(ASTConstructorDeclaration.class).take(2).toList(); + if (ctors.size() == 1 && isExplicitDefaultConstructor(node, ctors.get(0))) { + addViolation(data, ctors.get(0)); + } + } + + + private boolean isExplicitDefaultConstructor(ASTAnyTypeDeclaration declarator, ASTConstructorDeclaration ctor) { + return ctor.getArity() == 0 + && !hasIgnoredAnnotation(ctor) + && hasDefaultCtorVisibility(declarator, ctor) + && isEmptyBlock(ctor.getBody()) + && ctor.getThrowsList() == null; + } + + private boolean isEmptyBlock(ASTBlock body) { + if (body.size() == 0) { + return true; + } else if (body.size() == 1) { + ASTStatement stmt = body.get(0); + if (stmt instanceof ASTExplicitConstructorInvocation) { + ASTExplicitConstructorInvocation superCall = (ASTExplicitConstructorInvocation) stmt; + return superCall.isSuper() && superCall.getArgumentCount() == 0; + } } - return super.visit(node, data); + return false; } - /** - * Returns {@code true} if the node has only one {@link ASTConstructorDeclaration} - * child node and the constructor has empty body or simply invokes the superclass - * constructor with no arguments. - * - * @param node the node to check - */ - private boolean isExplicitDefaultConstructor(Node node) { - - List nodes - = node.findDescendantsOfType(ASTConstructorDeclaration.class); - - if (nodes.size() != 1) { - return false; + private boolean hasDefaultCtorVisibility(ASTAnyTypeDeclaration node, ASTConstructorDeclaration cons) { + if (node instanceof ASTClassOrInterfaceDeclaration) { + return node.getVisibility() == cons.getVisibility(); + } else if (node instanceof ASTEnumDeclaration) { + return cons.getVisibility() == Visibility.V_PRIVATE; } - - ASTConstructorDeclaration cdnode = nodes.get(0); - - return cdnode.getArity() == 0 && !hasIgnoredAnnotation(cdnode) - && !cdnode.hasDescendantOfType(ASTBlockStatement.class) && !cdnode.hasDescendantOfType(ASTNameList.class) - && hasDefaultConstructorInvocation(cdnode); - } - - /** - * Returns {@code true} if the constructor simply invokes superclass constructor - * with no arguments or doesn't invoke any constructor, otherwise {@code false}. - * - * @param cons the node to check - */ - private boolean hasDefaultConstructorInvocation(ASTConstructorDeclaration cons) { - ASTExplicitConstructorInvocation inv = cons.getFirstChildOfType(ASTExplicitConstructorInvocation.class); - return inv == null || inv.isSuper() && inv.getArgumentCount() == 0; - } - - /** - * Returns {@code true} if access modifier of constructor is same as class's, - * otherwise {@code false}. - * - * @param node the class declaration node - * @param cons the constructor declaration node - */ - private boolean haveSameAccessModifier(ASTClassOrInterfaceDeclaration node, ASTConstructorDeclaration cons) { - return node.isPrivate() && cons.isPrivate() - || node.isProtected() && cons.isProtected() - || node.isPublic() && cons.isPublic() - || node.isPackagePrivate() && cons.isPackagePrivate(); + return false; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java deleted file mode 100644 index 280e1ea57b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.java.ast.ASTCatchClause; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -/** - * Finds catch statements containing throwable as the - * type definition. - * - * @author Trond Andersen - */ -public class AvoidCatchingThrowableRule extends AbstractJavaRule { - - @Override - public Object visit(ASTCatchClause catchStatement, Object data) { - for (@NonNull ASTClassOrInterfaceType caughtException : catchStatement.getParameter().getAllExceptionTypes()) { - if (Throwable.class.equals(caughtException.getType())) { - addViolation(data, catchStatement); - } - } - return super.visit(catchStatement, data); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java deleted file mode 100644 index be73e9f279..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class AvoidMultipleUnaryOperatorsRule extends AbstractJavaRule { - - public AvoidMultipleUnaryOperatorsRule() { - super.addRuleChainVisit(ASTUnaryExpression.class); - super.addRuleChainVisit(ASTUnaryExpressionNotPlusMinus.class); - } - - @Override - public Object visit(ASTUnaryExpression node, Object data) { - checkUnaryDescendent(node, data); - return data; - } - - @Override - public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) { - checkUnaryDescendent(node, data); - return data; - } - - private void checkUnaryDescendent(Node node, Object data) { - boolean match = false; - if (node.getNumChildren() == 1) { - Node child = node.getChild(0); - if (child instanceof ASTUnaryExpression || child instanceof ASTUnaryExpressionNotPlusMinus) { - match = true; - } else if (child instanceof ASTPrimaryExpression) { - Node primaryExpression = child; - // Skip down PrimaryExpression/PrimaryPrefix/Expression chains - // created by parentheses - while (true) { - if (primaryExpression.getNumChildren() == 1 - && primaryExpression.getChild(0) instanceof ASTPrimaryPrefix - && primaryExpression.getChild(0).getNumChildren() == 1 - && primaryExpression.getChild(0).getChild(0) instanceof ASTExpression - && primaryExpression.getChild(0).getChild(0).getNumChildren() == 1) { - Node candidate = primaryExpression.getChild(0).getChild(0).getChild(0); - if (candidate instanceof ASTUnaryExpression - || candidate instanceof ASTUnaryExpressionNotPlusMinus) { - match = true; - break; - } else if (candidate instanceof ASTPrimaryExpression) { - primaryExpression = candidate; - continue; - } else { - break; - } - } else { - break; - } - } - } - } - - if (match) { - addViolation(data, node); - } - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java index 495e9bf31c..3ef6d2c387 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java @@ -4,22 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTBlock; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTExtendsList; -import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * The method clone() should only be implemented if the class implements the @@ -30,139 +21,43 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; * * @author acaplan */ -public class CloneMethodMustImplementCloneableRule extends AbstractJavaRule { +public class CloneMethodMustImplementCloneableRule extends AbstractJavaRulechainRule { - @Override - public Object visit(final ASTClassOrInterfaceDeclaration node, final Object data) { - if (extendsOrImplementsCloneable(node)) { - return data; - } - return super.visit(node, data); - } - - private boolean extendsOrImplementsCloneable(final ASTClassOrInterfaceDeclaration node) { - if (node.getType() != null) { - return Cloneable.class.isAssignableFrom(node.getType()); - } - - // From this point on, this is a best effort, the auxclasspath is incomplete. - - // TODO : Should we really care about this? - // Shouldn't the type resolver / symbol table report missing classes and the user - // know results are dependent on running under proper arguments? - final ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class); - if (impl != null) { - for (int ix = 0; ix < impl.getNumChildren(); ix++) { - final Node child = impl.getChild(ix); - - if (child.getClass() != ASTClassOrInterfaceType.class) { - continue; - } - - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) child; - if (type.getType() == null) { - if ("Cloneable".equals(type.getImage())) { - return true; - } - } else if (Cloneable.class.isAssignableFrom(type.getType())) { - return true; - } - } - } - - if (node.getNumChildren() != 0 && node.getChild(0) instanceof ASTExtendsList) { - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.getChild(0).getChild(0); - final Class clazz = type.getType(); - if (clazz != null) { - return Cloneable.class.isAssignableFrom(clazz); - } - } - - return false; + public CloneMethodMustImplementCloneableRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(final ASTMethodDeclaration node, final Object data) { - // Is this a clone method? - final ASTMethodDeclarator methodDeclarator = node.getFirstChildOfType(ASTMethodDeclarator.class); - if (!isCloneMethod(methodDeclarator)) { + if (!isCloneMethod(node)) { + return data; + } + ASTBlock body = node.getBody(); + if (body != null && justThrowsCloneNotSupported(body)) { return data; } - // Is the clone method just throwing CloneNotSupportedException? - final ASTClassOrInterfaceDeclaration classOrInterface = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); - if (classOrInterface != null //Don't analyze enums, which cannot subclass clone() - && (node.isFinal() || classOrInterface.isFinal())) { - if (node.findDescendantsOfType(ASTBlock.class).size() == 1) { - final List blocks = node.findDescendantsOfType(ASTBlockStatement.class); - if (blocks.size() == 1) { - final ASTBlockStatement block = blocks.get(0); - final ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - if (type != null && type.getType() != null && type.getNthParent(9).equals(node) - && type.getType().equals(CloneNotSupportedException.class)) { - return data; - } else if (type != null && type.getType() == null - && "CloneNotSupportedException".equals(type.getImage())) { - return data; - } - } - } + ASTAnyTypeDeclaration type = node.getEnclosingType(); + if (type instanceof ASTClassOrInterfaceDeclaration && !TypeTestUtil.isA(Cloneable.class, type)) { + // Nothing can save us now + addViolation(data, node); } - - // TODO : Should we really care about this? It can only happen with an incomplete auxclasspath - if (classOrInterface != null && classOrInterface.getType() == null) { - // Now check other whether implemented or extended classes are defined inside the same file - final Set classesNames = determineTopLevelCloneableClasses(classOrInterface); - - final ASTImplementsList implementsList = classOrInterface.getFirstChildOfType(ASTImplementsList.class); - if (implementsList != null) { - final List types = implementsList.findChildrenOfType(ASTClassOrInterfaceType.class); - for (final ASTClassOrInterfaceType t : types) { - if (classesNames.contains(t.getImage())) { - return data; - } - } - } - - final ASTExtendsList extendsList = classOrInterface.getFirstChildOfType(ASTExtendsList.class); - if (extendsList != null) { - final ASTClassOrInterfaceType type = extendsList.getFirstChildOfType(ASTClassOrInterfaceType.class); - if (classesNames.contains(type.getImage())) { - return data; - } - } - } - - // Nothing can save us now - addViolation(data, node); return data; } - /** - * Determines all the class/interface declarations inside this compilation - * unit, which implement Cloneable - * - * @param currentClass - * the node of the class, that is currently analyzed (inside this - * compilation unit) - * @return a Set of class/interface names - */ - private Set determineTopLevelCloneableClasses(final ASTClassOrInterfaceDeclaration currentClass) { - final List classes = currentClass.getFirstParentOfType(ASTCompilationUnit.class) - .findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); - final Set classesNames = new HashSet<>(); - for (final ASTClassOrInterfaceDeclaration c : classes) { - if (!Objects.equals(c, currentClass) && extendsOrImplementsCloneable(c)) { - classesNames.add(c.getImage()); - } - } - return classesNames; - } - - public boolean isCloneMethod(final ASTMethodDeclarator method) { - if (!"clone".equals(method.getImage())) { + private static boolean justThrowsCloneNotSupported(ASTBlock body) { + if (body.size() != 1) { return false; } - return method.getParameterCount() == 0; + return body.getChild(0) + .asStream() + .filterIs(ASTThrowStatement.class) + .map(ASTThrowStatement::getExpr) + .filter(it -> TypeTestUtil.isA(CloneNotSupportedException.class, it)) + .nonEmpty(); + } + + private static boolean isCloneMethod(final ASTMethodDeclaration method) { + return "clone".equals(method.getName()) && method.getFormalParameters().size() == 0; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java deleted file mode 100644 index 4b77168b9b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; - -public class CompareObjectsWithEqualsRule extends AbstractJavaRule { - - private boolean hasName(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTName; - } - - /** - * Indicate whether this node is allocating a new object. - * - * @param n - * node that might be allocating a new object - * @return true if child 0 is an AllocationExpression - */ - private boolean isAllocation(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTAllocationExpression - && n.getParent().getNumChildren() == 1; - } - - @Override - public Object visit(ASTEqualityExpression node, Object data) { - Node c0 = node.getChild(0).getChild(0); - Node c1 = node.getChild(1).getChild(0); - - // If either side is allocating a new object, there's no way an - // equals expression is correct - if (isAllocation(c0) || isAllocation(c1)) { - addViolation(data, node); - return data; - } - - // skip if either child is not a simple name - if (!hasName(c0) || !hasName(c1)) { - return data; - } - - // skip if either is a qualified name - if (isQualifiedName(c0.getChild(0).getImage()) || isQualifiedName(c1.getChild(0).getImage())) { - return data; - } - - // skip if either is part of a qualified name - if (isPartOfQualifiedName(node.getChild(0)) || isPartOfQualifiedName(node.getChild(1))) { - return data; - } - - // skip static initializers... missing some cases here - if (!node.getParentsOfType(ASTInitializer.class).isEmpty()) { - return data; - } - - ASTName n0 = (ASTName) c0.getChild(0); - ASTName n1 = (ASTName) c1.getChild(0); - - if (n0.getNameDeclaration() instanceof VariableNameDeclaration - && n1.getNameDeclaration() instanceof VariableNameDeclaration) { - VariableNameDeclaration nd0 = (VariableNameDeclaration) n0.getNameDeclaration(); - VariableNameDeclaration nd1 = (VariableNameDeclaration) n1.getNameDeclaration(); - - // skip array dereferences... this misses some cases - // FIXME catch comparisons btwn array elements of reference types - if (nd0.isArray() || nd1.isArray()) { - return data; - } - - if (nd0.isReferenceType() && nd1.isReferenceType()) { - ASTReferenceType type0 = ((Node) nd0.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - ASTReferenceType type1 = ((Node) nd1.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - // skip, if it is an enum - if (type0.getType() != null && type0.getType().equals(type1.getType()) - // It may be a custom enum class or an explicit Enum class usage - && (type0.getType().isEnum() || type0.getType() == java.lang.Enum.class)) { - return data; - } - - addViolation(data, node); - } - } - - return data; - } - - /** - * Checks whether the given node contains a qualified name, consisting of - * one ASTPrimaryPrefix and one or more ASTPrimarySuffix nodes. - * - * @param node - * the node - * @return true if it is a qualified name - */ - private boolean isPartOfQualifiedName(Node node) { - return node.getChild(0) instanceof ASTPrimaryPrefix - && !node.findChildrenOfType(ASTPrimarySuffix.class).isEmpty(); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java index 573b5a1c6d..f12a937083 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java @@ -5,28 +5,34 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; -public class JUnitSpellingRule extends AbstractJUnitRule { +public class JUnitSpellingRule extends AbstractJavaRulechainRule { + + public JUnitSpellingRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override - public Object visit(ASTMethodDeclaration node, Object data) { - if (isJUnit5Class || isJUnit4Class) { - return super.visit(node, data); + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (TestFrameworksUtil.isJUnit3Class(node)) { + node.getDeclarations(ASTMethodDeclaration.class) + .filter(this::isViolation) + .forEach(it -> addViolation(data, it)); } + return null; + } - if (node.getArity() != 0) { - return super.visit(node, data); + private boolean isViolation(ASTMethodDeclaration method) { + if (method.getArity() != 0) { + return false; } + String name = method.getName(); + return !"setUp".equals(name) && "setup".equalsIgnoreCase(name) + || !"tearDown".equals(name) && "teardown".equalsIgnoreCase(name); - String name = node.getName(); - if (!"setUp".equals(name) && "setup".equalsIgnoreCase(name)) { - addViolation(data, node); - } - if (!"tearDown".equals(name) && "teardown".equalsIgnoreCase(name)) { - addViolation(data, node); - } - return super.visit(node, data); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java index de0f71ada6..a95edc6abe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java @@ -5,20 +5,30 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnit3Class; -public class JUnitStaticSuiteRule extends AbstractJUnitRule { +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; + +public class JUnitStaticSuiteRule extends AbstractJavaRulechainRule { + + public JUnitStaticSuiteRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override - public Object visit(ASTMethodDeclaration node, Object data) { - if (node.getArity() != 0) { - return super.visit(node, data); + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (isJUnit3Class(node)) { + ASTMethodDeclaration suiteMethod = node.getDeclarations(ASTMethodDeclaration.class) + .filter(it -> "suite".equals(it.getName()) && it.getArity() == 0) + .first(); + if (suiteMethod != null + && (suiteMethod.getVisibility() != Visibility.V_PUBLIC || !suiteMethod.isStatic())) { + addViolation(data, suiteMethod); + } } - String name = node.getName(); - if ("suite".equals(name) && (!node.isStatic() || !node.isPublic())) { - addViolation(data, node); - } - return super.visit(node, data); + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java index 6900c018fc..145876c7fe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java @@ -4,36 +4,30 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; -public class TestClassWithoutTestCasesRule extends AbstractJUnitRule { +public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { + + public TestClassWithoutTestCasesRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isAbstract() || node.isInterface() || node.isNested()) { - return data; - } + if (isJUnit3Class(node)) { + boolean hasTests = + node.getDeclarations(ASTMethodDeclaration.class) + .any(TestFrameworksUtil::isJunit3MethodSignature); - List m = node.findDescendantsOfType(ASTMethodDeclaration.class); - boolean testsFound = false; - - if (m != null) { - for (ASTMethodDeclaration md : m) { - if (isJUnitMethod(md, data)) { - testsFound = true; - break; - } + if (!hasTests) { + addViolation(data, node); } } - - if (!testsFound) { - addViolation(data, node); - } - - return data; + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java index aae47c5a6e..ee853523e0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java @@ -4,108 +4,40 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; +import java.math.BigDecimal; +import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; -import net.sourceforge.pmd.lang.java.ast.ASTType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * An operation on an Immutable object (String, BigDecimal or BigInteger) won't * change the object itself. The result of the operation is a new object. * Therefore, ignoring the operation result is an error. */ -public class UselessOperationOnImmutableRule extends AbstractJavaRule { +public class UselessOperationOnImmutableRule extends AbstractJavaRulechainRule { - /** - * These are the BigDecimal methods which are immutable - */ - private static final Set BIG_DECIMAL_METHODS = - setOf(".abs", ".add", ".divide", ".divideToIntegralValue", ".max", ".min", ".movePointLeft", - ".movePointRight", ".multiply", ".negate", ".plus", ".pow", ".remainder", ".round", - ".scaleByPowerOfTen", ".setScale", ".stripTrailingZeros", ".subtract", ".ulp"); - - /** - * These are the BigInteger methods which are immutable - */ - private static final Set BIG_INTEGER_METHODS = - setOf(".abs", ".add", ".and", ".andNot", ".clearBit", ".divide", ".flipBit", ".gcd", ".max", - ".min", ".mod", ".modInverse", ".modPow", ".multiply", ".negate", ".nextProbablePrine", ".not", ".or", - ".pow", ".remainder", ".setBit", ".shiftLeft", ".shiftRight", ".subtract", ".xor"); - - /** - * These are the String methods which are immutable - */ - private static final Set STRING_METHODS = - setOf(".concat", ".intern", ".replace", ".replaceAll", ".replaceFirst", ".substring", - ".toLowerCase", ".toString", ".toUpperCase", ".trim"); - - /** - * These are the classes that the rule can apply to - */ - private static final Map> MAP_CLASSES = new HashMap<>(); - - static { - MAP_CLASSES.put("java.math.BigDecimal", BIG_DECIMAL_METHODS); - MAP_CLASSES.put("BigDecimal", BIG_DECIMAL_METHODS); - MAP_CLASSES.put("java.math.BigInteger", BIG_INTEGER_METHODS); - MAP_CLASSES.put("BigInteger", BIG_INTEGER_METHODS); - MAP_CLASSES.put("java.lang.String", STRING_METHODS); - MAP_CLASSES.put("String", STRING_METHODS); + public UselessOperationOnImmutableRule() { + super(ASTMethodCall.class); } @Override - public Object visit(ASTLocalVariableDeclaration node, Object data) { + public Object visit(ASTMethodCall node, Object data) { + ASTExpression qualifier = node.getQualifier(); + if (node.getParent() instanceof ASTExpressionStatement && qualifier != null) { - ASTVariableDeclaratorId var = getDeclaration(node); - if (var == null) { - return super.visit(node, data); - } - String variableName = var.getImage(); - for (NameOccurrence no : var.getUsages()) { - // FIXME - getUsages will return everything with the same name as - // the variable, - // see JUnit test, case 6. Changing to Node below, revisit when - // getUsages is fixed - Node sn = no.getLocation(); - Node primaryExpression = sn.getParent().getParent(); - Class parentClass = primaryExpression.getParent().getClass(); - if (parentClass.equals(ASTStatementExpression.class)) { - String methodCall = sn.getImage().substring(variableName.length()); - ASTType nodeType = node.getTypeNode(); - if (nodeType != null) { - if (MAP_CLASSES.get(nodeType.getTypeImage()).contains(methodCall)) { - addViolation(data, sn); - } - } - } - } - return super.visit(node, data); - } - - /** - * This method checks the variable declaration if it is on a class we care - * about. If it is, it returns the DeclaratorId - * - * @param node - * The ASTLocalVariableDeclaration which is a problem - * @return ASTVariableDeclaratorId - */ - private ASTVariableDeclaratorId getDeclaration(ASTLocalVariableDeclaration node) { - ASTType type = node.getTypeNode(); - if (type != null) { - if (MAP_CLASSES.keySet().contains(type.getTypeImage())) { - return node.getFirstDescendantOfType(ASTVariableDeclaratorId.class); + // these types are immutable, so any method of those whose + // result is ignored is a violation + if (TypeTestUtil.isA(String.class, qualifier) + || TypeTestUtil.isA(BigDecimal.class, qualifier) + || TypeTestUtil.isA(BigInteger.class, qualifier)) { + addViolation(data, node); } } return null; } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java new file mode 100644 index 0000000000..c3789c0b50 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java @@ -0,0 +1,140 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.internal; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInitializer; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +public final class JavaRuleUtil { + + private JavaRuleUtil() { + // utility class + } + + + /** + * Return true if the given expression is enclosed in a zero check. + * The expression must evaluate to a natural number (ie >= 0), so that + * {@code e < 1} actually means {@code e == 0}. + * + * @param e Expression + */ + public static boolean isZeroChecked(ASTExpression e) { + JavaNode parent = e.getParent(); + if (parent instanceof ASTInfixExpression) { + BinaryOp op = ((ASTInfixExpression) parent).getOperator(); + int checkLiteralAtIdx = 1 - e.getIndexInParent(); + JavaNode comparand = parent.getChild(checkLiteralAtIdx); + int expectedValue; + if (op == BinaryOp.NE || op == BinaryOp.EQ) { + // e == 0, e != 0, symmetric + expectedValue = 0; + } else if (op == BinaryOp.LT || op == BinaryOp.GE) { + // e < 1 + // 0 < e + // e >= 1 (e != 0) + // 1 >= e (e == 0 || e == 1) + // 0 >= e (e == 0) + // e >= 0 (true) + expectedValue = checkLiteralAtIdx; + } else if (op == BinaryOp.GT || op == BinaryOp.LE) { + // 1 > e + // e > 0 + + // 1 <= e (e != 0) + // e <= 1 (e == 0 || e == 1) + // e <= 0 (e == 0) + // 0 <= e (true) + expectedValue = 1 - checkLiteralAtIdx; + } else { + return false; + } + + return isIntLit(comparand, expectedValue); + } + return false; + } + + private static boolean isIntLit(JavaNode e, int value) { + if (e instanceof ASTNumericLiteral) { + return ((ASTNumericLiteral) e).getValueAsInt() == value; + } + return false; + } + + /** + * Returns true if the node is a {@link ASTMethodDeclaration} that + * is a main method. + */ + public static boolean isMainMethod(JavaNode node) { + if (node instanceof ASTMethodDeclaration) { + ASTMethodDeclaration decl = (ASTMethodDeclaration) node; + + + return decl.hasModifiers(JModifier.PUBLIC, JModifier.STATIC) + && "main".equals(decl.getName()) + && decl.isVoid() + && decl.getArity() == 1 + && TypeTestUtil.isExactlyA(String[].class, decl.getFormalParameters().get(0)); + } + return false; + } + + /** + * Returns true if the node is a utility class, according to this + * custom definition. + */ + public static boolean isUtilityClass(ASTAnyTypeDeclaration node) { + if (node.isInterface() || node.isEnum()) { + return false; + } + + ASTClassOrInterfaceDeclaration classNode = (ASTClassOrInterfaceDeclaration) node; + + // A class with a superclass or interfaces should not be considered + if (classNode.getSuperClassTypeNode() != null + || !classNode.getSuperInterfaceTypeNodes().isEmpty()) { + return false; + } + + // A class without declarations shouldn't be reported + boolean hasAny = false; + + for (ASTBodyDeclaration declNode : classNode.getDeclarations()) { + if (declNode instanceof ASTFieldDeclaration + || declNode instanceof ASTMethodDeclaration) { + + hasAny = isNonPrivate(declNode) && !isMainMethod(declNode); + if (!((AccessNode) declNode).hasModifiers(JModifier.STATIC)) { + return false; + } + + } else if (declNode instanceof ASTInitializer) { + if (!((ASTInitializer) declNode).isStatic()) { + return false; + } + } + } + + return hasAny; + } + + private static boolean isNonPrivate(ASTBodyDeclaration decl) { + return ((AccessNode) decl).getVisibility() != Visibility.V_PRIVATE; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java new file mode 100644 index 0000000000..76bed67851 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java @@ -0,0 +1,155 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.internal; + +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + +import java.util.Set; + +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +/** + * Utilities for rules related to test frameworks (Junit, TestNG, etc). + */ +public final class TestFrameworksUtil { + + private static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; + private static final String JUNIT4_TEST_ANNOT = "org.junit.Test"; + + private static final String TESTNG_TEST_ANNOT = "org.testng.annotations.Test"; + + private static final Set JUNIT5_ALL_TEST_ANNOTS = + setOf("org.junit.jupiter.api.Test", + "org.junit.jupiter.api.RepeatedTest", + "org.junit.jupiter.api.TestFactory", + "org.junit.jupiter.api.TestTemplate", + "org.junit.jupiter.params.ParameterizedTest" + ); + + private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", + "org.junit.jupiter.api.Assertions", + "org.hamcrest.MatcherAssert", + "org.testng.Assert", + "junit.framework.TestCase"); + + private TestFrameworksUtil() { + // utility class + } + + /** + * True if this is a junit @Test method (or a junit 3 method). + */ + public static boolean isJUnitMethod(ASTMethodDeclaration method) { + if (method.isStatic() || method.getBody() == null) { + return false; // skip various inapplicable method variations + } + + boolean result = false; + result = result || isJUnit5Method(method); + result = result || isJUnit4Method(method); + result = result || isJUnit3Method(method); + return result; + } + + /** + * Returns true if this is either a JUnit test or a TestNG test. + */ + public static boolean isTestMethod(ASTMethodDeclaration method) { + return isJUnitMethod(method) || isTestNgMethod(method); + } + + private static boolean isTestNgMethod(ASTMethodDeclaration method) { + return method.isAnnotationPresent(TESTNG_TEST_ANNOT); + } + + public static boolean isJUnit4Method(ASTMethodDeclaration method) { + return method.isAnnotationPresent(JUNIT4_TEST_ANNOT) && method.isPublic(); + } + + public static boolean isJUnit5Method(ASTMethodDeclaration method) { + return method.getDeclaredAnnotations().any( + it -> { + String canonicalName = it.getTypeMirror().getSymbol().getCanonicalName(); + return JUNIT5_ALL_TEST_ANNOTS.contains(canonicalName); + } + ); + } + + public static boolean isJUnit3Method(ASTMethodDeclaration method) { + return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) + && isJunit3MethodSignature(method); + } + + public static boolean isJunit4TestAnnotation(ASTAnnotation annot) { + return TypeTestUtil.isA(JUNIT4_TEST_ANNOT, annot); + } + + /** + * Does not check the class (use {@link #isJUnit3Class(ASTAnyTypeDeclaration)}). + */ + public static boolean isJunit3MethodSignature(ASTMethodDeclaration method) { + return method.isVoid() + && method.isPublic() + && method.getName().startsWith("test"); + } + + /** + * True if this is a {@code TestCase} class for Junit 3. + */ + public static boolean isJUnit3Class(ASTAnyTypeDeclaration node) { + return node.isRegularClass() + && !node.isNested() + && !node.isAbstract() + && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node); + } + + public static boolean isExpectExceptionCall(ASTMethodCall call) { + return "expect".equals(call.getMethodName()) && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()); + } + + public static boolean isCallOnAssertionContainer(ASTMethodCall call) { + return isCallOnType(call, ASSERT_CONTAINERS); + } + + private static boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { + JTypeMirror declaring = call.getMethodType().getDeclaringType(); + JTypeDeclSymbol sym = declaring.getSymbol(); + String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); + return qualifierTypes.contains(binaryName); + } + + public static boolean isProbableAssertCall(ASTMethodCall call) { + String name = call.getMethodName(); + return name.startsWith("assert") && !isSoftAssert(call) + || name.startsWith("check") + || name.startsWith("verify") + || "fail".equals(name) + || "failWith".equals(name) + || isExpectExceptionCall(call); + } + + private static boolean isSoftAssert(ASTMethodCall call) { + return TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", call.getMethodType().getDeclaringType()) + && !"assertAll".equals(call.getMethodName()); + } + + /** + * Tells if the node contains a @Test annotation with an expected exception. + */ + public static boolean isExpectAnnotated(ASTMethodDeclaration method) { + return method.getDeclaredAnnotations() + .filter(TestFrameworksUtil::isJunit4TestAnnotation) + .flatMap(ASTAnnotation::getMembers) + .any(it -> "expected".equals(it.getName())); + + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java index 5d42d5c6b0..04427bf008 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java @@ -4,32 +4,29 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; -import java.util.ArrayList; +import java.lang.reflect.Modifier; import java.util.List; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; +import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; -import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** *

@@ -58,97 +55,55 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
  */
 public class DoubleCheckedLockingRule extends AbstractJavaRule {
 
-    private List volatileFields;
-
     @Override
-    public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
-        if (node.isInterface()) {
-            return data;
-        }
-        return super.visit(node, data);
-    }
-
-    @Override
-    public Object visit(ASTCompilationUnit compilationUnit, Object data) {
-        if (this.volatileFields == null) {
-            this.volatileFields = new ArrayList<>(0);
-        } else {
-            this.volatileFields.clear();
-        }
-        return super.visit(compilationUnit, data);
-    }
-
-    @Override
-    public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
-        if (fieldDeclaration.isVolatile()) {
-            for (ASTVariableDeclaratorId declarator : fieldDeclaration
-                    .findDescendantsOfType(ASTVariableDeclaratorId.class)) {
-                this.volatileFields.add(declarator.getImage());
-            }
-        }
-        return super.visit(fieldDeclaration, data);
+    protected @NonNull RuleTargetSelector buildTargetSelector() {
+        return RuleTargetSelector.forTypes(ASTMethodDeclaration.class);
     }
 
     @Override
     public Object visit(ASTMethodDeclaration node, Object data) {
-        if (node.getResultType().isVoid()) {
-            return super.visit(node, data);
+        if (node.isVoid() || node.getResultTypeNode() instanceof ASTPrimitiveType || node.getBody() == null) {
+            return data;
         }
 
-        ASTType typeNode = (ASTType) node.getResultType().getChild(0);
-        if (typeNode.getNumChildren() == 0 || !(typeNode.getChild(0) instanceof ASTReferenceType)) {
-            return super.visit(node, data);
-        }
-
-        List rsl = node.findDescendantsOfType(ASTReturnStatement.class);
+        List rsl = node.descendants(ASTReturnStatement.class).toList();
         if (rsl.size() != 1) {
-            return super.visit(node, data);
+            return data;
         }
         ASTReturnStatement rs = rsl.get(0);
 
-        List pel = rs.findDescendantsOfType(ASTPrimaryExpression.class);
-        ASTPrimaryExpression ape = pel.get(0);
-        Node lastChild = ape.getChild(ape.getNumChildren() - 1);
-        String returnVariableName = null;
-        if (lastChild instanceof ASTPrimaryPrefix) {
-            returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
+        ASTExpression returnExpr = rs.getExpr();
+        if (!(returnExpr instanceof ASTNamedReferenceExpr)) {
+            return data;
         }
+
+        JVariableSymbol returnVariable = ((ASTNamedReferenceExpr) returnExpr).getReferencedSym();
         // With Java5 and volatile keyword, DCL is no longer an issue
-        if (returnVariableName == null || this.volatileFields.contains(returnVariableName)) {
-            return super.visit(node, data);
+        if (returnVariable instanceof JFieldSymbol
+            && Modifier.isVolatile(((JFieldSymbol) returnVariable).getModifiers())) {
+            return data;
         }
+
         // if the return variable is local and only written with the volatile
         // field, then it's ok, too
-        if (checkLocalVariableUsage(node, returnVariableName)) {
+        if (isLocalOnlyStoredWithVolatileField(node, returnVariable)) {
             return super.visit(node, data);
         }
+
         List isl = node.findDescendantsOfType(ASTIfStatement.class);
         if (isl.size() == 2) {
-            ASTIfStatement is = isl.get(0);
-            if (ifVerify(is, returnVariableName)) {
+            ASTIfStatement outerIf = isl.get(0);
+            if (isNullCheck(outerIf.getCondition(), returnVariable)) {
                 // find synchronized
-                List ssl = is.findDescendantsOfType(ASTSynchronizedStatement.class);
-                if (ssl.size() == 1) {
-                    ASTSynchronizedStatement ss = ssl.get(0);
-                    isl = ss.findDescendantsOfType(ASTIfStatement.class);
-                    if (isl.size() == 1) {
-                        ASTIfStatement is2 = isl.get(0);
-                        if (ifVerify(is2, returnVariableName)) {
-                            List sel = is2.findDescendantsOfType(ASTStatementExpression.class);
-                            if (sel.size() == 1) {
-                                ASTStatementExpression se = sel.get(0);
-                                if (se.getNumChildren() == 3) {
-                                    // primaryExpression, AssignmentOperator, Expression
-                                    if (se.getChild(0) instanceof ASTPrimaryExpression) {
-                                        ASTPrimaryExpression pe = (ASTPrimaryExpression) se.getChild(0);
-                                        if (matchName(pe, returnVariableName)) {
-                                            if (se.getChild(1) instanceof ASTAssignmentOperator) {
-                                                addViolation(data, node);
-                                            }
-                                        }
-                                    }
-                                }
-                            }
+                List ssl = outerIf.findDescendantsOfType(ASTSynchronizedStatement.class);
+                if (ssl.size() == 1 && ssl.get(0).ancestors().any(it -> it == outerIf)) {
+                    ASTIfStatement is2 = isl.get(1);
+                    if (isNullCheck(is2.getCondition(), returnVariable)) {
+                        List assignments = is2.findDescendantsOfType(ASTAssignmentExpression.class);
+                        if (assignments.size() == 1
+                            && isReferenceTo(assignments.get(0).getLeftOperand(), returnVariable)) {
+                            addViolation(data, node);
+
                         }
                     }
                 }
@@ -157,118 +112,54 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule {
         return super.visit(node, data);
     }
 
-    private boolean checkLocalVariableUsage(ASTMethodDeclaration node, String returnVariableName) {
-        List locals = node.findDescendantsOfType(ASTLocalVariableDeclaration.class);
-        ASTVariableInitializer initializer = null;
-        for (ASTLocalVariableDeclaration l : locals) {
-            ASTVariableDeclaratorId id = l.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
-            if (id != null && id.hasImageEqualTo(returnVariableName)) {
-                initializer = l.getFirstDescendantOfType(ASTVariableInitializer.class);
-                break;
-            }
-        }
-        // the return variable name doesn't seem to be a local variable
-        if (initializer == null) {
-            return false;
-        }
-
-        // verify the value with which the local variable is initialized
-        if (initializer.getNumChildren() > 0 && initializer.getChild(0) instanceof ASTExpression
-                && initializer.getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0) instanceof ASTPrimaryExpression
-                && initializer.getChild(0).getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0).getChild(0) instanceof ASTPrimaryPrefix
-                && initializer.getChild(0).getChild(0).getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0).getChild(0).getChild(0) instanceof ASTName) {
-            ASTName name = (ASTName) initializer.getChild(0).getChild(0).getChild(0).getChild(0);
-            if (name == null || !volatileFields.contains(name.getImage())) {
+    private boolean isLocalOnlyStoredWithVolatileField(ASTMethodDeclaration method, JVariableSymbol local) {
+        ASTExpression initializer;
+        if (local instanceof JLocalVariableSymbol) {
+            ASTVariableDeclaratorId id = local.tryGetNode();
+            if (id == null) {
                 return false;
             }
+            initializer = id.getInitializer();
         } else {
-            // not a simple assignment
+            // the return variable name doesn't seem to be a local variable
             return false;
         }
 
-        // now check every usage/assignment of the variable
-        List names = node.findDescendantsOfType(ASTName.class);
-        for (ASTName n : names) {
-            if (!n.hasImageEqualTo(returnVariableName)) {
-                continue;
-            }
-
-            Node expression = n.getNthParent(3);
-            if (expression instanceof ASTEqualityExpression) {
-                continue;
-            }
-            if (expression instanceof ASTStatementExpression) {
-                if (expression.getNumChildren() > 2 && expression.getChild(1) instanceof ASTAssignmentOperator) {
-                    ASTName value = expression.getChild(2).getFirstDescendantOfType(ASTName.class);
-                    if (value == null || !volatileFields.contains(value.getImage())) {
-                        return false;
-                    }
-                }
-            }
-        }
-
-        return true;
+        return (initializer == null || isVolatileFieldReference(initializer))
+            && method.descendants(ASTAssignmentExpression.class)
+                     .filter(it -> isReferenceTo(it.getLeftOperand(), local))
+                     .all(it -> isVolatileFieldReference(it.getRightOperand()));
     }
 
-    private boolean ifVerify(ASTIfStatement is, String varname) {
-        List finder = is.findDescendantsOfType(ASTPrimaryExpression.class);
-        if (finder.size() > 1) {
-            ASTPrimaryExpression nullStmt = findNonVariableStmt(varname, finder.get(0), finder.get(1));
-            if (nullStmt != null) {
-                if (nullStmt.getNumChildren() == 1 && nullStmt.getChild(0) instanceof ASTPrimaryPrefix) {
-                    ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) nullStmt.getChild(0);
-                    if (pp2.getNumChildren() == 1 && pp2.getChild(0) instanceof ASTLiteral) {
-                        ASTLiteral lit = (ASTLiteral) pp2.getChild(0);
-                        if (lit.getNumChildren() == 1 && lit.getChild(0) instanceof ASTNullLiteral) {
-                            return true;
-                        }
-                    }
+    private boolean isVolatileFieldReference(@Nullable ASTExpression initializer) {
+        if (initializer instanceof ASTNamedReferenceExpr) {
+            JVariableSymbol fieldSym = ((ASTNamedReferenceExpr) initializer).getReferencedSym();
+            return fieldSym instanceof JFieldSymbol && Modifier.isVolatile(((JFieldSymbol) fieldSym).getModifiers());
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isReferenceTo(@Nullable ASTExpression expr, JVariableSymbol symbol) {
+        if (expr instanceof ASTNamedReferenceExpr) {
+            return symbol != null && symbol.equals(((ASTNamedReferenceExpr) expr).getReferencedSym());
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isNullCheck(ASTExpression expr, JVariableSymbol var) {
+        if (expr instanceof ASTInfixExpression) {
+            ASTInfixExpression condition = (ASTInfixExpression) expr;
+            if (condition.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) {
+                ASTNullLiteral nullLit = condition.getFirstChildOfType(ASTNullLiteral.class);
+                if (nullLit != null) {
+                    ASTExpression otherChild = (ASTExpression) condition.getChild(1 - nullLit.getIndexInParent());
+                    return isReferenceTo(otherChild, var);
                 }
             }
         }
         return false;
     }
 
-    /**
-     * 

- * Sort out if apeLeft or apeRight are variable with the provided - * 'variableName'. - *

- * - * @param variableName - * @param apeLeft - * @param apeRight - * @return reference from either apeLeft or apeRight, if one of them match, - * or 'null', if none match. - */ - private ASTPrimaryExpression findNonVariableStmt(String variableName, ASTPrimaryExpression apeLeft, - ASTPrimaryExpression apeRight) { - if (matchName(apeLeft, variableName)) { - return apeRight; - } else if (matchName(apeRight, variableName)) { - return apeLeft; - } - return null; - } - - private boolean matchName(ASTPrimaryExpression ape, String name) { - if (ape.getNumChildren() == 1 && ape.getChild(0) instanceof ASTPrimaryPrefix) { - ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.getChild(0); - String name2 = getNameFromPrimaryPrefix(pp); - if (name2 != null && name2.equals(name)) { - return true; - } - } - return false; - } - - private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) { - if (pp.getNumChildren() == 1 && pp.getChild(0) instanceof ASTName) { - return ((ASTName) pp.getChild(0)).getImage(); - } - return null; - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java index c4cb425c5f..cac3f78028 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java @@ -4,8 +4,12 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * This rule finds the following: @@ -20,41 +24,28 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; * * @see feature request #381 Single character StringBuffer.append */ -public class AppendCharacterWithCharRule extends AbstractJavaRule { +public class AppendCharacterWithCharRule extends AbstractJavaRulechainRule { public AppendCharacterWithCharRule() { - addRuleChainVisit(ASTLiteral.class); + super(ASTStringLiteral.class); } @Override - public Object visit(ASTLiteral node, Object data) { - // REVERT ME - // ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class); - // if (bs == null) { - // return data; - // } - // - // if (node.isSingleCharacterStringLiteral()) { - // if (!InefficientStringBufferingRule.isInStringBufferOperationChain(node, "append")) { - // return data; - // } - // - // // ignore, if the literal is part of an expression, such as "X".repeat(5) - // final ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node.getNthParent(2); - // if (primaryExpression != null && primaryExpression.getFirstChildOfType(ASTPrimarySuffix.class) != null) { - // return data; - // } - // // ignore, if this literal is part of a different expression, e.g. "X" + something else - // if (primaryExpression != null && !(primaryExpression.getNthParent(2) instanceof ASTArgumentList)) { - // return data; - // } - // // ignore if this string literal is used as a constructor argument - // if (primaryExpression != null && primaryExpression.getNthParent(4) instanceof ASTAllocationExpression) { - // return data; - // } - // - // addViolation(data, node); - // } + public Object visit(ASTStringLiteral node, Object data) { + if (node.getParent() instanceof ASTArgumentList + && node.length() == 1 + && ((ASTArgumentList) node.getParent()).size() == 1) { + JavaNode callParent = node.getParent().getParent(); + if (callParent instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) callParent; + if ("append".equals(call.getMethodName()) + && (TypeTestUtil.isDeclaredInClass(StringBuilder.class, call.getMethodType()) + || TypeTestUtil.isDeclaredInClass(StringBuffer.class, call.getMethodType())) + ) { + addViolation(data, node); + } + } + } return data; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java index 63ad694116..84f22d3084 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java @@ -4,10 +4,11 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * This rule finds code which inefficiently determines empty strings. @@ -41,41 +42,45 @@ import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; * * @author acaplan */ -public class InefficientEmptyStringCheckRule extends AbstractInefficientZeroCheck { +public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { + + public InefficientEmptyStringCheckRule() { + super(ASTMethodCall.class); + } @Override - public boolean isTargetMethod(JavaNameOccurrence occ) { - if (occ.getNameForWhichThisIsAQualifier() != null - && occ.getNameForWhichThisIsAQualifier().getImage().contains("trim")) { - Node pExpression = occ.getLocation().getParent().getParent(); - if (pExpression.getNumChildren() > 2 && "length".equals(pExpression.getChild(2).getImage())) { - return true; - } + public Object visit(ASTMethodCall call, Object data) { + if (isTrimCall(call.getQualifier()) + && (isLengthZeroCheck(call) || isIsEmptyCall(call))) { + addViolation(data, call); + } + return null; + } + + private static boolean isLengthZeroCheck(ASTMethodCall call) { + return "length".equals(call.getMethodName()) + && call.getArguments().size() == 0 + && JavaRuleUtil.isZeroChecked(call); + } + + private static boolean isTrimCall(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + return "trim".equals(call.getMethodName()) + && call.getArguments().size() == 0 + && TypeTestUtil.isA(String.class, call.getQualifier()); } return false; } - @Override - public boolean appliesToClassName(String name) { - return "String".equals(name); - } - @Override - public Object visit(ASTPrimaryExpression node, Object data) { - - if (node.getNumChildren() > 3) { - // Check last suffix - if (!"isEmpty".equals(node.getChild(node.getNumChildren() - 2).getImage())) { - return data; - } - - Node prevCall = node.getChild(node.getNumChildren() - 4); - String target = prevCall.getNumChildren() > 0 ? prevCall.getChild(0).getImage() : prevCall.getImage(); - if (target != null && ("trim".equals(target) || target.endsWith(".trim"))) { - addViolation(data, node); - } + private static boolean isIsEmptyCall(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + return "isEmpty".equals(call.getMethodName()) + && call.getArguments().size() == 0; } - return data; + return false; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java index 86e6ff187c..801e1f4ab0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java @@ -5,17 +5,16 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; -import net.sourceforge.pmd.lang.java.ast.ASTCharLiteral; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.JModifier; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; /** * Detects redundant field initializers, i.e. the field initializer expressions @@ -24,75 +23,46 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; * @author lucian.ciufudean@gmail.com * @since Apr 10, 2009 */ -public class RedundantFieldInitializerRule extends AbstractJavaRule { +public class RedundantFieldInitializerRule extends AbstractJavaRulechainRule { public RedundantFieldInitializerRule() { - addRuleChainVisit(ASTFieldDeclaration.class); + super(ASTFieldDeclaration.class); } @Override public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) { - if (declaresNotFinalField(fieldDeclaration)) { - for (ASTVariableDeclarator varDecl : fieldDeclaration.descendants(ASTVariableDeclarator.class)) { - if (hasRedundantInitializer(fieldDeclaration, varDecl)) { - addViolation(data, varDecl); + if (!fieldDeclaration.hasModifiers(JModifier.FINAL)) { + for (ASTVariableDeclaratorId varId : fieldDeclaration.getVarIds()) { + ASTExpression init = varId.getInitializer(); + if (init != null) { + if (isDefaultValue(varId.getTypeMirror(), init)) { + addViolation(data, varId); + } } } } return data; } - private boolean declaresNotFinalField(ASTFieldDeclaration fieldDeclaration) { - return !fieldDeclaration.hasModifiers(JModifier.FINAL); - } - - private boolean hasRedundantInitializer(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator varDecl) { - return declaresFieldOfPrimitiveType(fieldDeclaration) - && hasRedundantInitializerOfPrimitive(varDecl) - || hasRedundantInitializerOfReference(varDecl); - } - - private boolean declaresFieldOfPrimitiveType(ASTFieldDeclaration fieldDeclaration) { - return fieldDeclaration.getTypeNode() instanceof ASTPrimitiveType; - } - - private boolean hasRedundantInitializerOfPrimitive(ASTVariableDeclarator varDecl) { - ASTLiteral literal = getLiteralValue(varDecl.getInitializer()); - if (literal != null) { - if (literal instanceof ASTNumericLiteral) { - return hasDefaultNumericValue((ASTNumericLiteral) literal); - } else if (literal instanceof ASTCharLiteral) { - return hasDefaultCharLiteralValue((ASTCharLiteral) literal); - } else if (literal instanceof ASTBooleanLiteral) { - return isDefaultBooleanLiteral((ASTBooleanLiteral) literal); + private boolean isDefaultValue(JTypeMirror type, ASTExpression expr) { + if (type.isPrimitive()) { + if (type.isPrimitive(PrimitiveTypeKind.BOOLEAN)) { + return expr instanceof ASTBooleanLiteral && !((ASTBooleanLiteral) expr).isTrue(); + } else { + if (!isOkExpr(expr)) { + // whitelist named constants or calculations involving them + return false; + } + Object constValue = expr.getConstValue(); + return constValue instanceof Number && ((Number) constValue).doubleValue() == 0d + || constValue instanceof Character && constValue.equals('\u0000'); } + } else { + return expr instanceof ASTNullLiteral; } - return false; } - private boolean hasDefaultNumericValue(ASTNumericLiteral literal) { - return literal.getConstValue().doubleValue() == 0; + private static boolean isOkExpr(ASTExpression e) { + return e.descendantsOrSelf().none(it -> it instanceof ASTVariableAccess || it instanceof ASTFieldAccess); } - - private boolean hasDefaultCharLiteralValue(ASTCharLiteral literal) { - return literal.getConstValue() == '\0'; - } - - private boolean isDefaultBooleanLiteral(ASTBooleanLiteral literal) { - return !literal.isTrue(); - } - - private boolean hasRedundantInitializerOfReference(ASTVariableDeclarator varDecl) { - return getLiteralValue(varDecl.getInitializer()) instanceof ASTNullLiteral; - } - - private ASTLiteral getLiteralValue(ASTExpression expr) { - if (expr instanceof ASTLiteral) { - return (ASTLiteral) expr; - } else if (expr instanceof ASTCastExpression) { - return getLiteralValue(((ASTCastExpression) expr).getOperand()); - } - return null; - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java index 57a3b8569c..36ac315fec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java @@ -4,87 +4,32 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import static net.sourceforge.pmd.lang.ast.NodeStream.asInstanceOf; +import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.List; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class StringInstantiationRule extends AbstractJavaRule { - public StringInstantiationRule() { - addRuleChainVisit(ASTAllocationExpression.class); + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTConstructorCall.class); } @Override - public Object visit(ASTAllocationExpression node, Object data) { - if (!(node.getChild(0) instanceof ASTClassOrInterfaceType)) { - return data; - } - - if (!TypeTestUtil.isA(String.class, (ASTClassOrInterfaceType) node.getChild(0))) { - return data; - } - - if (isArrayAccess(node)) { - addViolation(data, node); - return data; - } - - List exp = node.findDescendantsOfType(ASTExpression.class); - if (exp.size() >= 2) { - return data; - } - - if (node.descendants().map(asInstanceOf(ASTArrayDimsAndInits.class, ASTAdditiveExpression.class)).nonEmpty()) { - return data; - } - - ASTName name = node.getFirstDescendantOfType(ASTName.class); - // Literal, i.e., new String("foo") - if (name == null) { - addViolation(data, node); - return data; - } - - NameDeclaration nd = name.getNameDeclaration(); - if (!(nd instanceof TypedNameDeclaration)) { - return data; - } - - if (TypeTestUtil.isA(String.class, ((TypedNameDeclaration) nd).getTypeNode())) { + public Object visit(ASTConstructorCall node, Object data) { + ASTArgumentList args = node.getArguments(); + if (args.size() <= 1 + && TypeTestUtil.isExactlyA(String.class, node.getTypeNode())) { + if (args.size() == 1 && TypeTestUtil.isExactlyA(byte[].class, args.get(0))) { + // byte array ctor is ok + return data; + } addViolation(data, node); } return data; } - - private boolean isArrayAccess(ASTAllocationExpression node) { - ASTArguments arguments = node.getFirstChildOfType(ASTArguments.class); - if (arguments == null || arguments.size() != 1) { - return false; - } - - Node firstArg = arguments.getFirstChildOfType(ASTArgumentList.class).getChild(0); - ASTPrimaryExpression primary = firstArg.getFirstChildOfType(ASTPrimaryExpression.class); - if (primary == null || primary.getType() != String.class) { - return false; - } - - ASTPrimarySuffix suffix = primary.getFirstChildOfType(ASTPrimarySuffix.class); - return suffix != null && suffix.isArrayDereference(); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java deleted file mode 100644 index 4f150c8faa..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; - -/** - * Finds toString() call on String object. - * - * Note: due to an issue with type resolution, this implementation doesn't detect cases when toString() - * call is chained to a method returning String which is not declared in the class having the call or the call - * arguments are not of the exact same type as method parameters are, excluding the case when method name and - * number of it's parameters is enough to identify the method. Example: - *
{@code
- *    class A {
- *         public String str() {
- *            return "exampleStr";
- *         }
- *    }
- *    class B {
- *        public void foo() {
- *            String s = new A().str().toString(); // not detected because str() is from another class
- *            s = getString().toString(); // detected
- *            s = getData(new FileInputStream()).toString(); // detected because of argument type
- *            s = getData(new Integer(4), new Integer(5)).toString(); // detected because of unique args count
- *        }
- *        public String getString() {
- *            return "exampleStr";
- *        }
- *        public String getData(InputStream is) {
- *            return "argsResolutionIssueExample";
- *        }
- *        public int getData(String s) {
- *            return 0;
- *        }
- *        public String getData(Number a, Number b) {
- *            return "uniqueArgsCountExample";
- *        }
- *    }
- *    }
- */ -public class StringToStringRule extends AbstractJavaRule { - - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTMethodCall.class); - } - - @Override - public Object visit(ASTMethodCall node, Object data) { - if ("toString".equals(node.getMethodName()) - && node.getArguments().size() == 0 - && TypeTestUtil.isA(String.class, node.getQualifier())) { - addViolation(data, node); - } - - return data; - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java index e2d9c9ddd1..d5144314a4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java @@ -4,41 +4,33 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.rule.AbstractPoorMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** + * */ -public class UseIndexOfCharRule extends AbstractPoorMethodCall { +public class UseIndexOfCharRule extends AbstractJavaRulechainRule { - private static final String TARGET_TYPE_NAME = "String"; - private static final String[] METHOD_NAMES = new String[] { "indexOf", "lastIndexOf" }; - - /** - * Method targetTypeName. - * - * @return String - */ - @Override - protected String targetTypename() { - return TARGET_TYPE_NAME; - } - - /** - * Method methodNames. - * - * @return String[] - */ - @Override - protected String[] methodNames() { - return METHOD_NAMES; + public UseIndexOfCharRule() { + super(ASTMethodCall.class); } @Override - protected boolean isViolationArgument(Node arg) { - return true; - // FIXME - REVERT ME - // return ((ASTLiteral) arg).isSingleCharacterStringLiteral(); + public Object visit(ASTMethodCall node, Object data) { + if ("indexOf".equals(node.getMethodName()) || "lastIndexOf".equals(node.getMethodName())) { + if (TypeTestUtil.isA(String.class, node.getQualifier()) + && node.getArguments().size() >= 1) { // there are two overloads of each + ASTExpression arg = node.getArguments().get(0); + if (arg instanceof ASTStringLiteral && ((ASTStringLiteral) arg).getConstValue().length() == 1) { + addViolation(data, node); + } + } + } + return data; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java deleted file mode 100644 index 7b449d59d7..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; - -/** - * This rule finds places where StringBuffer.toString() is called just to see if - * the string is 0 length by either using .equals("") or toString().length(). - * - *
- * StringBuffer sb = new StringBuffer("some string");
- * if (sb.toString().equals("")) {
- *     // this is wrong
- * }
- * if (sb.length() == 0) {
- *     // this is right
- * }
- * 
- * - * @author acaplan - * @author Philip Graf - */ -public class UseStringBufferLengthRule extends AbstractJavaRule { - - // FIXME Need to remove this somehow. - /* - * Specifically, we need a symbol tree that can be traversed downwards, so - * that instead of visiting each name and then visiting the declaration for - * that name, we should visit all the declarations and check their usages. - * With that in place, this rule would be reduced to: - find all - * StringBuffer declarations - check each usage - flag those that involve - * variable.toString() - */ - private Set alreadySeen = new HashSet<>(); - - @Override - public Object visit(ASTMethodDeclaration acu, Object data) { - alreadySeen.clear(); - return super.visit(acu, data); - } - - @Override - public Object visit(ASTName decl, Object data) { - if (!decl.getImage().endsWith("toString")) { - return data; - } - NameDeclaration nd = decl.getNameDeclaration(); - if (nd == null) { - return data; - } - if (alreadySeen.contains(nd) || !(nd instanceof VariableNameDeclaration) - || !ConsecutiveLiteralAppendsRule.isStringBuilderOrBuffer(((VariableNameDeclaration) nd).getDeclaratorId())) { - return data; - } - alreadySeen.add(nd); - - if (isViolation(decl)) { - addViolation(data, decl); - } - - return data; - } - - /** - * Returns true for the following violations: - * - *
-     * StringBuffer sb = new StringBuffer("some string");
-     * if (sb.toString().equals("")) {
-     *     // this is a violation
-     * }
-     * if (sb.toString().length() == 0) {
-     *     // this is a violation
-     * }
-     * if (sb.length() == 0) {
-     *     // this is ok
-     * }
-     * 
- */ - private boolean isViolation(ASTName decl) { - // the (grand)parent of a violation has four children - Node parent = decl.getParent().getParent(); - if (parent.getNumChildren() == 4) { - // 1. child: sb.toString where sb is a VariableNameDeclaration for a - // StringBuffer or StringBuilder - if (parent.getChild(0).getFirstChildOfType(ASTName.class).getImage().endsWith(".toString")) { - // 2. child: the arguments of toString - // no need to check as both StringBuffer and StringBuilder only - // have one toString method - // 3. child: equals or length, 4. child: their arguments - return isEqualsViolation(parent) || isLengthViolation(parent); - } - } - return false; - } - - private boolean isEqualsViolation(Node parent) { - // 3. child: equals - if (parent.getChild(2).hasImageEqualTo("equals")) { - // 4. child: the arguments of equals, there must be exactly one and - // it must be "" - List argList = parent.getChild(3).findDescendantsOfType(ASTArgumentList.class); - if (argList.size() == 1) { - List literals = argList.get(0).findDescendantsOfType(ASTLiteral.class); - return literals.size() == 1 && literals.get(0).hasImageEqualTo("\"\""); - } - } - return false; - } - - private boolean isLengthViolation(Node parent) { - // 3. child: length - return parent.getChild(2).hasImageEqualTo("length"); - // 4. child: the arguments of length - // no need to check as String has only one length method - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java index db64609724..64a2a8c1c7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java @@ -4,91 +4,59 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; -import net.sourceforge.pmd.lang.java.ast.ASTType; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class UselessStringValueOfRule extends AbstractJavaRule { + @Override - public Object visit(ASTPrimaryPrefix node, Object data) { - if (node.getNumChildren() == 0 || !(node.getChild(0) instanceof ASTName)) { - return super.visit(node, data); - } - - String image = ((ASTName) node.getChild(0)).getImage(); - - if ("String.valueOf".equals(image)) { - Node parent = node.getParent(); - if (parent.getNumChildren() != 2) { - return super.visit(node, data); - } - // skip String.valueOf(anyarraytype[]) - ASTArgumentList args = parent.getFirstDescendantOfType(ASTArgumentList.class); - if (args != null) { - ASTName arg = args.getFirstDescendantOfType(ASTName.class); - if (arg != null) { - NameDeclaration declaration = arg.getNameDeclaration(); - if (declaration != null) { - ASTType argType = declaration.getNode().getParent().getParent() - .getFirstDescendantOfType(ASTType.class); - if (argType != null && argType.getChild(0) instanceof ASTReferenceType - // FIXME - REVERT ME - // && ((ASTReferenceType) argType.getChild(0)).isArray() - ) { - return super.visit(node, data); - } - } - } - } - - Node gp = parent.getParent(); - if (parent instanceof ASTPrimaryExpression && gp instanceof ASTAdditiveExpression - && "+".equals(gp.getImage())) { - boolean ok = false; - if (gp.getChild(0) == parent) { - ok = !isPrimitive(gp.getChild(1)); - } else { - for (int i = 0; !ok && gp.getChild(i) != parent; i++) { - ok = !isPrimitive(gp.getChild(i)); - } - } - if (ok) { - super.addViolation(data, node); - return data; - } - } - } - return super.visit(node, data); + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTMethodCall.class); } - private static boolean isPrimitive(Node parent) { - boolean result = false; - if (parent instanceof ASTPrimaryExpression && parent.getNumChildren() == 1) { - Node child = parent.getChild(0); - if (child instanceof ASTPrimaryPrefix && child.getNumChildren() == 1) { - Node gc = child.getChild(0); - if (gc instanceof ASTName) { - ASTName name = (ASTName) gc; - NameDeclaration nd = name.getNameDeclaration(); - if (nd instanceof VariableNameDeclaration && ((VariableNameDeclaration) nd).isPrimitiveType()) { - result = true; - } - } else if (gc instanceof ASTLiteral) { - result = !((ASTLiteral) gc).isStringLiteral(); - } + @Override + public Object visit(ASTMethodCall node, Object data) { + if (node.getParent() instanceof ASTInfixExpression + && ((ASTInfixExpression) node.getParent()).getOperator() == BinaryOp.ADD) { + ASTExpression valueOfArg = getValueOfArg(node); + if (valueOfArg == null) { + return data; //not a valueOf call + } else if (TypeTestUtil.isExactlyA(String.class, valueOfArg)) { + addViolation(data, node); // valueOf call on a string + return data; + } + + ASTExpression sibling = (ASTExpression) node.getParent().getChild(1 - node.getIndexInParent()); + if (TypeTestUtil.isExactlyA(String.class, sibling) + && !valueOfArg.getTypeMirror().isArray() + // In `String.valueOf(a) + String.valueOf(b)`, + // only report the second call + && (getValueOfArg(sibling) == null || node.getIndexInParent() == 1)) { + addViolation(data, node); } } - return result; + return data; + } + + private static @Nullable ASTExpression getValueOfArg(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + if (call.getArguments().size() == 1 + && "valueOf".equals(call.getMethodName()) + && TypeTestUtil.isDeclaredInClass(String.class, call.getMethodType())) { + return call.getArguments().get(0); + } + } + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java deleted file mode 100644 index a444b61c48..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.regex; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import net.sourceforge.pmd.annotation.InternalApi; - -/** - * A simple helper class to regroup a bunch of method generally used by rules - * using regex. - * - * @author Romain PELISSE, belaran@gmail.com - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public final class RegexHelper { - - /** - * Default private empty constructors - */ - private RegexHelper() { - } - - /** - * Compiles a list of regex into a list of patterns. - * - * @param list - * the regex list - * @return the pattern list - */ - public static List compilePatternsFromList(List list) { - List patterns; - if (list != null && !list.isEmpty()) { - patterns = new ArrayList<>(list.size()); - for (String stringPattern : list) { - if (stringPattern != null && !"".equals(stringPattern)) { - patterns.add(Pattern.compile(stringPattern)); - } - } - } else { - patterns = new ArrayList<>(0); - } - return patterns; - } - - /** - * Simple commodity method (also designed to increase readability of source - * code, and to decrease import in the calling class). Provide a pattern and - * a subject, it'll do the proper matching. - * - * @param pattern - * a compiled regex pattern - * @param subject - * a String to match - * @return {@code true} if there is a match; {@code false} otherwise - */ - public static boolean isMatch(Pattern pattern, String subject) { - if (subject != null && !"".equals(subject)) { - Matcher matcher = pattern.matcher(subject); - if (matcher.find()) { - return true; - } - } - return false; - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java index 1583d95577..f2f1e4c768 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; import net.sf.saxon.expr.Expression; @@ -24,8 +23,13 @@ import net.sf.saxon.value.SequenceType; /** * A context node test function that may parse its string argument early * if it is a string literal. + * + * @param Type of state into which the argument is parsed + * @param Type of node the function applies. The function will return + * false for other kinds of node. */ -abstract class BaseRewrittenFunction extends BaseJavaXPathFunction { +// TODO could move that up to pmd-core +abstract class BaseRewrittenFunction extends BaseJavaXPathFunction { private final Class contextNodeType; @@ -50,8 +54,23 @@ abstract class BaseRewrittenFunction extends BaseJavaXPat } - protected abstract S parseArgument(String constantArg) throws XPathException; + /** + * Parse the argument into the state. This is called at build time + * if the arg is constant, otherwise it's anyway called before {@link #matches(Node, String, Object, boolean)} + * is called. + */ + protected abstract S parseArgument(String arg) throws XPathException; + /** + * Compute the result of the function. + * + * @param contextNode Context node + * @param arg Value of the argument + * @param parsedArg Result of {@link #parseArgument(String)} on the argument + * @param isConstant Whether the argument is constant (it was parsed in all cases) + * + * @return Whether the function matches + */ protected abstract boolean matches(N contextNode, String arg, S parsedArg, boolean isConstant) throws XPathException; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java index 4bc4baa301..a2a27f0224 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java @@ -23,9 +23,9 @@ public final class MatchesSignatureFunction extends BaseRewrittenFunction, JavaNo } @Override - protected Class parseArgument(String constantArg) throws XPathException { + protected Class parseArgument(String arg) throws XPathException { try { - return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + constantArg); + return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + arg); } catch (ClassNotFoundException e) { - throw new XPathException("No class named AST" + constantArg); + throw new XPathException("No class named AST" + arg); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java deleted file mode 100644 index a339bd4b43..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.xpath.internal; - -import java.util.function.BiPredicate; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; - -import net.sf.saxon.expr.XPathContext; -import net.sf.saxon.lib.ExtensionFunctionCall; -import net.sf.saxon.om.Sequence; -import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.BooleanValue; -import net.sf.saxon.value.SequenceType; - - -/** - * XPath function {@code pmd-java:typeIs(typeName as xs:string) as xs:boolean} - * and {@code typeIsExactly}. - * - *

Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIs('java.lang.String')]} - * - *

Returns true if the type of the node matches, false otherwise. - */ -public final class TypeIsFunction extends BaseJavaXPathFunction { - - public static final TypeIsFunction TYPE_IS_EXACTLY = new TypeIsFunction("typeIsExactly", TypeTestUtil::isExactlyA); - public static final TypeIsFunction TYPE_IS = new TypeIsFunction("typeIs", TypeTestUtil::isA); - - private final BiPredicate checker; - - private TypeIsFunction(String localName, BiPredicate checker) { - super(localName); - this.checker = checker; - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[] {SequenceType.SINGLE_STRING}; - } - - - @Override - public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { - return SequenceType.SINGLE_BOOLEAN; - } - - - @Override - public boolean dependsOnFocus() { - return true; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - @Override - public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { - Node contextNode = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); - String fullTypeName = arguments[0].head().getStringValue(); - - if (contextNode instanceof TypeNode) { - return BooleanValue.get(checker.test(fullTypeName, (TypeNode) contextNode)); - } else { - throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); - } - } - }; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index eb74945500..154db01020 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -4,11 +4,6 @@ package net.sourceforge.pmd.lang.java.types; -import static net.sourceforge.pmd.lang.java.types.TypeOps.typeArgContains; -import static net.sourceforge.pmd.util.OptionalBool.NO; -import static net.sourceforge.pmd.util.OptionalBool.UNKNOWN; -import static net.sourceforge.pmd.util.OptionalBool.YES; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -22,14 +17,9 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.pcollections.ConsPStack; -import org.pcollections.HashTreePSet; -import org.pcollections.PSet; -import org.pcollections.PStack; import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar; import net.sourceforge.pmd.util.CollectionUtil; -import net.sourceforge.pmd.util.OptionalBool; /** * Helper class for {@link TypeSystem#lub(Collection)} and {@link TypeSystem#glb(Collection)}. @@ -70,10 +60,10 @@ final class Lub { /** * The "relevant" parameterizations of G, Relevant(G), is: * - *

+     * 
{@code
      * Relevant(G) = { V | 1 ≤ i ≤ k: V in ST(Ui) and V = G<...> }
      *             = { V ∈ stunion | V = G<...> }
-     * 
+ * }
* *

G must be erased (raw). * @@ -221,12 +211,6 @@ final class Lub { */ private JTypeMirror lcta(JTypeMirror t, JTypeMirror s) { - if (typeArgContains(t, s).somehow()) { - return t; - } else if (typeArgContains(s, t).somehow()) { - return s; - } - TypePair pair = new TypePair(t, s); if (lubCache.add(pair)) { @@ -336,73 +320,52 @@ final class Lub { List bounds = new ArrayList<>(mostSpecific); - JTypeMirror ck = null; // Ck is the primary bound - int primaryIdx = 0; + JTypeMirror primaryBound = null; - OptionalBool retryWithCaptureBounds = NO; - PSet cvarLowers = HashTreePSet.empty(); - PStack cvarsToRemove = ConsPStack.empty(); - JTypeMirror lastBadClass = null; for (int i = 0; i < bounds.size(); i++) { JTypeMirror ci = bounds.get(i); - if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { - throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); - } if (isExclusiveIntersectionBound(ci)) { // either Ci is an array, or Ci is a class, or Ci is a type var (possibly captured) // Ci is not unresolved - if (ck == null) { - ck = ci; - primaryIdx = i; + if (primaryBound == null) { + primaryBound = ci; + // move primary bound first + Collections.swap(bounds, 0, i); } else { - JTypeMirror lower = cvarLowerBound(ci); - if (lower != ci && lower != ts.NULL_TYPE) { // NOPMD CompareObjectsWithEquals - cvarLowers = cvarLowers.plus(lower); - cvarsToRemove = cvarsToRemove.plus(ci); - retryWithCaptureBounds = YES; - } else { - retryWithCaptureBounds = retryWithCaptureBounds == YES ? YES - : UNKNOWN; - } - lastBadClass = ci; + throw new IllegalArgumentException( + "Bad intersection, unrelated class types " + ci + " and " + primaryBound + " in " + types + ); } } } - switch (retryWithCaptureBounds) { // when several capture variables were found - case YES: - bounds.removeAll(cvarsToRemove); - bounds.addAll(cvarLowers); - return glb(ts, bounds); - case NO: - break; - default: - throw new IllegalArgumentException( - "Bad intersection, unrelated class types " + lastBadClass + " and " + ck + " in " + types); - } - - if (ck == null) { + if (primaryBound == null) { if (bounds.size() == 1) { return bounds.get(0); } - ck = ts.OBJECT; - } else { - // move primary bound first - Collections.swap(bounds, 0, primaryIdx); + primaryBound = ts.OBJECT; } - return new JIntersectionType(ts, ck, bounds); + return new JIntersectionType(ts, primaryBound, bounds); + } + + private static void checkGlbComponent(Collection types, JTypeMirror ci) { + if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { + throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); + } } private static @NonNull List flattenRemoveTrivialBound(Collection types) { - ArrayList bounds = new ArrayList<>(types.size()); + List bounds = new ArrayList<>(types.size()); for (JTypeMirror type : types) { // flatten intersections: (A & (B & C)) => (A & B & C) if (type instanceof JIntersectionType) { bounds.addAll(((JIntersectionType) type).getComponents()); } else { + checkGlbComponent(types, type); + if (!type.isTop()) { bounds.add(type); } @@ -418,12 +381,5 @@ final class Lub { && (ci.getSymbol() == null || !ci.getSymbol().isUnresolved()); } - private static JTypeMirror cvarLowerBound(JTypeMirror t) { - if (t instanceof JTypeVar && ((JTypeVar) t).isCaptured()) { - return ((JTypeVar) t).getLowerBound(); - } - return t; - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java index 79b78eb95f..cd85884be7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java @@ -9,14 +9,11 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - /** - * A partial function built on a map + * A partial function built on a map. */ -class MapFunction implements Function { +abstract class MapFunction implements Function { private final Map map; @@ -24,7 +21,7 @@ class MapFunction implements Function { this.map = map; } - public Map getMap() { + protected Map getMap() { return map; } @@ -32,11 +29,6 @@ class MapFunction implements Function { return map.isEmpty(); } - @Override - public @Nullable R apply(@NonNull T var) { - return map.get(var); - } - @Override public String toString() { return map.entrySet().stream() diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java index 9a9b709e89..3bceea2880 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java @@ -434,7 +434,7 @@ public final class TypeOps { * implies convertibility (the conversion is technically called * "widening reference conversion"). You can check those cases using: * - * {@link #bySubtyping() t.isConvertibleTo(s).naturally()} + * {@link #bySubtyping() t.isConvertibleTo(s).bySubtyping()} * *

Unchecked conversion may go backwards from subtyping. For example, * {@code List} is a subtype of the raw type {@code List}, and @@ -475,8 +475,8 @@ public final class TypeOps { * {@code T <: |S|} and {@code T For example, {@code List} is a subtype of the raw * {@code Collection}, not a subtype of {@code Collection}, diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java index 979352d446..3eb68145b9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java @@ -143,8 +143,7 @@ abstract class TypeVarImpl implements JTypeVar { private static final int PRIME = 997; // largest prime less than 1000 - private final JWildcardType wildcard; - private final int captureId = hashCode() % PRIME; + private final @NonNull JWildcardType wildcard; private JTypeMirror upperBound; private JTypeMirror lowerBound; @@ -160,10 +159,6 @@ abstract class TypeVarImpl implements JTypeVar { this.wildcard = wild; } - public JWildcardType getWildcard() { - return wildcard; - } - void setUpperBound(@NonNull JTypeMirror upperBound) { this.upperBound = upperBound; } @@ -195,7 +190,7 @@ abstract class TypeVarImpl implements JTypeVar { } @Override - public @Nullable JWildcardType getCapturedOrigin() { + public JWildcardType getCapturedOrigin() { return wildcard; } @@ -232,7 +227,7 @@ abstract class TypeVarImpl implements JTypeVar { @Override public @NonNull String getName() { - return "capture#" + captureId + " of " + wildcard; + return "capture#" + hashCode() % PRIME + " of " + wildcard; } } } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 650b24d428..767431d2fc 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -67,11 +67,11 @@ public class Outer { language="java" since="5.5.4" maximumLanguageVersion="10" - message="Avoid autogenerated methods to access private fields and methods of inner / outer classes" + message="Consider giving this member package visibility to access it from {0} without a synthetic accessor method" class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> -When accessing a private field / method from another class, the Java compiler will generate a accessor methods +When accessing private fields / methods from another class, the Java compiler will generate accessor methods with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can be avoided by changing the visibility of the field / method from private to package-private. @@ -138,7 +138,7 @@ public class Foo { @@ -184,11 +184,7 @@ Avoid printStackTrace(); use a logger call instead. @@ -327,7 +323,7 @@ if held within objects with long lifetimes. @@ -406,7 +402,7 @@ better placed in classes or enums. See Effective Java, item 19. @@ -451,9 +447,7 @@ By convention, the default label should be the last label in a switch statement. @@ -496,7 +490,7 @@ public class Foo { @@ -800,7 +794,7 @@ public class Foo extends TestCase { language="java" since="5.0" message="Unit tests should not contain more than ${maximumAsserts} assert(s)." - class="net.sourceforge.pmd.lang.rule.XPathRule" + class="net.sourceforge.pmd.lang.java.rule.bestpractices.JUnitTestContainsTooManyAssertsRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#junittestcontainstoomanyasserts"> Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which @@ -810,25 +804,6 @@ Customize the maximum number of assertions used by this Rule to suit your needs. This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods starting with "test". 3 - - - - - $maximumAsserts] -]]> - - - - - -Position literals first in comparisons, if the second argument is null then NullPointerExceptions -can be avoided, they will just return false. - -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. - - 3 - - - - - - - -Position literals first in comparisons, if the second argument is null then NullPointerExceptions -can be avoided, they will just return false. - -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. - - 3 - - - - - @@ -1610,24 +1522,12 @@ more specific methods, like assertNull, assertNotNull. @@ -1662,24 +1562,12 @@ by more specific methods, like assertSame, assertNotSame. @@ -1711,13 +1599,12 @@ When asserting a value is the same as a literal or Boxed boolean, use assertTrue @@ -1909,10 +1796,10 @@ a block `{}` is sufficient. 3 - -//DoStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral] | -//WhileStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral[@True = false()]] - + diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index ad5337c4fd..036d47bf0c 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -37,12 +37,27 @@ public class Foo { language="java" since="1.5" message="Avoid using dollar signs in variable/method/class/interface names" - class="net.sourceforge.pmd.lang.java.rule.codestyle.AvoidDollarSignsRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#avoiddollarsigns"> Avoid using dollar signs in variable/method/class/interface names. 3 + + + + + + + @@ -101,8 +116,8 @@ visibility cannot be reduced). Clarify your intent by using private or package a @@ -130,7 +145,8 @@ and increases the maintenance burden. 2 - //Name[starts-with(@Image,'System.loadLibrary')] + //MethodCall[TypeExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.System')] + and @MethodName = 'loadLibrary'] @@ -172,10 +188,7 @@ prefix for these methods. //MethodDeclaration [starts-with(@Name, 'get')] [@Arity = 0 or $checkParameterizedMethods = true()] - [ - ResultType/Type/PrimitiveType[@Image = 'boolean'] - and not(../Annotation//Name[@Image = 'Override']) - ] + [ PrimitiveType[@Kind = 'boolean'] and @Overridden = false() ] ]]> @@ -206,9 +219,8 @@ another constructor (such as an overloaded constructor) is called, this rule wil @@ -355,27 +367,27 @@ boolean bar(int x, int y) { 1 and not(self::Block or self::IfStatement)] + [ $checkSingleIfStmt + (: Inside this (...) is the definition of a "single if statement" :) + or not(parent::*/@Else = false() (: No else stmt :) + (: Not the last branch of an 'if ... else if' chain :) + and not(parent::IfStatement[parent::IfStatement]))] + | (: Reports case labels if one of their subordinate statements is not braced :) - //SwitchLabel[$checkCaseStmt] - [count(following-sibling::BlockStatement except following-sibling::SwitchLabel[1]/following-sibling::BlockStatement) > 1 - or (some $stmt (: in only the block statements until the next label :) - in following-sibling::BlockStatement except following-sibling::SwitchLabel[1]/following-sibling::BlockStatement - satisfies not($stmt/Statement/Block))] + //SwitchFallthroughBranch[$checkCaseStmt] + [count(*) > 1 and (count(*) > 2 or not(child::*[2]/self::Block))] ]]> @@ -408,21 +420,19 @@ The rule allows methods and fields annotated with Guava's @VisibleForTesting and @@ -434,12 +444,23 @@ or MethodDeclaration[@PackagePrivate= true()] language="java" since="0.5" message="Avoid importing anything from the package 'java.lang'" - class="net.sourceforge.pmd.lang.java.rule.codestyle.DontImportJavaLangRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#dontimportjavalang"> Avoid importing anything from the package 'java.lang'. These classes are automatically imported (JLS 7.5.3). 4 + + + + + + @@ -535,7 +549,7 @@ public abstract class ShouldBeAbstract { @@ -628,11 +642,7 @@ Some for loops can be simplified to while loops, this makes them more concise. @@ -702,10 +712,10 @@ Names for references to generic values should be limited to a single uppercase l 1 +//TypeParameter[ + string-length(@Name) > 1 or - upper-case(@Image) != @Image + upper-case(@Name) != @Name ] ]]> @@ -844,14 +854,8 @@ The Local Home interface of a Session EJB should be suffixed by 'LocalHome'. @@ -882,14 +886,8 @@ The Local Interface of a Session EJB should be suffixed by 'Local'. @@ -1011,18 +1009,11 @@ The EJB Specification states that any MessageDrivenBean or SessionBean should be @@ -1097,7 +1088,7 @@ Detects when a class, interface, enum or annotation does not have a package defi 3 - /CompilationUnit[not(./PackageDeclaration)]/TypeDeclaration[1] + /CompilationUnit[not(PackageDeclaration)]/*[pmd-java:nodeIs("AnyTypeDeclaration")][1] @@ -1131,14 +1122,9 @@ public class ClassInDefaultPackage { 3 - //PackageDeclaration/Name[lower-case(@Image)!=@Image] + //PackageDeclaration[lower-case(@Name) != @Name] @@ -1259,17 +1245,8 @@ Remote Interface of a Session EJB should not have a suffix. @@ -1305,14 +1282,8 @@ A Remote Home interface type of a Session EJB should be suffixed by 'Home'. @@ -1402,13 +1373,13 @@ Fields, local variables, or parameter names that are very short are not helpful @@ -1471,12 +1442,22 @@ import static Yoko; // Too much ! language="java" since="6.2.0" message="Avoid the use of value in annotations when it's the only element" - class="net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryAnnotationValueElementRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#unnecessaryannotationvalueelement"> Avoid the use of value in annotations when it's the only element. 3 + + + + + + + + @@ -1830,8 +1807,8 @@ E.g. `int[] x = new int[] { 1, 2, 3 };` can be written as `int[] x = { 1, 2, 3 } diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index 8b743427ae..411be40ab8 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -1144,8 +1144,7 @@ Avoid unnecessary comparisons in boolean expressions, they serve no purpose and diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml index ccdc5a3301..cd74404fa7 100644 --- a/pmd-java/src/main/resources/category/java/documentation.xml +++ b/pmd-java/src/main/resources/category/java/documentation.xml @@ -96,12 +96,12 @@ and unintentional empty constructors. diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 5121280ffc..fa98864f01 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -247,13 +247,20 @@ public class Foo { language="java" since="1.2" message="A catch statement should never catch throwable since it includes errors." - class="net.sourceforge.pmd.lang.java.rule.errorprone.AvoidCatchingThrowableRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#avoidcatchingthrowable"> Catching Throwable errors is not recommended since its scope is very broad. It includes runtime issues such as OutOfMemoryError that should be exposed and managed separately. 3 + + + + + @@ -576,7 +575,7 @@ public void bar() { @@ -584,6 +583,14 @@ The use of multiple unary operators may be problematic, and/or confusing. Ensure that the intended usage is not a bug, or consider simplifying the expression. 2 + + + + + Use equals() to compare object references; avoid comparing them with ==. 3 + + + + + + + + @@ -1521,7 +1546,7 @@ Empty finally blocks serve no purpose and should be removed. @@ -1555,7 +1580,7 @@ Empty If Statement finds instances where a condition is checked but nothing is d @@ -1613,7 +1638,7 @@ Empty block statements serve no purpose and should be removed. 3 - //BlockStatement/Statement/Block[not(*)] + //Block[not(*)][parent::Block or parent::SwitchFallthroughBranch or parent::SwitchArrowBranch] @@ -1650,13 +1675,11 @@ and should be removed. @@ -1714,7 +1737,7 @@ Empty synchronized blocks serve no purpose and should be removed. 3 - //SynchronizedStatement/Block[1][not(*)] + //SynchronizedStatement/Block[not(*)] @@ -1744,7 +1767,7 @@ Avoid empty try blocks - what's the point? @@ -1779,7 +1802,7 @@ a while loop that does a lot in the exit expression, rewrite it to make it clear @@ -1809,19 +1832,7 @@ Tests for null should not use the equals() method. The '==' operator should be u @@ -3315,11 +3326,8 @@ Use the equals() method instead. diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index e1a7a30368..ad13849a9c 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -23,7 +23,7 @@ gets it. 3 - //MethodDeclaration[@Synchronized = true()] + //MethodDeclaration[pmd-java:modifiers() = "synchronized"] @@ -87,8 +87,8 @@ it contains methods that are not thread-safe. @@ -121,7 +121,7 @@ the volatile keyword should not be used for maintenance purpose and portability. 2 - //FieldDeclaration[@Volatile = true()] + //FieldDeclaration[pmd-java:modifiers() = "volatile"] @@ -150,9 +150,16 @@ Also EJB's might be moved between machines in a cluster and only managed resourc @@ -207,17 +214,7 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co @@ -348,8 +345,7 @@ perform efficient map reads without blocking other threads. @@ -384,15 +380,7 @@ one is chosen. The thread chosen is arbitrary; thus its usually safer to call n diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 25c7cb9209..ecdc51230d 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -24,7 +24,7 @@ It is much better to use one of the type-specific toString() methods instead. @@ -209,7 +209,7 @@ that one covers both. @@ -368,9 +363,7 @@ Note that new Byte() is deprecated since JDK 9 for that reason. @@ -544,9 +537,7 @@ Note that new Integer() is deprecated since JDK 9 for that reason. @@ -576,9 +567,7 @@ Note that new Long() is deprecated since JDK 9 for that reason. @@ -694,16 +683,9 @@ ensure that the string is not empty by making an additional check first. @@ -740,9 +722,7 @@ Note that new Short() is deprecated since JDK 9 for that reason. @@ -777,12 +757,19 @@ private String bar = new String("bar"); // just do a String bar = "bar"; language="java" since="1.0" message="Avoid calling toString() on String objects; this is unnecessary." - class="net.sourceforge.pmd.lang.java.rule.performance.StringToStringRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#stringtostring"> Avoid calling toString() on objects already known to be string instances; this is unnecessary. 3 + + + + //MethodCall[pmd-java:matchesSig("java.lang.String#toString()")] + + + @@ -881,9 +866,7 @@ ArrayList is a much better Collection implementation than Vector if thread-safe @@ -1105,14 +1088,22 @@ public class Foo { Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toString().equals("") or StringBuffer.toString().length() == ... 3 + + + + //MethodCall[pmd-java:matchesSig('_#length()') + and MethodCall[pmd-java:matchesSig('java.lang.CharSequence#toString()')]] + + + - - diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java index 80424834d3..4de7893d34 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java @@ -9,7 +9,6 @@ import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSuppressed; import org.junit.Test; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -38,9 +37,7 @@ public class ExcludeLinesTest extends BaseNonParserTest { @Test public void testAlternateMarker() { - ParserOptions options = new ParserOptions(); - options.setSuppressMarker("FOOBAR"); - Report rpt = java.withParserOptions(options).executeRule(getRule(), TEST3); + Report rpt = java.withSuppressMarker("FOOBAR").executeRule(getRule(), TEST3); assertSize(rpt, 0); assertSuppressed(rpt, 1); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index c3ba3904f0..4f8ed3223a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -14,8 +14,8 @@ import org.junit.Test; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { @Test - public void testExclusionOfUselessParantheses() throws RuleSetNotFoundException { - RuleSetReferenceId ref = createRuleSetReferenceId( + public void testExclusionOfUselessParantheses() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "Custom ruleset for tests\n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); Rule rule = ruleset.getRuleByName("UselessParentheses"); assertNull(rule); } 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 3e82902345..2fc217381d 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 @@ -7,7 +7,6 @@ package net.sourceforge.pmd.cli; import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.IOException; import java.util.regex.Pattern; import org.junit.Assert; @@ -39,7 +38,7 @@ public class CLITest extends BaseCLITest { } @Test - public void changeJavaVersion() throws IOException { + public void changeJavaVersion() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-version", "1.5", "-language", "java", "-debug", }; String resultFilename = runTest(args, "chgJavaVersion"); @@ -54,14 +53,14 @@ public class CLITest extends BaseCLITest { } @Test - public void exitStatusWithViolations() throws IOException { + public void exitStatusWithViolations() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", }; String resultFilename = runTest(args, "exitStatusWithViolations", 4); assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); } @Test - public void exitStatusWithViolationsAndWithoutFailOnViolations() throws IOException { + public void exitStatusWithViolationsAndWithoutFailOnViolations() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", "-failOnViolation", "false", }; String resultFilename = runTest(args, "exitStatusWithViolationsAndWithoutFailOnViolations", 0); assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); @@ -71,7 +70,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRuleset() throws Exception { + public void testWrongRuleset() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); @@ -85,7 +84,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRulesetWithRulename() throws Exception { + public void testWrongRulesetWithRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml/UseCollectionIsEmpty", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); @@ -99,7 +98,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRulename() throws Exception { + public void testWrongRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml/ThisRuleDoesNotExist", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 23de5ce3de..9712174595 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -8,15 +8,13 @@ import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetLoader; public class PMD5RulesetTest { @Test - public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetLoader().toFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); + public void loadRuleset() { + RuleSet ruleset = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); Assert.assertNull(ruleset.getRuleByName("GuardLogStatement")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index b72ee13766..c72a41b24e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -15,9 +15,7 @@ import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetLoader; -import net.sourceforge.pmd.RuleSetNotFoundException; public class QuickstartRulesetTest { @@ -26,15 +24,15 @@ public class QuickstartRulesetTest { @After public void cleanup() { - Handler[] handlers = Logger.getLogger(RuleSetFactory.class.getName()).getHandlers(); + Handler[] handlers = Logger.getLogger(RuleSetLoader.class.getName()).getHandlers(); for (Handler handler : handlers) { - Logger.getLogger(RuleSetFactory.class.getName()).removeHandler(handler); + Logger.getLogger(RuleSetLoader.class.getName()).removeHandler(handler); } } @Test - public void noDeprecations() throws RuleSetNotFoundException { - Logger.getLogger(RuleSetFactory.class.getName()).addHandler(new Handler() { + public void noDeprecations() { + Logger.getLogger(RuleSetLoader.class.getName()).addHandler(new Handler() { @Override public void publish(LogRecord record) { Assert.fail("No Logging expected: " + record.getMessage()); @@ -49,7 +47,8 @@ public class QuickstartRulesetTest { } }); - RuleSet quickstart = new RuleSetLoader().enableCompatibility(false).loadFromResource("rulesets/java/quickstart.xml"); + RuleSetLoader ruleSetFactory = new RuleSetLoader().enableCompatibility(false); + RuleSet quickstart = ruleSetFactory.loadFromResource("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java index 2d1dc1cb61..08077df94a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java @@ -16,6 +16,7 @@ import org.junit.Ignore; import org.junit.Test; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.JavaParsingHelper; @Ignore("This test is Java specific even though parts of it should apply to any language implementation") @@ -26,19 +27,19 @@ public class SimpleNodeTest extends BaseParserTest { @Test public void testMethodDiffLines() { List methods = java.getNodes(ASTMethodDeclaration.class, METHOD_DIFF_LINES); - verifyNode(methods.iterator().next(), 2, 9, 4, 3); + TestUtilsKt.assertPosition(methods.iterator().next(), 2, 9, 4, 3); } @Test public void testMethodSameLine() { List methods = java.getNodes(ASTMethodDeclaration.class, METHOD_SAME_LINE); - verifyNode(methods.iterator().next(), 2, 9, 2, 22); + TestUtilsKt.assertPosition(methods.iterator().next(), 2, 9, 2, 22); } @Test public void testNoLookahead() { List uCD = java.getNodes(ASTClassOrInterfaceDeclaration.class, NO_LOOKAHEAD); - verifyNode(uCD.iterator().next(), 1, 8, 1, 21); + TestUtilsKt.assertPosition(uCD.iterator().next(), 1, 8, 1, 21); } @Test @@ -69,7 +70,7 @@ public class SimpleNodeTest extends BaseParserTest { public void testColumnsOnQualifiedName() { for (Node node : java.getNodes(ASTName.class, QUALIFIED_NAME)) { if (node.getImage().equals("java.io.File")) { - verifyNode(node, 1, 8, 1, 20); + TestUtilsKt.assertPosition(node, 1, 8, 1, 20); } } } @@ -78,10 +79,10 @@ public class SimpleNodeTest extends BaseParserTest { public void testLineNumbersForNameSplitOverTwoLines() { for (Node node : java.getNodes(ASTName.class, BROKEN_LINE_IN_NAME)) { if (node.getImage().equals("java.io.File")) { - verifyNode(node, 1, 8, 2, 5); + TestUtilsKt.assertPosition(node, 1, 8, 2, 5); } if (node.getImage().equals("Foo")) { - verifyNode(node, 2, 15, 2, 19); + TestUtilsKt.assertPosition(node, 2, 15, 2, 19); } } } @@ -256,13 +257,6 @@ public class SimpleNodeTest extends BaseParserTest { assertTrue(nodes.get(0) instanceof ASTFieldDeclaration); } - private void verifyNode(Node node, int beginLine, int beginCol, int endLine, int endCol) { - assertEquals("Unexpected beginning line: ", beginLine, node.getBeginLine()); - assertEquals("Unexpected beginning column: ", beginCol, node.getBeginColumn()); - assertEquals("Unexpected ending line:", endLine, node.getEndLine()); - assertEquals("Unexpected ending column:", endCol, node.getEndColumn()); - } - private static final String HAS_EXPLICIT_EXTENDS = "public class Test extends Foo {}"; private static final String NO_EXPLICIT_EXTENDS = "public class Test {}"; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java index 481f730528..73faf421b8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AccessorClassGenerationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java index 6fbc2e6676..678eac6042 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AccessorMethodGenerationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java index 6eac549ace..37d37625e7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidMessageDigestFieldTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java index e17a23785b..0a52f862d1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidPrintStackTraceTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java index 87e128b4c2..88219b9257 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidStringBufferFieldTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java index 6e8e65b3a1..e72544c3fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ConstantsInInterfaceTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java index d318d09aaf..7c8d1c663a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DefaultLabelNotLastInSwitchStmtTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java index cb88f5816b..2e7b29a342 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoubleBraceInitializationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java index 79c05a8951..1bd0203640 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ForLoopVariableCountTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java index 93a3be36b1..ab7c80a5be 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitAssertionsShouldIncludeMessageTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java index 12cd714f75..76656a65d0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitTestContainsTooManyAssertsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java index 307970a3c7..373e30c109 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitTestsShouldIncludeAssertTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java index 4419c0a53c..6cef9b5e22 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitUseExpectedTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java index 6977e9638d..afec19e09a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LiteralsFirstInComparisonsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsTest.java deleted file mode 100644 index 2cafcde078..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -@org.junit.Ignore("Rule has not been updated yet") -public class PositionLiteralsFirstInCaseInsensitiveComparisonsTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsTest.java deleted file mode 100644 index cddaf17808..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -@org.junit.Ignore("Rule has not been updated yet") -public class PositionLiteralsFirstInComparisonsTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java index 008181fb35..bbec6a1612 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SwitchStmtsShouldHaveDefaultTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java index 80a086254d..4df42d6789 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertEqualsInsteadOfAssertTrueTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java index c67101252c..a593a9e0ff 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertNullInsteadOfAssertTrueTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java index ba0d3684bf..e874a309b1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertSameInsteadOfAssertTrueTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java index a445fbd465..cb8ded71bf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertTrueInsteadOfAssertEqualsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java index 3dc87e3f32..f941133e82 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseCollectionIsEmptyTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java index ffbbe9192e..31fcad9210 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class WhileLoopWithLiteralBooleanTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java new file mode 100644 index 0000000000..7236e9d73d --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a; + +public class PackagePrivateMethod { + + // package private + void printMessage() { + System.out.println("Click"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java new file mode 100644 index 0000000000..2f5ab59a9b --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a; + +public class PackagePrivateMethodRealExtend extends PackagePrivateMethod { + + // package private, does override + @Override + void printMessage() { + System.out.println("Click"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java new file mode 100644 index 0000000000..1ff65f10a8 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.b; + +import net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a.PackagePrivateMethod; + +public class PackagePrivateMethodExtend extends PackagePrivateMethod { + // does not override + void printMessage() { + System.out.println("Hack"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java index e03e264b84..129fe92fa9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AtLeastOneConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsTest.java index 98c36504d6..1d8e87383c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidDollarSignsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java index 698650369f..1f22c1d607 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidProtectedFieldInFinalClassTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java index 3ba8d54d1e..61bb110a08 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidProtectedMethodInFinalClassNotExtendingTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java index 2a7030ae70..8886751443 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingNativeCodeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java index 2b5d55e37c..c6c6734dc8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class BooleanGetMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java index 38a6eb2be2..05b5cf0c8c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CallSuperInConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java index 6cf69fb473..81605d543f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ClassNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java index 8d978e150c..a023f70921 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ConfusingTernaryTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java index cfaee67e57..c7ff86946e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ControlStatementBracesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java index 03e4db86ca..19c6c714fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DefaultPackageTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangTest.java index d727e34d9c..5c0060bcba 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DontImportJavaLangTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java index a9fcbdc7dd..8d2ca65fdc 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyMethodInAbstractClassShouldBeAbstractTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java index 9dadb1123c..d961107c92 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ExtendsObjectTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java index 2141b9496a..c2ab5652a2 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FieldNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java index 3bc2994c77..646e03a853 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ForLoopShouldBeWhileLoopTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java index 5cb25d09b3..11ecb73e9a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FormalParameterNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java index 8b9f5e59c8..f7357c2542 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class GenericsNamingTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java index 2518a739b9..11d40a1788 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalHomeNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java index 5ed4ddb40a..be1832f299 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalInterfaceSessionNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java index 23f2a469fc..bee83930e8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalVariableNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java index cf115f9d72..793af6fa63 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LongVariableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java index d9c5373a22..e12899a61d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MDBAndSessionBeanNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java index 2af54b09d6..c9f328d06f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MethodNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java index cd513a7cf9..54b1246e32 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class NoPackageTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java index 989872db79..0b59065adb 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class PackageCaseTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java index 57026550ba..1d0c445aea 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RemoteInterfaceNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java index 329b25c412..7e95cb5d1a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RemoteSessionInterfaceNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java index e8af416afb..1c4bdc49c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortClassNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java index 94b3735ddf..7133947372 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java index 333dc77c50..2d29ac3442 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortVariableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java index 4794450818..43af1eac98 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TooManyStaticImportsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementTest.java index 0501fec771..1e41e5769d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryAnnotationValueElementTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java index cf2558d8f4..b2cf45becf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java index fe745b690a..be1a80e550 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseShortArrayInitializerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseUnderscoresInNumericLiteralsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseUnderscoresInNumericLiteralsTest.java index 308eb3de8f..ecaaba4bf3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseUnderscoresInNumericLiteralsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseUnderscoresInNumericLiteralsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseUnderscoresInNumericLiteralsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java index 9425ca6585..19f7b4bf6e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessQualifiedThisTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java index 3f30017e80..ef90b6a888 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifyBooleanExpressionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/UncommentedEmptyConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/UncommentedEmptyConstructorTest.java index 0569522532..c8753d83bd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/UncommentedEmptyConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/UncommentedEmptyConstructorTest.java @@ -4,12 +4,8 @@ package net.sourceforge.pmd.lang.java.rule.documentation; -import org.junit.Ignore; - import net.sourceforge.pmd.testframework.PmdRuleTst; -@Ignore("Ignored until modifiers (@Private, etc) are made accessible " - + "to XPath rules without deprecation warning") public class UncommentedEmptyConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java index 3ecd614152..8f1d8d151d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidAssertAsIdentifierTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableTest.java index d0574bd722..f05ef97555 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidCatchingThrowableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java index 94f169f12e..81d14c3042 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidDecimalLiteralsInBigDecimalConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java index aa38545c85..0c936de878 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidEnumAsIdentifierTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsTest.java index 44b3e8ce2f..5ac541e089 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidMultipleUnaryOperatorsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java index 0e9797fc46..f1f5744870 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CloneMethodMustImplementCloneableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java index 7fbce4fd5b..46b5b9b837 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CompareObjectsWithEqualsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java index f4d2e4661a..53e61e8226 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyCatchBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java index 66034331c1..bb81c264a3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyFinalizerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java index 415da04f7c..f076b33017 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyFinallyBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java index d5eb20180c..bf2bff63cf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyIfStmtTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java index 3583c7aa6e..32d1abfab9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyInitializerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java index 5decf78cbf..2d49e087d9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyStatementBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java index c41b98d5b5..fe4d05a8e8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyStatementNotInLoopTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java index 99c1e3de49..edf5cd3362 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptySwitchStatementsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java index eb7dfb1665..a66b28d808 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptySynchronizedBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java index b47a75b36d..3c6e1330b6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyTryBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java index 6a0488998c..d370081916 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyWhileStmtTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java index 5dcb9eba9f..e869a5d609 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EqualsNullTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java index c4e17f7a50..67b9a62bb5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitStaticSuiteTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java index cff9c119fd..c901a75521 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TestClassWithoutTestCasesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java index 01a50ed0cc..54012b3130 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessOperationOnImmutableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java new file mode 100644 index 0000000000..77a7cd62b2 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals; + +public class ClassWithFields { + private Object a; + private Object b; + + boolean test1() { + return false; + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java new file mode 100644 index 0000000000..406fc7da92 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals; + +public class CompareObjectsWithEqualsSample { + boolean bar(String a, String b) { + return false; + } + + void array(int[] a, String[] b) { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java new file mode 100644 index 0000000000..5b85f10384 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java @@ -0,0 +1,12 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.useequalstocomparestrings; + +public class ClassWithStringFields { + private String string1 = "a"; + private String string2 = "a"; + + public void bar() { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java new file mode 100644 index 0000000000..245842ba6e --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.useequalstocomparestrings; + +public class UseEqualsToCompareStringsSample { + void bar(String x) {} +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java index 1661961b6e..5c1793c6d8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidSynchronizedAtMethodLevelTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java index 6cfdfccf71..2afcbaa379 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidThreadGroupTest extends PmdRuleTst { // Used by AvoidThreadGroup test cases public static class ThreadGroup { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java index 303f0fab0b..ce61a3e5c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingVolatileTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java index 586f1dffc2..9ba3e87d61 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoNotUseThreadsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java index a3977158b5..c628ed4b78 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DontCallThreadRunTest extends PmdRuleTst { // Used by DontCallThreadRun test cases public static class TestThread extends Thread { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java index 658afce33c..201f12e621 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoubleCheckedLockingTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java index bca9699fb5..2ddf93e4e9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseConcurrentHashMapTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java index 1b16dd00f6..c03cbd61d0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseNotifyAllInsteadOfNotifyTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java index f274a1c79a..c230229435 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AddEmptyStringTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java index d3d01c6875..d289d83430 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AppendCharacterWithCharTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidFileStreamTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidFileStreamTest.java index ab79e829e6..592afe9c97 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidFileStreamTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidFileStreamTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidFileStreamTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java index ea6421d25c..5a639a8a74 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingShortTypeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java index 4dbc1dac69..2db2199779 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ByteInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java index 6d3628ad5b..3adeb48d5b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class InefficientEmptyStringCheckTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java index 0b874c069a..a951c8eda7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class IntegerInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java index c21a845ad7..a2b951cb00 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LongInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java index 263ecd0b79..0a3548a3f6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RedundantFieldInitializerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java index 6516321a80..564c94b6fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java index dea1a15667..e2a3fbcd15 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifyStartsWithTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java index f5300916e1..fc6fdbdd97 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class StringInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java index efc2a971e2..730ab7469e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TooFewBranchesForASwitchStatementTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java index 8a1a78ed6d..296890c709 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseArrayListInsteadOfVectorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java index b79726c7d2..401a76f34b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseIndexOfCharTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java index c09449b7e7..1d7ebb58d6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseStringBufferLengthTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java index bb24f281d4..ef7221eafa 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessStringValueOfTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index bf5e264856..b7306f39c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import java.util.function.Consumer; @@ -15,6 +16,7 @@ import org.junit.Assert; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -24,7 +26,7 @@ import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class BaseXPathFunctionTest extends BaseNonParserTest { @@ -57,10 +59,13 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { Rule rule = makeXpathRuleFromXPath(xpath); - PmdXPathException thrown = Assert.assertThrows(PmdXPathException.class, () -> executeRule(rule, code)); + FileAnalysisException thrown = Assert.assertThrows(FileAnalysisException.class, () -> executeRule(rule, code)); - exceptionSpec.accept(thrown); - assertThat(thrown.getRuleName(), equalTo(RULE_NAME_PLACEHOLDER)); + assertThat(thrown.getCause(), instanceOf(PmdXPathException.class)); + + PmdXPathException cause = (PmdXPathException) thrown.getCause(); + exceptionSpec.accept(cause); + assertThat(cause.getRuleName(), equalTo(RULE_NAME_PLACEHOLDER)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java index ab5afb58b6..37b67fd77b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java index fed206c337..3079fb7071 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java @@ -10,7 +10,7 @@ import net.sourceforge.pmd.Rule; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class HasAnnotationXPathTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java index 55eba4ef8e..edc812faaa 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java @@ -4,13 +4,18 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; +import static org.junit.Assert.assertEquals; + +import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { @@ -53,5 +58,13 @@ public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { assertFinds(rule, 0, "enum O {; { \"\".substring(1, 2); this.foo(1, 'c');} void foo(int a, int b) {} }"); } + @Test + public void testMatchInvalidSig() { + Rule rule = makeXpathRuleFromXPath("//*[pmd-java:matchesSig('_#')]"); + + PmdXPathException e = Assert.assertThrows(PmdXPathException.class, rule::getTargetSelector); + assertEquals(Phase.INITIALIZATION, e.getPhase()); + } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java index a93edfd99a..97a2b9a69c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class NodeIsFunctionTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java new file mode 100644 index 0000000000..2f96c75897 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java @@ -0,0 +1,48 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; + +/** + * @author Clément Fournier + * @since 7.0.0 + */ +public class TypeIsFunctionTest extends BaseXPathFunctionTest { + + + @Test + public void testHasAnnotation() { + Rule rule = makeXpathRuleFromXPath("//Annotation[pmd-java:typeIs('java.lang.Override')]"); + assertFinds(rule, 1, "interface O { @Override void foo(); }"); + } + + + @Test + public void testHasAnnotationNonQual() { + Rule rule = makeXpathRuleFromXPath("//Annotation[pmd-java:typeIs('Override')]"); + + //does not match + assertFinds(rule, 0, "interface O { @Override void foo(); }"); + } + + @Test + public void testTypeIsArray() { + Rule rule = makeXpathRuleFromXPath("//*[pmd-java:typeIs('int[]')]"); + + // ArrayType + VariableDeclaratorId + assertFinds(rule, 2, "class K { int[] i; }"); + } + + @Test + public void testWrongTypeReturnsFalse() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:typeIs('java.lang.Override')]"); + + assertFinds(rule, 0, "interface O { @Override void foo(); }"); + } + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java index 7c088371ae..d6f72183d5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java @@ -9,9 +9,7 @@ import static org.hamcrest.Matchers.equalTo; import org.hamcrest.MatcherAssert; import org.junit.Assert; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; @@ -20,9 +18,6 @@ import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; public class InvocationMatcherTest extends BaseNonParserTest { - @Rule - public final ExpectedException expect = ExpectedException.none(); - @Test public void testSimpleMatcher() { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt index db7fae2567..5f8b134f08 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt @@ -4,19 +4,14 @@ package net.sourceforge.pmd.lang.java.types +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly -import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe -import io.kotest.property.PropTestConfig import io.kotest.property.checkAll import io.kotest.property.forAll -import io.mockk.InternalPlatformDsl.toArray import net.sourceforge.pmd.lang.ast.test.shouldBeA -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData.* -import java.io.Serializable /** * Tests "the greatest lower bound" (glb). @@ -36,7 +31,7 @@ class GlbTest : FunSpec({ } // in particular - checkAll(ts.allTypesGen) { t -> + checkAll(ts.refTypeGen) { t -> glb(t, t) shouldBe t // regardless of what kind of type t is } } @@ -76,6 +71,24 @@ class GlbTest : FunSpec({ } + + test("Test lub of zero types") { + shouldThrow { + ts.glb(emptyList()) + } + } + + + test("Test GLB errors") { + + shouldThrow { + glb(int, t_Number) + } + shouldThrow { + glb(int, ts.OBJECT) + } + } + test("Test GLB corner cases") { glb(t_Iterable[`?` extends t_Number], t_Iterable[t_String]).shouldBeA { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt index af1c0a62fe..c863a5079c 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt @@ -4,14 +4,11 @@ package net.sourceforge.pmd.lang.java.types +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.collections.shouldContainExactly -import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import io.kotest.property.checkAll -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData import net.sourceforge.pmd.lang.java.types.testdata.LubTestData.* -import java.io.Serializable /** * Tests "least upper bound" (lub). @@ -83,10 +80,26 @@ class LubTest : FunSpec({ ) shouldBe listOf(`t_List{Integer}`, `t_List{String}`) } - test("Test lub with related type arguments") { + test("Test lub with related type arguments (LCTA)") { + + fun checkLcta(vararg components: JTypeMirror, expected: JTypeMirror) { + val l = ts.lub(components.map { GenericSub::class[it] }) as JClassType + l.typeArgs[0] shouldBe expected + } - lub(GenericSub::class[t_Integer], GenericSub::class[t_Number]) shouldBe GenericSub::class[`?` extends t_Number] lub(GenericSub::class[t_Integer], GenericSub::class[`?` extends t_Number]) shouldBe GenericSub::class[`?` extends t_Number] + + checkLcta(t_Integer, t_Number, expected = `?` extends t_Number) + checkLcta(t_Integer, `?` extends t_Number, expected = `?` extends t_Number) + checkLcta(t_Integer, `?` `super` t_Number, expected = `?` `super` t_Integer) + checkLcta(t_Integer, `?` `super` t_Integer, expected = `?` `super` t_Integer) + checkLcta(t_Integer, `?` extends t_Integer, expected = `?` extends t_Integer) + + checkLcta(`?` `super` t_Integer, `?` `super` t_Number, expected = `?` `super` t_Integer) + checkLcta(`?` extends t_Integer, `?` extends t_Number, expected = `?` extends t_Number) + checkLcta(`?` extends t_Integer, `?` `super` t_Number, expected = `?`) + checkLcta(`?` `super` t_Integer, `?` extends t_Number, expected = `?`) + } test("Test lub with identical type arguments") { @@ -109,6 +122,12 @@ class LubTest : FunSpec({ } + test("Test lub of zero types") { + shouldThrow { + ts.lub(emptyList()) + } + } + test("Test lub with interface intersection") { // this example recurses into lub(Comparable, Comparable), at which point diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java index 54858f8423..93869d1726 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java @@ -29,7 +29,7 @@ public class Foo { // class Bar @AnnotationWithParams({@Nested(1) , @Nested(2) , @Nested - }) + }) public void foo() { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java index 7b985c8bde..93852a818c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java @@ -4,8 +4,8 @@ package foo.bar.baz; // another irrelevant comment @ MyAnnotation ("ugh") @NamedQueries({ - @NamedQuery( - )}) + @NamedQuery( + )}) public class Foo {// CPD-ON diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml index a56faedb7f..7bc0e88f3a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml @@ -156,4 +156,19 @@ public class Foo { ]]> java 10 + + AccessorClassGeneration false-negative: subclass calls private constructor #1998 + 1 + + java 10 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index c6ffc8a927..d3fde8ab50 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -7,7 +7,10 @@ inner class accesses private field from outer class 1 - 8 + 2 + + Consider giving this member package visibility to access it from Foo.InnerClass without a synthetic accessor method + inner class accesses private field from outer class unqualified 1 - 8 + 2 outer class accesses private field from inner class 1 - 9 + 5 + + Consider giving this member package visibility to access it from Foo without a synthetic accessor method + inner class accesses private method of outer class, unqualified 1 - 4 + 8 inner class accesses private method of outer class, qualified 1 - 4 + 8 outer class accesses private method of inner class 1 - 8 + 3 java 10 + + check violations are not duplicated + 1 + 3 + + java 10 + + inner class accesses non-private methods of outer class 0 @@ -200,15 +227,15 @@ public class Foo implements Parcelable { #808 - [java] AccessorMethodGeneration false positives with compile time constants 4 - 25,26,27,28 + 8,9,10,11 java 10 + + AccessorMethodGeneration false positive with overloads #807 + 0 + + java 10 + + + AccessorMethodGeneration false positive with overloads #807 (control test) + 1 + + java 10 + + + AccessorMethodGeneration: Name clash with another public field not properly handled #342 + 0 + + java 10 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml index 0bb995b977..6a2915da3f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml @@ -8,8 +8,8 @@ assertArrayEquals ok 0 assertArrayEquals bad 1 0 assertThat bad 1 1 @@ -430,6 +433,40 @@ class SimpleTest { public void simpleMethod() { assertEquals(0, Integer.compare(0, 0), "Not equals 0 != 1"); } +} + ]]> + + + [java] JUnitAssertionsShouldIncludeMessage false positive with method call #2883 + 0 + + + + JUnitAssertionsShouldIncludeMessage false positive with AssertJ #1565 + 0 + ()).as("Should return database entries as map") + .hasSize(3) + .containsEntry(3, 4); + } + } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml index ba19d3eea6..6bf4b9742f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml @@ -8,6 +8,7 @@ JUnit 3 Test contains no assert 0 JUnit 4 Test contains no assert 0 JUnit 3 Test contains one assert 0 JUnit 4 Test contains one assert 0 JUnit 3 Test contains more than one assert 1 JUnit 4 Test contains more than one assert 1 2 0 2 0 JUnit 5 Test contains more than one assert 5 - 10,17,24,31,39 + 11,18,25,32,40 + + Should not count unrelated asserts + 0 + + + + #1212 [java] Don't raise JUnitTestContainsTooManyAsserts on JUnit 5's assertAll + 0 + assertEquals(expResult, result), + () -> assertEquals(3, mutableInteger.getValue())); + } + } + ]]> + + + #2374 [java] False positive JUnitTestContainsTooManyAsserts for JUnit 5 assertAll() + 0 + assertEquals(2, result.x), + () -> assertEquals(4, result.y) + ); + } + } + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml index d94456d6e0..e6ab530c82 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml @@ -261,9 +261,10 @@ public class Foo_Test #285 Issues with @Rule annotation and ExpectedException 1 - 7 + 8 0 0 + + [java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods #2147 + 0 + assertTrue(item.size() <= totalFieldsCount)); + } + + public static JsonNode assertSuccessResponse() { + JsonNode jsonNode = doStuffToBuildTheJsonNode(); + assertTrue(jsonNode.hasNonNull("count")); + assertTrue(jsonNode.hasNonNull("data")); + assertTrue(jsonNode.get("count").asInt() > 0); + assertEquals(jsonNode.get("count").asInt(), jsonNode.get("data").size()); + return jsonNode; + } +} + ]]> + + + [java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field #1422 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index f76ba2658f..4155cb0c92 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -30,7 +30,7 @@ public class Foo { ok, empty literal in .equals comparison - 0 + 4 bad, testing false negative at the end of a chain 1 @@ -315,6 +316,22 @@ public class Foo { if (getStr("b").equals("ab")) { } // nok if ("ab".equals(getStr("b"))) { } // ok } +} + ]]> + + + FN with unresolved types + 2 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml index 16098caa2f..70b797df76 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml @@ -573,4 +573,38 @@ public record Point(int x, int y) { ]]> java 14-preview + + Package private method cannot be overridden outside of its package #1969 (1) + 1 + + + + Package private method cannot be overridden outside of its package #1969 (2) + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInCaseInsensitiveComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInCaseInsensitiveComparisons.xml deleted file mode 100644 index fbe42d1095..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInCaseInsensitiveComparisons.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - ok, literal comes first - 0 - - - - - bad, literal comes last - 1 - - - - - ok - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml deleted file mode 100644 index 7fd74de81e..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - ok, literal comes first - 0 - - - - - bad, literal comes last - 1 - - - - - ok - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - #1256 PositionLiteralsFirstInComparisons false positive with Characters - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml index 0ecf6177f1..813e52cd08 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml @@ -78,6 +78,64 @@ public class Foo { break; } } +} + ]]> + + + + Enum type, not ok + 1 + + + + Enum type, not ok (arrow branches) + 1 + { + int y = 8; + } + case B -> throw new IllegalStateException(); + } + } +} + ]]> + + + #2806 [java] SwitchStmtsShouldHaveDefault false-positive with Java 14 non-fallthrough branches + 0 + foo = "eq"; + case "!=" -> foo = "ne"; + default -> throw new IllegalArgumentException(); + } + return foo + 'a'; + } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml index 62459c0442..bf11084399 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml @@ -1722,6 +1722,10 @@ class Foo { FP with anonymous classes on the way 0 false-positive in DD-part of DataflowAnomalyAnalysis #1675 0 formatterClass = findClass(formatterClassName); formatter = formatterClass.getDeclaredConstructor().newInstance(); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ignored) { + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException ignored) { } } - setFormatter((formatter == null) ? new SimpleFormatter() : formatter); + return ((formatter == null) ? new Formatter() : formatter); } } ]]> @@ -3345,12 +3349,12 @@ public class UnusedAssignmentNative { 0 interceptors; + class Test { + private final List interceptors; private int preProcessIndex = -1; - public void applyPreProcess(NativeWebRequest request, Callable task) throws Exception { - for (CallableProcessingInterceptor interceptor : this.interceptors) { + public void applyPreProcess(String request, java.util.concurrent.Callable task) throws Exception { + for (T interceptor : this.interceptors) { interceptor.preProcess(request, task); this.preProcessIndex++; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml index 992f5403b5..b604960de4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd"> - TEST1 + Identity test doesn't match 0 - TEST2 + Junit 3 problem 1 - TEST3 + Overload of equal doesn't match 0 - Not a JUnit test - TEST2 - 0 + JUnit 4, even outside of @Test method + 1 - JUnit4 - TEST2 + JUnit4 - match 1 JUnit 4 - assertTrue with null 1 1 JUnit Test contains assertEquals on other than boolean literal 0 JUnit Test contains assertEquals on boolean literal 5 JUnit Test contains assertEquals with Boxed booleans 8 JUnit Test contains assertEquals with Boxed booleans as param 0 fail, == 0 1 - 3 + 4 ok, isEmpty 0 fail, != 0 1 - 3 + 4 ok, !isEmpty 0 fail, != 0 1 - 3 + 4 ok, !isEmpty 0 fail, 0 == 1 - 3 + 4 fail, > 0 1 - 3 + 4 0){ @@ -133,6 +141,7 @@ public class Foo { ok, in expression 0 ok, in expression 0 5 25,28,31,34,37 #1304 UseCollectionIsEmpty false positive comparing to 1 0 c = new ArrayList(); @@ -345,6 +356,29 @@ public class Foo { public int size() { return 0; } +} + ]]> + + + #2542 UseCollectionIsEmpty can not detect the case foo.bar().size() + 1 + list = new ArrayList<>(); + + Foo foo() { return this; } + Foo bar() { return this; } + List list() { return list; } + + public void bar() { + if (foo().bar().foo().list().size() == 0) { + throw new RuntimeException("Empty list"); + } + } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml index ba3d513697..6a950e25b4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml @@ -468,7 +468,7 @@ public class Foo { - Case, dangling unbraced statement + Case, dangling unbraced statement 2 true 1 6 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml index 7a9b4a83f9..443844b561 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml @@ -59,6 +59,7 @@ public class Foo { #1410 DefaultPackage triggers on field annotated with @VisibleForTesting 0 + + + Dont flag default methods + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml index f2e821e3fd..1ee74d2ba5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml @@ -193,6 +193,8 @@ public class MyException extends RuntimeException { Exclude serialPersistentFields by default 0 1 upper case/single letter 0 +public interface Foo { } ]]> @@ -18,7 +18,7 @@ public interface Foo 2 upper case/single letter 0 +public interface Foo > { } ]]> @@ -28,7 +28,7 @@ public interface Foo 1 lower Case/single letter 1 +public interface Foo { } ]]> @@ -38,7 +38,7 @@ public interface Foo 1 lower case/multiple letter 1 +public interface Foo > { } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml index 635299aad2..de4750a949 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml @@ -8,6 +8,7 @@ Bad SessionBean name 1 @@ -16,6 +17,7 @@ public class SomeClass implements SessionBean {} Bad MessageDrivenBean name 1 @@ -24,6 +26,7 @@ public class SomeClass implements MessageDrivenBean {} Good SessionBean name 0 @@ -32,6 +35,7 @@ public class SomeBean implements SessionBean {} Good MessageDrivenBean name 0 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml index 2326457a5e..c727224ba2 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml @@ -223,6 +223,34 @@ public class Foo { public void m_fooBar() { } +} + ]]> + + + #2528 [java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest + moo_[a-z][A-Za-z]* + 1 + 14 + + The JUnit 5 test method name 'moo_str2' doesn't match 'moo_[a-z][A-Za-z]*' + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml index 92303ef71b..3d14e79550 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml @@ -153,6 +153,7 @@ public class Foo { Issue #1003: UnnecessaryConstructor triggered on required empty constructor (Dagger @Inject) 0 + + + array initialization with Object LHS type + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml index 2d11209715..fee76365db 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml @@ -8,6 +8,7 @@ simple failure true 1 + 2 + + + #2532 Expression as argument + 1 + 5 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidMultipleUnaryOperators.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidMultipleUnaryOperators.xml index f09baf3cee..36c79fe6c8 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidMultipleUnaryOperators.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidMultipleUnaryOperators.xml @@ -19,7 +19,8 @@ public class Foo { Compound - 8 + 4 + 2,3,4,5 Compound with parentheses - 8 + 4 + 2,3,4,5 Compound with parentheses an mixed operators - 9 + 3 + 2,3,4 + + #1532 [java] CloneMethodMustImplementCloneable: Implemented Interface extends Cloneable - part 2: interface + 1 + + + #1532 [java] CloneMethodMustImplementCloneable: Implemented Interface extends Cloneable 0 @@ -186,4 +196,14 @@ class CloneableClass implements TestInterface { } ]]> + + [java] CloneMethodMustImplementCloneable triggers for interfaces #1005 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 987dbbbd85..896037151d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -20,10 +20,18 @@ public class Foo { primitives are ok 0 @@ -41,22 +49,26 @@ public class Foo { - missed hit - qualified names. that's ok, we can't resolve the types yet, so better to skip this for now - 0 + Hits it + 1 - more qualified name skippage + charAt return char - that's ok 0 array element comparison - 0 + 1 + 11 @@ -107,12 +128,18 @@ public class Foo { Comparing against new object should always return false - 1 + 2 + 7,9 @@ -122,7 +149,11 @@ public class Foo { qualified call in allocation 0 + + + comparing in equals method should be ok + 0 + + + + [java] CompareObjectsWithEqualsRule: False positive with Enums #2716 + 0 + + + + [java] CompareObjectsWithEquals - false negative with type res #2880 + 2 + + + + + #2934 False negative - class with fields + 4 + 8,9,10,11 + + + + + #2934 comparison with null is valid and with primitive literals + 2 + 18,19 + + + + + #2934 this and class should be ignored + 1 + 16 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml index a2cee743d1..40006caa25 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml @@ -76,26 +76,33 @@ public class Foo { - InterruptedException is OK - 0 + InterruptedException is not OK - changed behavior in PMD7 + 1 + 7 - + public class Foo { + void foo() { + try { + } catch (InterruptedException ignored) {} + try { + } catch (InterruptedException notok) {} + } + } + ]]> + - CloneNotSupportedException is OK - 0 + CloneNotSupportedException is not OK - changed behavior in PMD7 + 1 + 7 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyStatementBlock.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyStatementBlock.xml index a1876b6fe7..03a94b2933 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyStatementBlock.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyStatementBlock.xml @@ -38,6 +38,34 @@ public class Foo { return; } } +} + ]]> + + + block in switch case + 1 + 4 + + + + block in switch case (arrow) + 1 + 4 + { } + } + } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index e001d193a4..476b7ae864 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -6,11 +6,15 @@ failure case using == - 1 + 2 + 5,6 @@ -19,8 +23,11 @@ public class Foo { failure case using != 1 + 5 using equals, OK 0 using compareTo, OK 0 using length, OK 0 + + + + #2934 false negatives with fields + 8 + 8,9,10,11,13,14,15,16 + + + + + #2934 comparison with null is valid + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml index 167fe3d4be..10e48cd971 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml @@ -10,6 +10,7 @@ useless operation on BigDecimal 1 useless operation on BigInteger 1 using the result, so OK 0 using the result in a method call, so OK 0 BigInteger obtained from compound method call 0 Using generics on List, OK 0 getSolution() { List result = new ArrayList(); @@ -92,6 +98,7 @@ public class Foo { BigInteger in conditional statement 0 1702782, Immutable used in comparison 0 BigInteger calls in expression 0 2645268, ClassCastException using Annotation on Local Field 1 Using Executors directly is not allowed - 2 + 4 + + + getContextClassLoader is ok + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml index b5609a55df..9030c6367f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml @@ -30,16 +30,30 @@ public class Foo { - #565 - inheritance use case - call to Thread().run() + [java]DontCallThreadRun can't detect the case that call run() in foo.bar.run() #2538 1 + + [java]DontCallThreadRun can't detect the case that call run() in this.run() #2537 + 1 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml index cce05f421b..c093dce805 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml @@ -8,6 +8,7 @@ Basic use case 2 #1034 UseConcurrentHashMap flags calls to methods that return Map 0 @@ -37,17 +41,30 @@ public class Foo { #1342 UseConcurrentHashMap false positive (with documentation example) 1 - 3 + 5 + + FP with Properties + 0 + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml index 4630020e38..a5c7622583 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml @@ -9,6 +9,7 @@ 1 2 + + + [java]UseNotifyAllInsteadOfNotify falsely detect a special case with argument: foo.notify(bar) #2577 + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml index a6c3759c71..9b917cbec6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml @@ -28,6 +28,18 @@ public class Foo { ]]> + + appending single char escaped + 1 + + + this is probably wrong, but shouldn't fail 0 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml index e77c303c83..2d997d0b81 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml @@ -144,6 +144,8 @@ public class Foo { String.trim().isEmpty() is called after a chain call, should have failed 2 String.trim().isEmpty() is called after a chain call, should have failed twice 2 java 14-preview + + [java] InefficientEmptyStringCheck false negative in anonymous class #1224 + 1 + 0) { + System.out.println("Non-empty string."); + } + } + + @Override + public void keyPressed(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + } + } + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml index 097ad12c1e..7d8d6fbdea 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml @@ -1434,7 +1434,7 @@ public class RedundantFieldInitializerTest { } ]]> - + #2441 can not detect a case char c = '\0'; 1 @@ -1442,6 +1442,25 @@ public class RedundantFieldInitializerTest { + + + + Non-literals are ok + 1 + 9 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml index 81e7958516..69e3638b73 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml @@ -40,7 +40,6 @@ public class Foo { ]]> - #1392 SimplifyStartsWith false-negative @@ -66,6 +64,26 @@ public class SimplifyStartsWith { || nextSibling.getType() == JavadocTokenTypes.EOF || nextSibling.getText().startsWith(" "); } + + static class DetailNode { + String getText() { return ""; } + } + +} + ]]> + + + #2712 SimplifyStartsWith false-positive on receiver != String + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml index f3870f345a..7556e7d23e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml @@ -20,6 +20,7 @@ public class Bar { TEST1 1 TEST2 1 TEST3 1 + StringBuffer.toString.equals(""), bad - 2 + 0 valueOf as first/last expression in concatenation 1 + 4 @@ -66,6 +68,34 @@ public class Foo { ]]> + + Double valueOf call, only second is reported + 1 + 4 + + + + + valueOf a string (both are reported) + 2 + 3,4 + + + [ 1977438 ] False positive for UselessStringValueOf 0 @@ -109,6 +139,19 @@ public class Test { private void print(String s) { System.out.println(s); } +} + ]]> + + + FP with char array valueOf + 0 + diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java index 100e47054a..a5381e0de5 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.ecmascript; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser; @@ -18,8 +17,8 @@ class EcmascriptHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { - return new EcmascriptParser(rhinoVersion, parserOptions.getSuppressMarker()); + public Parser getParser() { + return new EcmascriptParser(rhinoVersion); } } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java index 895d334900..c71c6691a6 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java @@ -16,7 +16,6 @@ import org.mozilla.javascript.ast.Comment; import org.mozilla.javascript.ast.ErrorCollector; import org.mozilla.javascript.ast.ParseProblem; -import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.ParseException; @@ -24,11 +23,9 @@ import net.sourceforge.pmd.lang.ast.RootNode; public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Parser { private final int esVersion; - private final String suppressMarker; - public EcmascriptParser(int version, String suppressMarker) { + public EcmascriptParser(int version) { this.esVersion = version; - this.suppressMarker = AssertionUtil.requireParamNotNull("suppression marker", suppressMarker); } private AstRoot parseEcmascript(final String sourceCode, final List parseProblems) throws ParseException { @@ -60,6 +57,7 @@ public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Pars final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(parseProblems); ASTAstRoot tree = (ASTAstRoot) treeBuilder.build(astRoot); + String suppressMarker = task.getCommentMarker(); Map suppressMap = new HashMap<>(); if (astRoot.getComments() != null) { for (Comment comment : astRoot.getComments()) { 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 4c5921329b..33a6ad5a85 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 @@ -16,7 +16,6 @@ import org.mozilla.javascript.ast.AstRoot; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ecmascript.rule.AbstractEcmascriptRule; @@ -150,9 +149,8 @@ public class EcmascriptParserTest extends EcmascriptParserTestBase { assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(2)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); - ParserOptions parserOptions = new ParserOptions(); - parserOptions.setSuppressMarker("FOOOO"); - root = js.withParserOptions(parserOptions).parse("function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n"); + root = js.withSuppressMarker("FOOOO") + .parse("function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n"); assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(3)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java index 9b54e37ab0..5516d45e0b 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.jsp; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.jsp.ast.JspParser; @@ -17,7 +16,7 @@ import net.sourceforge.pmd.lang.jsp.ast.JspParser; public class JspHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new JspParser(); } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index e83ff59988..f83570e141 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.ast.test import net.sourceforge.pmd.* -import net.sourceforge.pmd.lang.* +import net.sourceforge.pmd.lang.Language +import net.sourceforge.pmd.lang.LanguageRegistry +import net.sourceforge.pmd.lang.LanguageVersion +import net.sourceforge.pmd.lang.LanguageVersionHandler import net.sourceforge.pmd.lang.ast.* import net.sourceforge.pmd.processor.AbstractPMDProcessor import net.sourceforge.pmd.reporting.GlobalAnalysisListener @@ -13,6 +16,8 @@ import net.sourceforge.pmd.util.document.TextFile import org.apache.commons.io.IOUtils import java.io.InputStream import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Path /** * Language-independent base for a parser utils class. @@ -29,7 +34,7 @@ abstract class BaseParsingHelper, T : RootNode val defaultVerString: String?, val resourceLoader: Class<*>?, val resourcePrefix: String, - val parserOptions: ParserOptions? = null + val suppressMarker: String = PMD.SUPPRESS_MARKER, ) { companion object { @@ -59,7 +64,7 @@ abstract class BaseParsingHelper, T : RootNode else language.getVersion(version) ?: throw AssertionError("Unsupported version $version for language $language") } - private val language: Language + val language: Language get() = LanguageRegistry.getLanguage(langName) ?: throw AssertionError("'$langName' is not a supported language (available ${LanguageRegistry.getLanguages()})") @@ -91,13 +96,8 @@ abstract class BaseParsingHelper, T : RootNode clone(params.copy(resourceLoader = contextClass, resourcePrefix = resourcePrefix)) - /** - * Returns an instance of [Self] for which the [parse] methods use - * the provided [parserOptions]. - */ - fun withParserOptions(parserOptions: ParserOptions?): Self = - clone(params.copy(parserOptions = parserOptions)) - + fun withSuppressMarker(marker: String): Self = + clone(params.copy(suppressMarker = marker)) fun getHandler(version: String): LanguageVersionHandler { return getVersion(version).languageVersionHandler @@ -117,13 +117,16 @@ abstract class BaseParsingHelper, T : RootNode * so. */ @JvmOverloads - fun parse(sourceCode: String, version: String? = null): T { + fun parse(sourceCode: String, version: String? = null, filename: String = TextFile.UNKNOWN_FILENAME): T { val lversion = if (version == null) defaultVersion else getVersion(version) val handler = lversion.languageVersionHandler - val options = params.parserOptions ?: handler.defaultParserOptions - val parser = handler.getParser(options) - val textDoc = TextDocument.readOnlyString(sourceCode, TextFile.UNKNOWN_FILENAME, lversion) - val task = Parser.ParserTask(textDoc, SemanticErrorReporter.noop(), options.suppressMarker) + val parser = handler.parser + val textDoc = TextDocument.readOnlyString(sourceCode, filename, lversion) + val task = Parser.ParserTask(textDoc, SemanticErrorReporter.noop()) + task.properties.also { + handler.declareParserTaskProperties(it) + it.setProperty(Parser.ParserTask.COMMENT_MARKER, params.suppressMarker) + } val rootNode = rootClass.cast(parser.parse(task)) if (params.doProcess) { postProcessing(handler, lversion, rootNode) @@ -148,7 +151,7 @@ abstract class BaseParsingHelper, T : RootNode override fun getLanguageVersion(): LanguageVersion = lversion } - val stages = selectProcessingStages(handler).sortedWith(Comparator { o1, o2 -> o1.compare(o2) }) + val stages = selectProcessingStages(handler).sortedWith { o1, o2 -> o1.compare(o2) } stages.forEach { it.processAST(rootNode, astAnalysisContext) @@ -164,6 +167,13 @@ abstract class BaseParsingHelper, T : RootNode open fun parseResource(resource: String, version: String? = null): T = parse(readResource(resource), version) + /** + * Fetches and [parse]s the [path]. + */ + @JvmOverloads + open fun parseFile(path: Path, version: String? = null): T = + parse(IOUtils.toString(Files.newBufferedReader(path)), version, filename = path.toAbsolutePath().toString()) + /** * Fetches the source of the given [clazz]. */ @@ -212,24 +222,21 @@ abstract class BaseParsingHelper, T : RootNode */ @JvmOverloads fun executeRule(rule: Rule, code: String, filename: String = "testfile.${language.extensions[0]}"): Report { - val rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule) - val configuration = PMDConfiguration() - configuration.setDefaultLanguageVersion(defaultVersion) - configuration.suppressMarker = params.parserOptions?.suppressMarker ?: PMD.SUPPRESS_MARKER + configuration.suppressMarker = this.params.suppressMarker val reportBuilder = Report.GlobalReportBuilderListener() val fullListener = GlobalAnalysisListener.tee(listOf(GlobalAnalysisListener.exceptionThrower(), reportBuilder)) + AbstractPMDProcessor.runSingleFile( - listOf(rules), - TextFile.forCharSeq(code, "testFile", getVersion(null)), + listOf(RuleSet.forSingleRule(rule)), + TextFile.forCharSeq(code, filename, defaultVersion), fullListener, configuration ) fullListener.close() - return reportBuilder.result } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt index 25ebe215a3..44ce65c6c2 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt @@ -33,6 +33,25 @@ open class RelevantAttributePrinter : BaseNodeAttributePrinter() { } +/** + * Only prints the begin/end coordinates. + */ +object CoordinatesPrinter : BaseNodeAttributePrinter() { + + private val Considered = setOf("BeginLine", "EndLine", "BeginColumn", "EndColumn") + + override fun fillAttributes(node: Node, result: MutableList) { + result += AttributeInfo("BeginLine", node.beginLine) + result += AttributeInfo("EndLine", node.endLine) + result += AttributeInfo("EndColumn", node.endColumn) + result += AttributeInfo("BeginColumn", node.beginColumn) + } + + override fun ignoreAttribute(node: Node, attribute: Attribute): Boolean = + attribute.name !in Considered + +} + /** * Base attribute printer, subclass to filter attributes. */ diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt index caaca5ca62..932fa04b1e 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt @@ -9,6 +9,7 @@ import io.kotest.matchers.equalityMatcher import io.kotest.matchers.should import net.sourceforge.pmd.Report import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.lang.ast.Node import kotlin.reflect.KCallable import kotlin.reflect.jvm.isAccessible import kotlin.test.assertEquals @@ -91,3 +92,11 @@ fun assertSuppressed(report: Report, size: Int): List { + + private Token token; + private ScalaTokenAdapter previousComment; + + ScalaTokenAdapter(Token token, ScalaTokenAdapter comment) { + this.token = token; + this.previousComment = comment; + } + + @Override + public ScalaTokenAdapter getNext() { + throw new UnsupportedOperationException(); + } + + @Override + public ScalaTokenAdapter getPreviousComment() { + return previousComment; + } + + @Override + public String getImage() { + return token.text(); + } + + @Override + public int getBeginLine() { + return token.pos().startLine() + 1; + } + + @Override + public int getEndLine() { + return token.pos().endLine() + 1; + } + + @Override + public int getBeginColumn() { + return token.pos().startColumn() + 1; + } + + @Override + public int getEndColumn() { + return token.pos().endColumn() + 2; + } + + @Override + public boolean isEof() { + return token instanceof Token.EOF; + } + + @Override + public String toString() { + return "ScalaTokenAdapter{" + + "token=" + token + + ", previousComment=" + previousComment + + "}"; + } +} diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index b619951613..e94ed84938 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -9,8 +9,10 @@ import java.util.Properties; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.scala.ScalaLanguageHandler; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -24,7 +26,7 @@ import scala.meta.tokenizers.TokenizeException; import scala.meta.tokens.Token; /** - * Scala Tokenizer class. Uses the Scala Meta Tokenizer. + * Scala Tokenizer class. Uses the Scala Meta Tokenizer, but adapts it for use with generic filtering */ public class ScalaTokenizer implements Tokenizer { @@ -72,19 +74,21 @@ public class ScalaTokenizer implements Tokenizer { // tokenize with a filter try { scala.meta.tokens.Tokens tokens = tokenizer.tokenize(); - ScalaTokenFilter filter = new ScalaTokenFilter(tokens.iterator()); - Token token; + // use extensions to the standard PMD TokenManager and Filter + ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator()); + ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); + + ScalaTokenAdapter token; while ((token = filter.getNextToken()) != null) { - if (StringUtils.isEmpty(token.text())) { + if (StringUtils.isEmpty(token.getImage())) { continue; } - Position pos = token.pos(); - TokenEntry cpdToken = new TokenEntry(token.text(), + TokenEntry cpdToken = new TokenEntry(token.getImage(), filename, - pos.startLine() + 1, - pos.startColumn() + 1, - pos.endColumn() + 2); + token.getBeginLine(), + token.getBeginColumn(), + token.getEndColumn()); tokenEntries.add(cpdToken); } } catch (Exception e) { @@ -103,19 +107,25 @@ public class ScalaTokenizer implements Tokenizer { } /** - * Token Filter skips un-helpful tokens to only register important tokens + * Implementation of the generic Token Manager, also skips un-helpful tokens and comments to only register important tokens * and patterns. + * + * Keeps track of comments, for special comment processing */ - private static class ScalaTokenFilter { + private static class ScalaTokenManager implements TokenManager { + Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, - Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class }; + Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; - ScalaTokenFilter(Iterator iterator) { + ScalaTokenAdapter previousComment = null; + + ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; } - Token getNextToken() { + @Override + public ScalaTokenAdapter getNextToken() { if (!tokenIter.hasNext()) { return null; } @@ -123,9 +133,12 @@ public class ScalaTokenizer implements Tokenizer { Token token; do { token = tokenIter.next(); + if (isComment(token)) { + previousComment = new ScalaTokenAdapter(token, previousComment); + } } while (token != null && skipToken(token) && tokenIter.hasNext()); - return token; + return new ScalaTokenAdapter(token, previousComment); } private boolean skipToken(Token token) { @@ -137,5 +150,22 @@ public class ScalaTokenizer implements Tokenizer { } return skip; } + + private boolean isComment(Token token) { + return token instanceof Token.Comment; + } } + + private static class ScalaTokenFilter extends BaseTokenFilter { + ScalaTokenFilter(TokenManager tokenManager) { + super(tokenManager); + } + + @Override + protected boolean shouldStopProcessing(ScalaTokenAdapter currentToken) { + return currentToken == null; + } + + } + } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java index f13a3c52b9..7f3d055f3b 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.scala; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.scala.ast.ScalaParser; import scala.meta.Dialect; @@ -38,7 +37,7 @@ public class ScalaLanguageHandler extends AbstractPmdLanguageVersionHandler { @Override - public ScalaParser getParser(ParserOptions parserOptions) { + public ScalaParser getParser() { return new ScalaParser(dialect); } } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java index 36865795c5..d09b6c84d1 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java @@ -36,6 +36,11 @@ public class ScalaTokenizerTest extends CpdTextComparisonTest { doTest("sample-LiftActor"); } + @Test + public void testSuppressionComments() { + doTest("special_comments"); + } + @Test public void tokenizeFailTest() { ex.expect(TokenMgrError.class); diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java index 111815dbca..7c4e8d4978 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java @@ -20,5 +20,4 @@ public final class ScalaParsingHelper extends BaseParsingHelper { child { - it.assertBounds(bline = 1, bcol = 1, eline = 3, ecol = 2) + it.assertPosition(bline = 1, bcol = 1, eline = 3, ecol = 2) it::isImplicit shouldBe false child { - it.assertBounds(bline = 1, bcol = 7, eline = 1, ecol = 10) + it.assertPosition(bline = 1, bcol = 7, eline = 1, ecol = 10) it::isImplicit shouldBe false } child { - it.assertBounds(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length + it.assertPosition(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length it::isImplicit shouldBe true child { - it.assertBounds(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length + it.assertPosition(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length it::isImplicit shouldBe true } } child { - it.assertBounds(bline = 1, bcol = 11, eline = 3, ecol = 2) + it.assertPosition(bline = 1, bcol = 11, eline = 3, ecol = 2) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length it::isImplicit shouldBe true child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length it::isImplicit shouldBe true } } child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 12) + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 12) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 6, eline = 2, ecol = 7) + it.assertPosition(bline = 2, bcol = 6, eline = 2, ecol = 7) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 6, eline = 2, ecol = 7) + it.assertPosition(bline = 2, bcol = 6, eline = 2, ecol = 7) it::isImplicit shouldBe false } } child { - it.assertBounds(bline = 2, bcol = 10, eline = 2, ecol = 12) + it.assertPosition(bline = 2, bcol = 10, eline = 2, ecol = 12) } } } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt index 734b8e68a6..09f9208c75 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt @@ -1,6 +1,4 @@ [Image] or [Truncated image[ Bcol Ecol -L1 - [/* Example source code copied from[ 1 5 L19 [package] 1 9 [net] 9 13 @@ -40,14 +38,10 @@ L26 [Unit] 19 24 L27 [}] 1 3 -L29 - [/**\n * The definition of a schedu[ 1 5 L32 [trait] 1 7 [LAScheduler] 7 19 [{] 19 21 -L33 - [/**\n * Execute some code on ano[ 3 7 L38 [def] 3 7 [execute] 7 15 @@ -79,8 +73,6 @@ L43 [onSameThread] 7 20 [=] 20 22 [false] 22 28 -L45 - [/**\n * Set this variable to the[ 3 7 L48 [@] 3 5 [volatile] 4 13 @@ -88,7 +80,6 @@ L48 [threadPoolSize] 17 32 [=] 32 34 [16] 34 37 - [// issue 194] 37 50 L50 [@] 3 5 [volatile] 4 13 @@ -98,8 +89,6 @@ L50 [threadPoolSize] 37 52 [*] 52 54 [25] 54 57 -L52 - [/**\n * If it's Full, then creat[ 3 7 L57 [@] 3 5 [volatile] 4 13 @@ -149,7 +138,6 @@ L64 [val] 15 19 [es] 19 22 [=] 22 24 - [// Executors.newFixedThreadPool(th[ 24 72 L65 [new] 9 13 [ThreadPoolExecutor] 13 32 @@ -282,8 +270,6 @@ L91 [ILAExecute] 13 24 [=] 24 26 [_] 26 28 -L93 - [/**\n * Execute some code on ano[ 3 7 L98 [def] 3 7 [execute] 7 15 @@ -464,8 +450,6 @@ L127 [MailboxItem] 15 27 [=] 27 29 [_] 29 31 -L129 - [/*\n def find(f: MailboxItem =>[ 5 8 L134 [def] 5 9 [remove] 9 16 @@ -588,8 +572,6 @@ L157 [\]] 71 73 [)] 72 74 [{] 74 76 -L158 - [// override def find(f: MailboxIte[ 5 79 L159 [next] 5 10 [=] 10 12 @@ -659,8 +641,6 @@ L167 [)] 42 44 L168 [}] 5 7 -L170 - [/**\n * Send a message to the Ac[ 3 7 L175 [def] 3 7 [send] 7 12 @@ -675,8 +655,6 @@ L175 [this] 28 33 [!] 33 35 [msg] 35 39 -L177 - [/**\n * Send a message to the Ac[ 3 7 L182 [def] 3 7 [!] 7 9 @@ -793,8 +771,6 @@ L199 [)] 10 12 L200 [}] 3 5 -L202 - [/**\n * This method inserts the [ 3 7 L207 [protected] 3 13 [def] 13 17 @@ -936,8 +912,6 @@ L230 [}] 5 7 L231 [}] 3 5 -L233 - [/**\n * A list of LoanWrappers t[ 3 7 L236 [protected] 3 13 [def] 13 17 @@ -949,8 +923,6 @@ L236 [\]] 52 54 [=] 54 56 [Nil] 56 60 -L238 - [/**\n * You can wrap calls aroun[ 3 7 L242 [protected] 3 13 [def] 13 17 @@ -1574,8 +1546,6 @@ L349 [}] 3 5 L350 [}] 1 3 -L352 - [/**\n * A SpecializedLiftActor des[ 1 5 L362 [class] 1 7 [MockSpecializedLiftActor] 7 32 @@ -1602,8 +1572,6 @@ L363 [\]] 45 47 [=] 47 49 [Nil] 49 53 -L365 - [/**\n * Send a message to the mo[ 3 7 L369 [override] 3 12 [def] 12 16 @@ -1630,10 +1598,6 @@ L372 [}] 5 7 L373 [}] 3 5 -L375 - [// We aren't required to implement[ 3 80 -L376 - [// since the message handler never[ 3 44 L377 [override] 3 12 [def] 12 16 @@ -1653,8 +1617,6 @@ L378 [=>] 12 15 L379 [}] 3 5 -L381 - [/**\n * Test to see if this acto[ 3 7 L384 [def] 3 7 [hasReceivedMessage_?] 7 28 @@ -1672,8 +1634,6 @@ L384 [(] 72 74 [msg] 73 77 [)] 76 78 -L386 - [/**\n * Returns the list of mess[ 3 7 L389 [def] 3 7 [messages] 7 16 @@ -1684,8 +1644,6 @@ L389 [\]] 23 25 [=] 25 27 [messagesReceived] 27 44 -L391 - [/**\n * Return the number of mes[ 3 7 L394 [def] 3 7 [messageCount] 7 20 @@ -1830,8 +1788,6 @@ L417 [msg] 24 28 L418 [}] 3 5 -L420 - [/**\n * Send a message to the Act[ 3 6 L425 [def] 3 7 [sendAndGetFuture] 7 24 @@ -1849,8 +1805,6 @@ L425 [this] 51 56 [!<] 56 59 [msg] 59 63 -L427 - [/**\n * Send a message to the Act[ 3 6 L431 [def] 3 7 [!<] 7 10 @@ -1888,8 +1842,6 @@ L434 [future] 5 12 L435 [}] 3 5 -L437 - [/**\n * Send a message to the Act[ 3 6 L442 [def] 3 7 [sendAndGetReply] 7 23 @@ -1904,8 +1856,6 @@ L442 [this] 40 45 [!?] 45 48 [msg] 48 52 -L444 - [/**\n * Send a message to the Act[ 3 6 L448 [def] 3 7 [!?] 7 10 @@ -1942,8 +1892,6 @@ L451 [get] 12 16 L452 [}] 3 5 -L455 - [/**\n * Send a message to the Act[ 3 6 L461 [def] 3 7 [sendAndGetReply] 7 23 @@ -1967,8 +1915,6 @@ L461 [,] 70 72 [msg] 72 76 [)] 75 77 -L463 - [/**\n * Send a message to the Act[ 3 6 L468 [def] 3 7 [!?] 7 10 @@ -1995,8 +1941,6 @@ L469 [,] 21 23 [timeout] 23 31 [)] 30 32 -L472 - [/**\n * Send a message to the A[ 5 8 L477 [def] 3 7 [!!] 7 10 @@ -2043,8 +1987,6 @@ L480 [)] 23 25 L481 [}] 3 5 -L483 - [/**\n * Send a message to the Act[ 3 6 L487 [def] 3 7 [!!] 7 10 @@ -2193,8 +2135,6 @@ L506 [)] 18 20 L507 [}] 3 5 -L509 - [/**\n * The Actor should call thi[ 3 6 L513 [protected] 3 13 [def] 13 17 @@ -2226,8 +2166,6 @@ L517 [}] 3 5 L518 [}] 1 3 -L520 - [/**\n * A MockLiftActor for use in[ 1 5 L529 [class] 1 7 [MockLiftActor] 7 21 @@ -2503,7 +2441,6 @@ L565 [(] 24 26 [true] 25 30 [)] 29 31 - [// access private and protected me[ 31 71 L566 [m] 9 11 [.] 10 12 @@ -2826,8 +2763,6 @@ L604 [}] 5 7 L605 [}] 1 3 -L607 - [/**\n * Java versions of Actors sh[ 1 5 L612 [class] 1 7 [LiftActorJ] 7 18 diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala new file mode 100644 index 0000000000..655a06a4a5 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala @@ -0,0 +1,24 @@ +// Testing CPD suppression + +// Irrelevant comment +// CPD-OFF +// CPD-ON + +case class Foo() { // special multiline comments + + /* CPD-OFF + * + * + * */ val hi = "Hello" /* This is a comment ending in CPD-ON */ + + private def bar(i: Int) : Int = { + val CPD = 40 + val OFF = 60 + CPD-OFF // This should tokenize + } + + /* CPD-OFF */ + def bar2(s: String): String = "bar2" + /* CPD-ON */ + +} diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt new file mode 100644 index 0000000000..1fcfdc6950 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt @@ -0,0 +1,40 @@ + [Image] or [Truncated image[ Bcol Ecol +L7 + [case] 1 6 + [class] 6 12 + [Foo] 12 16 + [(] 15 17 + [)] 16 18 + [{] 18 20 +L14 + [private] 3 11 + [def] 11 15 + [bar] 15 19 + [(] 18 20 + [i] 19 21 + [:] 20 22 + [Int] 22 26 + [)] 25 27 + [:] 27 29 + [Int] 29 33 + [=] 33 35 + [{] 35 37 +L15 + [val] 5 9 + [CPD] 9 13 + [=] 13 15 + [40] 15 18 +L16 + [val] 5 9 + [OFF] 9 13 + [=] 13 15 + [60] 15 18 +L17 + [CPD] 5 9 + [-] 8 10 + [OFF] 9 13 +L18 + [}] 3 5 +L24 + [}] 1 3 +EOF diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java index b5aec2c447..5f09696809 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java @@ -5,14 +5,13 @@ package net.sourceforge.pmd.lang.swift; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.swift.ast.PmdSwiftParser; public class SwiftHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(final ParserOptions parserOptions) { + public Parser getParser() { return new PmdSwiftParser(); } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java index 2c37fed1fb..41a01c7833 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java @@ -19,7 +19,6 @@ import net.sourceforge.pmd.ant.SourceLanguage; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.util.ResourceLoader; /** * Base test class for {@link LanguageVersion} implementations.
@@ -101,13 +100,15 @@ public class AbstractLanguageVersionTest { return; } - ResourceLoader rl = new ResourceLoader(); Properties props = new Properties(); - String rulesetsProperties = "category/" + simpleTerseName + "/categories.properties"; - try (InputStream inputStream = rl.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { + String rulesetsProperties = "/category/" + simpleTerseName + "/categories.properties"; + try (InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties)) { + if (inputStream == null) { + throw new IOException(); + } props.load(inputStream); } - assertRulesetsAndCategoriesProperties(rl, props); + assertRulesetsAndCategoriesProperties(props); } /** @@ -123,16 +124,15 @@ public class AbstractLanguageVersionTest { return; } - ResourceLoader rl = new ResourceLoader(); Properties props = new Properties(); - String rulesetsProperties = "rulesets/" + simpleTerseName + "/rulesets.properties"; - InputStream inputStream = rl.loadClassPathResourceAsStream(rulesetsProperties); + String rulesetsProperties = "/rulesets/" + simpleTerseName + "/rulesets.properties"; + InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties); if (inputStream != null) { // rulesets.properties file exists try (InputStream in = inputStream) { props.load(in); } - assertRulesetsAndCategoriesProperties(rl, props); + assertRulesetsAndCategoriesProperties(props); } } @@ -155,21 +155,19 @@ public class AbstractLanguageVersionTest { + " in the language versions of its language", 1, count); } - private void assertRulesetsAndCategoriesProperties(ResourceLoader rl, Properties props) - throws IOException, RuleSetNotFoundException { + private void assertRulesetsAndCategoriesProperties(Properties props) throws IOException { String rulesetFilenames = props.getProperty("rulesets.filenames"); assertNotNull(rulesetFilenames); + RuleSetLoader factory = new RuleSetLoader(); + if (rulesetFilenames.trim().isEmpty()) { return; } String[] rulesets = rulesetFilenames.split(","); for (String r : rulesets) { - try (InputStream stream = rl.loadClassPathResourceAsStream(r)) { - assertNotNull(stream); - } - RuleSet ruleset = new RuleSetLoader().loadFromResource(r); + RuleSet ruleset = factory.loadFromResource(r); assertNotNull(ruleset); } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index 234b15da1f..b262bbcf46 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -12,11 +12,13 @@ import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,7 +28,6 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Pattern; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -44,7 +45,6 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.util.ResourceLoader; /** * Base test class to verify the language's rulesets. This class should be @@ -54,13 +54,24 @@ public abstract class AbstractRuleSetFactoryTest { @org.junit.Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private static SAXParserFactory saxParserFactory; private static ValidateDefaultHandler validateDefaultHandler; private static SAXParser saxParser; protected Set validXPathClassNames = new HashSet<>(); + private final Set languagesToSkip = new HashSet<>(); public AbstractRuleSetFactoryTest() { + this(new String[0]); + } + + /** + * Constructor used when a module that depends on another module wants to filter out the dependee's rulesets. + * + * @param languagesToSkip {@link Language}s terse names that appear in the classpath via a dependency, but should be + * skipped because they aren't the primary language which the concrete instance of this class is testing. + */ + public AbstractRuleSetFactoryTest(String... languagesToSkip) { + this.languagesToSkip.addAll(Arrays.asList(languagesToSkip)); validXPathClassNames.add(XPathRule.class.getName()); } @@ -72,7 +83,7 @@ public abstract class AbstractRuleSetFactoryTest { */ @BeforeClass public static void init() throws Exception { - saxParserFactory = SAXParserFactory.newInstance(); + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); saxParserFactory.setValidating(true); saxParserFactory.setNamespaceAware(true); @@ -259,42 +270,47 @@ public abstract class AbstractRuleSetFactoryTest { } // Gets all test PMD Ruleset XML files - private List getRuleSetFileNames() throws IOException, RuleSetNotFoundException { + private List getRuleSetFileNames() throws IOException { List result = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { + if (this.languagesToSkip.contains(language.getTerseName())) { + continue; + } result.addAll(getRuleSetFileNames(language.getTerseName())); } return result; } - private List getRuleSetFileNames(String language) throws IOException, RuleSetNotFoundException { + private List getRuleSetFileNames(String language) throws IOException { List ruleSetFileNames = new ArrayList<>(); - try { - Properties properties = new Properties(); - try (InputStream is = new ResourceLoader().loadClassPathResourceAsStreamOrThrow("rulesets/" + language + "/rulesets.properties")) { - properties.load(is); - } - String fileNames = properties.getProperty("rulesets.filenames"); - StringTokenizer st = new StringTokenizer(fileNames, ","); - while (st.hasMoreTokens()) { - ruleSetFileNames.add(st.nextToken()); - } - } catch (RuleSetNotFoundException e) { + Properties properties = new Properties(); + @SuppressWarnings("PMD.CloseResource") + InputStream input = getClass().getResourceAsStream("rulesets/" + language + "/rulesets.properties"); + if (input == null) { // this might happen if a language is only support by CPD, but not // by PMD System.err.println("No ruleset found for language " + language); + return Collections.emptyList(); } + try (InputStream is = input) { + properties.load(is); + } + String fileNames = properties.getProperty("rulesets.filenames"); + StringTokenizer st = new StringTokenizer(fileNames, ","); + while (st.hasMoreTokens()) { + ruleSetFileNames.add(st.nextToken()); + } + return ruleSetFileNames; } - private RuleSet loadRuleSetByFileName(String ruleSetFileName) throws RuleSetNotFoundException { + private RuleSet loadRuleSetByFileName(String ruleSetFileName) { return new RuleSetLoader().loadFromResource(ruleSetFileName); } - private boolean validateAgainstSchema(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstSchema(String fileName) throws IOException, SAXException { try (InputStream inputStream = loadResourceAsStream(fileName)) { boolean valid = validateAgainstSchema(inputStream); if (!valid) { @@ -304,16 +320,14 @@ public abstract class AbstractRuleSetFactoryTest { } } - private boolean validateAgainstSchema(InputStream inputStream) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstSchema(InputStream inputStream) throws IOException, SAXException { saxParser.parse(inputStream, validateDefaultHandler.resetValid()); inputStream.close(); return validateDefaultHandler.isValid(); } - private boolean validateAgainstDtd(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstDtd(String fileName) throws IOException, SAXException { try (InputStream inputStream = loadResourceAsStream(fileName)) { boolean valid = validateAgainstDtd(inputStream); if (!valid) { @@ -323,8 +337,7 @@ public abstract class AbstractRuleSetFactoryTest { } } - private boolean validateAgainstDtd(InputStream inputStream) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstDtd(InputStream inputStream) throws IOException, SAXException { // Read file into memory String file = readFullyToString(inputStream); @@ -364,12 +377,11 @@ public abstract class AbstractRuleSetFactoryTest { } } - private static InputStream loadResourceAsStream(String resource) throws RuleSetNotFoundException { - return new ResourceLoader().loadClassPathResourceAsStreamOrThrow(resource); + private InputStream loadResourceAsStream(String resource) { + return getClass().getResourceAsStream(resource); } - private void testRuleSet(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private void testRuleSet(String fileName) throws IOException, SAXException { // Load original XML // String xml1 = @@ -388,7 +400,8 @@ public abstract class AbstractRuleSetFactoryTest { // System.out.println("xml2: " + xml2); // Read RuleSet from XML, first time - RuleSet ruleSet2 = new RuleSetLoader().loadFromResource(createRuleSetReferenceId(xml2)); + RuleSetLoader loader = new RuleSetLoader(); + RuleSet ruleSet2 = loader.loadFromString("", xml2); // Do write/read a 2nd time, just to be sure @@ -401,7 +414,7 @@ public abstract class AbstractRuleSetFactoryTest { // System.out.println("xml3: " + xml3); // Read RuleSet from XML, second time - RuleSet ruleSet3 = new RuleSetLoader().loadFromResource(createRuleSetReferenceId(xml3)); + RuleSet ruleSet3 = loader.loadFromString("", xml3); // The 2 written XMLs should all be valid w.r.t Schema/DTD assertTrue("1st roundtrip RuleSet XML is not valid against Schema (filename: " + fileName + ")", @@ -428,10 +441,10 @@ public abstract class AbstractRuleSetFactoryTest { private void assertEqualsRuleSet(String message, RuleSet ruleSet1, RuleSet ruleSet2) { assertEquals(message + ", RuleSet name", ruleSet1.getName(), ruleSet2.getName()); assertEquals(message + ", RuleSet description", ruleSet1.getDescription(), ruleSet2.getDescription()); - assertEquals(message + ", RuleSet exclude patterns", ruleSet1.getExcludePatterns(), - ruleSet2.getExcludePatterns()); - assertEquals(message + ", RuleSet include patterns", ruleSet1.getIncludePatterns(), - ruleSet2.getIncludePatterns()); + assertEquals(message + ", RuleSet exclude patterns", ruleSet1.getFileExclusions(), + ruleSet2.getFileExclusions()); + assertEquals(message + ", RuleSet include patterns", ruleSet1.getFileInclusions(), + ruleSet2.getFileInclusions()); assertEquals(message + ", RuleSet rule count", ruleSet1.getRules().size(), ruleSet2.getRules().size()); for (int i = 0; i < ruleSet1.getRules().size(); i++) { @@ -493,22 +506,6 @@ public abstract class AbstractRuleSetFactoryTest { } } - /** - * Create a {@link RuleSetReferenceId} by the given XML string. - * - * @param ruleSetXml - * the ruleset file content as string - * @return the {@link RuleSetReferenceId} - */ - protected static RuleSetReferenceId createRuleSetReferenceId(final String ruleSetXml) { - return new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleSetXml.getBytes(StandardCharsets.UTF_8)); - } - }; - } - /** * Validator for the SAX parser */ @@ -532,17 +529,17 @@ public abstract class AbstractRuleSetFactoryTest { } @Override - public void error(SAXParseException e) throws SAXException { + public void error(SAXParseException e) { log("Error", e); } @Override - public void fatalError(SAXParseException e) throws SAXException { + public void fatalError(SAXParseException e) { log("FatalError", e); } @Override - public void warning(SAXParseException e) throws SAXException { + public void warning(SAXParseException e) { log("Warning", e); } @@ -553,17 +550,15 @@ public abstract class AbstractRuleSetFactoryTest { } @Override - public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { + public InputSource resolveEntity(String publicId, String systemId) throws IOException { String resource = schemaMapping.get(systemId); if (resource != null) { - try { - InputStream inputStream = loadResourceAsStream(resource); - return new InputSource(inputStream); - } catch (RuleSetNotFoundException e) { - System.err.println(e.getMessage()); - throw new IOException(e.getMessage()); + InputStream inputStream = getClass().getResourceAsStream(resource); + if (inputStream == null) { + throw new FileNotFoundException(resource); } + return new InputSource(inputStream); } throw new IllegalArgumentException( "No clue how to handle: publicId=" + publicId + ", systemId=" + systemId); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java deleted file mode 100644 index 2027fa82b3..0000000000 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Unit tests for {@link ParserOptions}. - */ -public class ParserOptionsTest { - - /** - * Verify that the equals and hashCode methods work as expected. - */ - @Test - public void testEqualsHashcode() { - ParserOptions options1 = new ParserOptions(); - options1.setSuppressMarker("foo"); - ParserOptions options2 = new ParserOptions(); - options2.setSuppressMarker("bar"); - ParserOptions options3 = new ParserOptions(); - options3.setSuppressMarker("foo"); - ParserOptions options4 = new ParserOptions(); - options4.setSuppressMarker("bar"); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); - } - - /** - * Verify equals and hashCode for 4 {@link ParserOptions} instances. The - * given options should be as follows: 1 and 3 are equals, as are 2 and 4. - * - * @param options1 - * first option instance - equals third - * @param options2 - * second option instance - equals fourth - * @param options3 - * third option instance - equals first - * @param options4 - * fourth option instance - equals second - */ - public static void verifyOptionsEqualsHashcode(ParserOptions options1, ParserOptions options2, - ParserOptions options3, ParserOptions options4) { - // Objects should be different - Assert.assertNotSame(options1, options2); - Assert.assertNotSame(options1, options2); - Assert.assertNotSame(options1, options3); - Assert.assertNotSame(options2, options3); - Assert.assertNotSame(options2, options4); - Assert.assertNotSame(options3, options4); - - // Check all 16 equality combinations - Assert.assertEquals(options1, options1); - Assert.assertFalse(options1.equals(options2)); - Assert.assertEquals(options1, options3); - Assert.assertFalse(options1.equals(options4)); - - Assert.assertFalse(options2.equals(options1)); - Assert.assertEquals(options2, options2); - Assert.assertFalse(options2.equals(options3)); - Assert.assertEquals(options2, options4); - - Assert.assertEquals(options3, options1); - Assert.assertFalse(options3.equals(options2)); - Assert.assertEquals(options3, options3); - Assert.assertFalse(options3.equals(options4)); - - Assert.assertFalse(options4.equals(options1)); - Assert.assertEquals(options4, options2); - Assert.assertFalse(options4.equals(options3)); - Assert.assertEquals(options4, options4); - - // Hashcodes should match up - Assert.assertNotEquals(options1.hashCode(), options2.hashCode()); - Assert.assertEquals(options1.hashCode(), options3.hashCode()); - Assert.assertNotEquals(options1.hashCode(), options4.hashCode()); - Assert.assertNotEquals(options2.hashCode(), options3.hashCode()); - Assert.assertEquals(options2.hashCode(), options4.hashCode()); - Assert.assertNotEquals(options3.hashCode(), options4.hashCode()); - } -} diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java index 6b2805d956..599ff818aa 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; import net.sourceforge.pmd.lang.BaseLanguageModule; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.RootNode; @@ -46,7 +45,7 @@ public class DummyLanguageModule extends BaseLanguageModule { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { DummyRootNode node = new DummyRootNode(); node.setCoords(1, 1, 1, 2); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index de268cd6a1..4c4135f5f6 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -38,10 +38,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RuleSetLoadException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -101,15 +100,15 @@ public abstract class RuleTst { */ public Rule findRule(String ruleSet, String ruleName) { try { - List ruleSets = RulesetsFactoryUtils.defaultFactory().createRuleSets(ruleSet); - Rule rule = new RuleSets(ruleSets).getRuleByName(ruleName); + RuleSet parsedRset = new RuleSetLoader().loadFromResource(ruleSet); + Rule rule = parsedRset.getRuleByName(ruleName); if (rule == null) { fail("Rule " + ruleName + " not found in ruleset " + ruleSet); } else { rule.setRuleSetName(ruleSet); } return rule; - } catch (RuleSetNotFoundException e) { + } catch (RuleSetLoadException e) { e.printStackTrace(); fail("Couldn't find ruleset " + ruleSet); return null; @@ -287,7 +286,7 @@ public abstract class RuleTst { GlobalAnalysisListener listener = GlobalAnalysisListener.tee(listOf(GlobalAnalysisListener.exceptionThrower(), reportBuilder))) { AbstractPMDProcessor.runSingleFile( - listOf(RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule)), + listOf(RuleSet.forSingleRule(rule)), TextFile.forCharSeq(code, "testFile", languageVersion), listener, config diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 3fdbf66647..bb3ae9a749 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -11,6 +11,10 @@ ../ + + 8 + + @@ -78,6 +82,12 @@ pmd-core + + net.sourceforge.pmd + pmd-apex + ${project.version} + + junit junit diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java new file mode 100644 index 0000000000..d172999181 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java @@ -0,0 +1,131 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import apex.jorje.semantic.symbol.type.BasicType; + +/** + * Represents all data types that can be referenced from a Visualforce page. This enum consolidates the data types + * available to CustomFields and Apex. It uses the naming convention of CustomFields. + * + * See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_field_types.htm#meta_type_fieldtype + */ +public enum DataType { + AutoNumber(false), + Checkbox(false, BasicType.BOOLEAN), + Currency(false, BasicType.CURRENCY), + Date(false, BasicType.DATE), + DateTime(false, BasicType.DATE_TIME), + Email(false), + EncryptedText(true), + ExternalLookup(true), + File(false), + Hierarchy(false), + Html(false), + IndirectLookup(false), + Location(false), + LongTextArea(true), + Lookup(false, BasicType.ID), + MasterDetail(false), + MetadataRelationship(false), + MultiselectPicklist(true), + Note(true), + Number(false, BasicType.DECIMAL, BasicType.DOUBLE, BasicType.INTEGER, BasicType.LONG), + Percent(false), + Phone(false), + Picklist(true), + Summary(false), + Text(true, BasicType.STRING), + TextArea(true), + Time(false, BasicType.TIME), + Url(false), + /** + * Indicates that Metatada was found, but it's type was not mappable. This could because it is a type which isn't + * mapped, or it was an edge case where the type was ambiguously defined in the Metadata. + */ + Unknown(true); + + private static final Logger LOGGER = Logger.getLogger(DataType.class.getName()); + + + /** + * True if this field is an XSS risk + */ + public final boolean requiresEscaping; + + /** + * The set of {@link BasicType}s that map to this type. Multiple types can map to a single instance of this enum. + */ + private final Set basicTypes; + + /** + * A case insensitive map of the enum name to its instance. The case metadata is not guaranteed to have the correct + * case. + */ + private static final Map CASE_INSENSITIVE_MAP = new HashMap<>(); + + /** + * Map of BasicType to DataType. Multiple BasicTypes may map to one DataType. + */ + private static final Map BASIC_TYPE_MAP = new HashMap<>(); + + static { + for (DataType dataType : DataType.values()) { + CASE_INSENSITIVE_MAP.put(dataType.name().toLowerCase(Locale.ROOT), dataType); + for (BasicType basicType : dataType.basicTypes) { + BASIC_TYPE_MAP.put(basicType, dataType); + } + } + } + + /** + * Map to correct instance, returns {@code Unknown} if the value can't be mapped. + */ + public static DataType fromString(String value) { + value = value != null ? value : ""; + DataType dataType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); + + if (dataType == null) { + dataType = DataType.Unknown; + LOGGER.fine("Unable to determine DataType of " + value); + } + + return dataType; + } + + /** + * Map to correct instance, returns {@code Unknown} if the value can't be mapped. + */ + public static DataType fromBasicType(BasicType value) { + DataType dataType = value != null ? BASIC_TYPE_MAP.get(value) : null; + + if (dataType == null) { + dataType = DataType.Unknown; + LOGGER.fine("Unable to determine DataType of " + value); + } + + return dataType; + } + + DataType(boolean requiresEscaping) { + this(requiresEscaping, null); + } + + DataType(boolean requiresEscaping, BasicType...basicTypes) { + this.requiresEscaping = requiresEscaping; + this.basicTypes = new HashSet<>(); + if (basicTypes != null) { + this.basicTypes.addAll(Arrays.asList(basicTypes)); + } + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java index e49f653c09..6a3858f6b6 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java @@ -4,16 +4,55 @@ package net.sourceforge.pmd.lang.vf; +import java.io.File; +import java.util.Collections; +import java.util.List; + import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.vf.ast.VfParser; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.PropertySource; public class VfHandler extends AbstractPmdLanguageVersionHandler { + static final List DEFAULT_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); + static final List DEFAULT_OBJECT_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); + + /** + * Directory that contains Apex classes that may be referenced from a Visualforce page. + * + *

Env variable is {@code PMD_VF_APEXDIRECTORIES}. + */ + public static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("apexDirectories") + .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_APEX_DIRECTORIES) + .delim(',') + .build(); + + /** + * Directory that contains Object definitions that may be referenced from a Visualforce page. + * + *

Env variable is {@code PMD_VF_OBJECTSDIRECTORIES}. + */ + public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("objectsDirectories") + .desc("Location of Custom Object directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_OBJECT_DIRECTORIES) + .delim(',') + .build(); + @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new VfParser(); } + @Override + public void declareParserTaskProperties(PropertySource source) { + source.definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); + source.definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); + overridePropertiesFromEnv(VfLanguageModule.TERSE_NAME, source); + } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java index a6573ce4a6..5f4fb07a96 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java @@ -4,7 +4,18 @@ package net.sourceforge.pmd.lang.vf.ast; +import java.util.IdentityHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import net.sourceforge.pmd.lang.ast.Node; + public final class ASTExpression extends AbstractVfNode { + private static final Logger LOGGER = Logger.getLogger(ASTExpression.class.getName()); + ASTExpression(int id) { super(id); } @@ -13,4 +24,129 @@ public final class ASTExpression extends AbstractVfNode { protected R acceptVfVisitor(VfVisitor visitor, P data) { return visitor.visit(this, data); } + + private void logWarning(String warning, Node node) { + LOGGER.warning(warning + + ". nodeClass=" + node.getClass().getSimpleName() + // + ", fileName=" + AbstractTokenManager.getFileName() + + ", beginLine=" + node.getBeginLine() + + ", image=" + node.getImage()); + } + + /** + *

+ * An Expression can contain one or more strings that map to a piece of data. This method maps the string + * from the Visualforce page to terminal AST node that the string represents. The terminal node will be either an + * ASTIdentifier or ASTLiteral. It is the terminal node that is most important since it represents the type of data + * that will be displayed in the page. + *

+ *

+ * The string representation can be reconstructed by starting at the {@code Identifier} node and traversing its + * siblings until a node other than a {@code DotExpression} is encountered. Some more advanced situations aren't + * currently handled by this method. The method will throw an exception in such cases. + *

+ *
{@code
+     *  results in AST
+     * 
+     * The method would return key=ASTIdentifier(Image='MyValue'), value="MyValue"
+     * }
+ *
{@code
+     *  results in AST (It's important to notice that DotExpression is
+     * a sibling of Identifier.
+     * 
+     * 
+     *     
+     * 
+     * This method would return key=ASTIdentifier(Image='Text__c'), value="MyObject__c.Text__c"
+     * }
+ * + * THE FOLLOWING SITUATIONS ARE NOT HANDLED AND WILL THROW AN EXCEPTION. + * This syntax causes ambiguities with Apex Controller methods that return Maps versus accessing a CustomObject's + * field via array notation. This may be addressed in a future release. + * + *
{@code
+     *  results in AST
+     * 
+     * 
+     *     
+     * 
+
+     *  results in AST
+     * 
+     * 
+     *     
+     *         
+     *             
+     *         
+     *     
+     * 
+     * }
+ * + * @throws DataNodeStateException if the results of this method could have been incorrect. Callers should typically + * not rethrow this exception, as it will happen often and doesn't represent a terminal exception. + */ + public Map getDataNodes() throws DataNodeStateException { + Map result = new IdentityHashMap<>(); + + int numChildren = getNumChildren(); + List identifiers = findChildrenOfType(ASTIdentifier.class); + for (ASTIdentifier identifier : identifiers) { + LinkedList identifierNodes = new LinkedList<>(); + + // The Identifier is the first item that makes up the string + identifierNodes.add(identifier); + int index = identifier.getIndexInParent(); + + // Iterate through the rest of the children looking for ASTDotExpression nodes. + // The Image value of these nodes will be used to reconstruct the string. Any other node encountered will + // cause the while loop to break. The content of identifierNodes is used to construct the string and map + // it to the last element in identifierNodes. + index++; + while (index < numChildren) { + final Node node = getChild(index); + if (node instanceof ASTDotExpression) { + // The next part of the identifier will constructed from dot or array notation + if (node.getNumChildren() == 1) { + final Node expressionChild = node.getChild(0); + if (expressionChild instanceof ASTIdentifier || expressionChild instanceof ASTLiteral) { + identifierNodes.add((VfTypedNode) expressionChild); + } else { + // This should never happen + logWarning("Node expected to be Identifier or Literal", node); + throw new DataNodeStateException(); + } + } else { + // This should never happen + logWarning("More than one child found for ASTDotExpression", node); + throw new DataNodeStateException(); + } + } else if (node instanceof ASTExpression) { + // Not currently supported. This can occur in a couple of cases that may be supported in the future. + // 1. Custom Field using array notation. MyObject__c['Text__c'] + // 2. An Apex method that returns a map. ControllerMethod['KeyForMap'] + throw new DataNodeStateException(); + } else { + // Any other node type is not considered part of the identifier and breaks out of the loop + break; + } + index++; + } + + // Convert the list of nodes to a string representation, store the last node in the list as the map's key + String idString = String.join(".", identifierNodes.stream() + .map(i -> i.getImage()) + .collect(Collectors.toList())); + result.put(identifierNodes.getLast(), idString); + } + return result; + } + + + /** + * Thrown in cases where the the Identifiers in this node aren't ALL successfully parsed in a call to + * {@link #getDataNodes()} + */ + public static final class DataNodeStateException extends Exception { + } + } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java index da76df713f..496c68da43 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vf.ast; -public final class ASTIdentifier extends AbstractVfNode { +public final class ASTIdentifier extends AbstractVFDataNode { ASTIdentifier(int id) { super(id); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java index a472a282ed..c35099148c 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vf.ast; -public final class ASTLiteral extends AbstractVfNode { +public final class ASTLiteral extends AbstractVFDataNode { ASTLiteral(int id) { super(id); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java new file mode 100644 index 0000000000..94a7a4b951 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Represents a node that displays a piece of data. + */ +abstract class AbstractVFDataNode extends AbstractVfNode implements VfTypedNode { + + private DataType dataType; + + AbstractVFDataNode(int id) { + super(id); + } + + @Override + public DataType getDataType() { + return dataType; + } + + void setDataType(DataType dataType) { + this.dataType = dataType; + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java new file mode 100644 index 0000000000..760ddb31d3 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java @@ -0,0 +1,112 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.Logger; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; +import org.apache.commons.lang3.tuple.Pair; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +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.vf.DataType; + +import apex.jorje.semantic.symbol.type.BasicType; + +/** + * Responsible for storing a mapping of Apex Class properties that can be referenced from Visualforce to the type of the + * property. + */ +class ApexClassPropertyTypes extends SalesforceFieldTypes { + private static final Logger LOGGER = Logger.getLogger(ApexClassPropertyTypes.class.getName()); + private static final String APEX_CLASS_FILE_SUFFIX = ".cls"; + + /** + * Looks in {@code apexDirectories} for an Apex property identified by {@code expression}. + */ + @Override + public void findDataType(String expression, List apexDirectories) { + String[] parts = expression.split("\\."); + if (parts.length >= 2) { + // Load the class and parse it + String className = parts[0]; + + for (Path apexDirectory : apexDirectories) { + Path apexFilePath = apexDirectory.resolve(className + APEX_CLASS_FILE_SUFFIX); + if (Files.exists(apexFilePath) && Files.isRegularFile(apexFilePath)) { + Node node = parse(expression, apexFilePath); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + node.acceptVisitor(visitor, null); + + for (Pair variable : visitor.getVariables()) { + putDataType(variable.getKey(), DataType.fromBasicType(variable.getValue())); + } + + if (containsExpression(expression)) { + // Break out of the loop if a variable was found + break; + } + } + } + } + } + + private Node parse(String expression, Path apexFilePath) { + String fileText; + try (BufferedReader reader = Files.newBufferedReader(apexFilePath, StandardCharsets.UTF_8)) { + fileText = IOUtils.toString(reader); + } catch (IOException e) { + throw new ContextedRuntimeException(e) + .addContextValue("expression", expression) + .addContextValue("apexFilePath", apexFilePath); + } + + LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); + + ParserTask task = new ParserTask( + languageVersion, + apexFilePath.toString(), + fileText, + SemanticErrorReporter.noop() + ); + + return parser.parse(task); + } + + @Override + protected DataType putDataType(String name, DataType dataType) { + DataType previousType = super.putDataType(name, dataType); + if (previousType != null && !previousType.equals(dataType)) { + // It is possible to have a property and method with different types that appear the same to this code. An + // example is an Apex class with a property "public String Foo {get; set;}" and a method of + // "Integer getFoo() { return 1; }". In this case set the value as Unknown because we can't be sure which it + // is. This code could be more complex in an attempt to determine if all the types are safe from escaping, + // but we will allow a false positive in order to let the user know that the code could be refactored to be + // more clear. + super.putDataType(name, DataType.Unknown); + LOGGER.warning("Conflicting types for " + + name + + ". CurrentType=" + + dataType + + ", PreviousType=" + + previousType); + } + return previousType; + } + +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java new file mode 100644 index 0000000000..13eeba6c62 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java @@ -0,0 +1,90 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; + +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ApexVisitorBase; + +import apex.jorje.semantic.symbol.member.method.Generated; +import apex.jorje.semantic.symbol.member.method.MethodInfo; +import apex.jorje.semantic.symbol.type.BasicType; + +/** + * Visits an Apex class to determine a mapping of referenceable expressions to expression type. + */ +final class ApexClassPropertyTypesVisitor extends ApexVisitorBase { + + /** + * Prefix for standard bean type getters, i.e. getFoo + */ + private static final String BEAN_GETTER_PREFIX = "get"; + /** + * This is the prefix assigned to automatic get/set properties such as String myProp { get; set; } + */ + private static final String PROPERTY_PREFIX_ACCESSOR = "__sfdc_"; + + private static final String RETURN_TYPE_VOID = "void"; + + /** + * Pairs of (variableName, BasicType) + */ + private final List> variables; + + ApexClassPropertyTypesVisitor() { + this.variables = new ArrayList<>(); + } + + public List> getVariables() { + return this.variables; + } + + /** + * Stores the return type of the method in {@link #variables} if the method is referenceable from a + * Visualforce page. + */ + @Override + public Void visit(ASTMethod node, Void data) { + MethodInfo mi = node.getNode().getMethodInfo(); + if (mi.getParameterTypes().isEmpty() + && isVisibleToVisualForce(node) + && !RETURN_TYPE_VOID.equalsIgnoreCase(mi.getReturnType().getApexName()) + && (mi.getGenerated().equals(Generated.USER) || mi.isPropertyAccessor())) { + StringBuilder sb = new StringBuilder(); + List parents = node.getParentsOfType(ASTUserClass.class); + Collections.reverse(parents); + for (ASTUserClass parent : parents) { + sb.append(parent.getImage()).append("."); + } + String name = node.getImage(); + for (String prefix : new String[]{BEAN_GETTER_PREFIX, PROPERTY_PREFIX_ACCESSOR}) { + if (name.startsWith(prefix)) { + name = name.substring(prefix.length()); + } + } + sb.append(name); + + variables.add(Pair.of(sb.toString(), mi.getReturnType().getBasicType())); + } + return visitApexNode(node, data); + } + + /** + * Used to filter out methods that aren't visible to the Visualforce page. + * + * @return true if the method is visible to Visualforce. + */ + private boolean isVisibleToVisualForce(ASTMethod node) { + ASTModifierNode modifier = node.getFirstChildOfType(ASTModifierNode.class); + return modifier.isGlobal() | modifier.isPublic(); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes.java new file mode 100644 index 0000000000..ac156c2b6f --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes.java @@ -0,0 +1,255 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.lang3.exception.ContextedRuntimeException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. + */ +class ObjectFieldTypes extends SalesforceFieldTypes { + private static final Logger LOGGER = Logger.getLogger(ObjectFieldTypes.class.getName()); + + public static final String CUSTOM_OBJECT_SUFFIX = "__c"; + private static final String FIELDS_DIRECTORY = "fields"; + private static final String MDAPI_OBJECT_FILE_SUFFIX = ".object"; + private static final String SFDX_FIELD_FILE_SUFFIX = ".field-meta.xml"; + + private static final Map STANDARD_FIELD_TYPES; + + static { + STANDARD_FIELD_TYPES = new HashMap<>(); + STANDARD_FIELD_TYPES.put("createdbyid", DataType.Lookup); + STANDARD_FIELD_TYPES.put("createddate", DataType.DateTime); + STANDARD_FIELD_TYPES.put("id", DataType.Lookup); + STANDARD_FIELD_TYPES.put("isdeleted", DataType.Checkbox); + STANDARD_FIELD_TYPES.put("lastmodifiedbyid", DataType.Lookup); + STANDARD_FIELD_TYPES.put("lastmodifieddate", DataType.DateTime); + STANDARD_FIELD_TYPES.put("name", DataType.Text); + STANDARD_FIELD_TYPES.put("systemmodstamp", DataType.DateTime); + } + + /** + * Keep track of which ".object" files have already been processed. All fields are processed at once. If an object + * file has been processed + */ + private final Set objectFileProcessed; + + // XML Parsing objects + private final DocumentBuilder documentBuilder; + private final XPathExpression customObjectFieldsExpression; + private final XPathExpression customFieldFullNameExpression; + private final XPathExpression customFieldTypeExpression; + private final XPathExpression sfdxCustomFieldFullNameExpression; + private final XPathExpression sfdxCustomFieldTypeExpression; + + ObjectFieldTypes() { + this.objectFileProcessed = new HashSet<>(); + + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(false); + documentBuilderFactory.setValidating(false); + documentBuilderFactory.setIgnoringComments(true); + documentBuilderFactory.setIgnoringElementContentWhitespace(true); + documentBuilderFactory.setExpandEntityReferences(false); + documentBuilderFactory.setCoalescing(false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + + try { + XPath xPath = XPathFactory.newInstance().newXPath(); + this.customObjectFieldsExpression = xPath.compile("/CustomObject/fields"); + this.customFieldFullNameExpression = xPath.compile("fullName/text()"); + this.customFieldTypeExpression = xPath.compile("type/text()"); + this.sfdxCustomFieldFullNameExpression = xPath.compile("/CustomField/fullName/text()"); + this.sfdxCustomFieldTypeExpression = xPath.compile("/CustomField/type/text()"); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + /** + * Looks in {@code objectsDirectories} for a custom field identified by {@code expression}. + */ + @Override + protected void findDataType(String expression, List objectsDirectories) { + // The expression should be in the form . + String[] parts = expression.split("\\."); + if (parts.length == 1) { + throw new RuntimeException("Malformed identifier: " + expression); + } else if (parts.length == 2) { + String objectName = parts[0]; + String fieldName = parts[1]; + + addStandardFields(objectName); + + // Attempt to find a metadata file that contains the custom field. The information will be located in a + // file located at /.object or in an file located at + // //fields/.field-meta.xml. The list of object directories + // defaults to the [/../objects] but can be overridden by the user. + for (Path objectsDirectory : objectsDirectories) { + Path sfdxCustomFieldPath = getSfdxCustomFieldPath(objectsDirectory, objectName, fieldName); + if (sfdxCustomFieldPath != null) { + // SFDX Format + parseSfdxCustomField(objectName, sfdxCustomFieldPath); + } else { + // MDAPI Format + String fileName = objectName + MDAPI_OBJECT_FILE_SUFFIX; + Path mdapiPath = objectsDirectory.resolve(fileName); + if (Files.exists(mdapiPath) && Files.isRegularFile(mdapiPath)) { + parseMdapiCustomObject(mdapiPath); + } + } + + if (containsExpression(expression)) { + // Break out of the loop if a variable was found + break; + } + } + } else { + // TODO: Support cross object relationships, these are expressions that contain "__r" + LOGGER.fine("Expression does not have two parts: " + expression); + } + } + + /** + * Sfdx projects decompose custom fields into individual files. This method will return the individual file that + * corresponds to <objectName>.<fieldName> if it exists. + * + * @return path to the metadata file for the Custom Field or null if not found + */ + private Path getSfdxCustomFieldPath(Path objectsDirectory, String objectName, String fieldName) { + Path fieldsDirectoryPath = Paths.get(objectsDirectory.toString(), objectName, FIELDS_DIRECTORY); + if (Files.exists(fieldsDirectoryPath) && Files.isDirectory(fieldsDirectoryPath)) { + Path sfdxFieldPath = Paths.get(fieldsDirectoryPath.toString(), fieldName + SFDX_FIELD_FILE_SUFFIX); + if (Files.exists(sfdxFieldPath) && Files.isRegularFile(sfdxFieldPath)) { + return sfdxFieldPath; + } + } + return null; + } + + /** + * Determine the type of the custom field. + */ + private void parseSfdxCustomField(String customObjectName, Path sfdxCustomFieldPath) { + try { + Document document = documentBuilder.parse(sfdxCustomFieldPath.toFile()); + Node fullNameNode = (Node) sfdxCustomFieldFullNameExpression.evaluate(document, XPathConstants.NODE); + Node typeNode = (Node) sfdxCustomFieldTypeExpression.evaluate(document, XPathConstants.NODE); + String type = typeNode.getNodeValue(); + DataType dataType = DataType.fromString(type); + + String key = customObjectName + "." + fullNameNode.getNodeValue(); + putDataType(key, dataType); + } catch (IOException | SAXException | XPathExpressionException e) { + throw new ContextedRuntimeException(e) + .addContextValue("customObjectName", customObjectName) + .addContextValue("sfdxCustomFieldPath", sfdxCustomFieldPath); + } + } + + /** + * Parse the custom object path and determine the type of all of its custom fields. + */ + private void parseMdapiCustomObject(Path mdapiObjectFile) { + String fileName = mdapiObjectFile.getFileName().toString(); + + String customObjectName = fileName.substring(0, fileName.lastIndexOf(MDAPI_OBJECT_FILE_SUFFIX)); + if (!objectFileProcessed.contains(customObjectName)) { + try { + Document document = documentBuilder.parse(mdapiObjectFile.toFile()); + NodeList fieldsNodes = (NodeList) customObjectFieldsExpression.evaluate(document, XPathConstants.NODESET); + for (int i = 0; i < fieldsNodes.getLength(); i++) { + Node fieldsNode = fieldsNodes.item(i); + Node fullNameNode = (Node) customFieldFullNameExpression.evaluate(fieldsNode, XPathConstants.NODE); + if (fullNameNode == null) { + throw new RuntimeException("fullName evaluate failed for " + customObjectName + " " + fieldsNode.getTextContent()); + } + String name = fullNameNode.getNodeValue(); + if (endsWithIgnoreCase(name, CUSTOM_OBJECT_SUFFIX)) { + Node typeNode = (Node) customFieldTypeExpression.evaluate(fieldsNode, XPathConstants.NODE); + if (typeNode == null) { + throw new RuntimeException("type evaluate failed for object=" + customObjectName + ", field=" + name + " " + fieldsNode.getTextContent()); + } + String type = typeNode.getNodeValue(); + DataType dataType = DataType.fromString(type); + String key = customObjectName + "." + fullNameNode.getNodeValue(); + putDataType(key, dataType); + } + } + } catch (IOException | SAXException | XPathExpressionException e) { + throw new ContextedRuntimeException(e) + .addContextValue("customObjectName", customObjectName) + .addContextValue("mdapiObjectFile", mdapiObjectFile); + } + objectFileProcessed.add(customObjectName); + } + } + + /** + * Add the set of standard fields which aren't present in the metadata file, but may be refernced from the + * visualforce page. + */ + private void addStandardFields(String customObjectName) { + for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { + putDataType(customObjectName + "." + entry.getKey(), entry.getValue()); + } + } + + /** + * Null safe endsWithIgnoreCase + */ + private boolean endsWithIgnoreCase(String str, String suffix) { + return str != null && str.toLowerCase(Locale.ROOT).endsWith(suffix.toLowerCase(Locale.ROOT)); + } + + @Override + protected DataType putDataType(String name, DataType dataType) { + DataType previousType = super.putDataType(name, dataType); + if (previousType != null && !previousType.equals(dataType)) { + // It should not be possible to have conflicting types for CustomFields + throw new RuntimeException("Conflicting types for " + + name + + ". CurrentType=" + + dataType + + ", PreviousType=" + + previousType); + } + return previousType; + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java new file mode 100644 index 0000000000..3faeb5e563 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java @@ -0,0 +1,102 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. The + * fields are identified by in a case insensitive manner. + */ +abstract class SalesforceFieldTypes { + /** + * Cache of lowercase variable names to the variable type declared in the field's metadata file. + */ + private final Map variableNameToVariableType; + + /** + * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry + * which we haven't previously found. + */ + private final Set variableNameProcessed; + + SalesforceFieldTypes() { + this.variableNameToVariableType = new HashMap<>(); + this.variableNameProcessed = new HashSet<>(); + } + + /** + * + * @param expression expression literal as declared in the Visualforce page + * @param vfFileName file name of the Visualforce page that contains expression. Used to resolve relative paths + * included in {@code metadataDirectories} + * @param metadataDirectories absolute or relative list of directories that may contain the metadata corresponding + * to {@code expression} + * @return the DataType if it can be determined, else null + */ + public DataType getDataType(String expression, String vfFileName, List metadataDirectories) { + String lowerExpression = expression.toLowerCase(Locale.ROOT); + if (variableNameToVariableType.containsKey(lowerExpression)) { + // The expression has been previously retrieved + return variableNameToVariableType.get(lowerExpression); + } else if (variableNameProcessed.contains(lowerExpression)) { + // The expression has been previously requested, but was not found + return null; + } else { + // fixme getting a Path from the display name is just wrong. + Path vfFilePath = Paths.get(vfFileName).toAbsolutePath(); + List resolvedPaths = new ArrayList<>(); + for (String metadataDirectory : metadataDirectories) { + if (Paths.get(metadataDirectory).isAbsolute()) { + resolvedPaths.add(Paths.get(metadataDirectory)); + } else { + resolvedPaths.add(vfFilePath.getParent().resolve(metadataDirectory)); + } + } + + findDataType(expression, resolvedPaths); + variableNameProcessed.add(lowerExpression); + return variableNameToVariableType.get(lowerExpression); + } + } + + /** + * Stores {@link DataType} in a map using lower cased {@code expression} as the key. + * @param expression expression literal as declared in the Visualforce page + * @param dataType identifier determined for + * @return the previous value associated with {@code key}, or {@code null} if there was no mapping for {@code key}. + */ + protected DataType putDataType(String expression, DataType dataType) { + return variableNameToVariableType.put(expression.toLowerCase(Locale.ROOT), dataType); + } + + /** + * @return true if the expression has previously been stored via {@link #putDataType(String, DataType)} + */ + protected boolean containsExpression(String expression) { + return variableNameToVariableType.containsKey(expression.toLowerCase(Locale.ROOT)); + } + + /** + * Subclasses should attempt to find the {@code DataType} of {@code expression} within + * {@code metadataDirectories}. The subclass should store the value by invoking + * {@link #putDataType(String, DataType)}. + * + * @param expression expression as defined in the Visualforce page, case is preserved + * @param metadataDirectories list of directories that may contain the metadata corresponding to {@code expression} + */ + protected abstract void findDataType(String expression, List metadataDirectories); +} + diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java new file mode 100644 index 0000000000..e45aae08a2 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * This is internal API, and can be changed at any time. + */ +@InternalApi +public final class VfAstInternals { + + private VfAstInternals() { + // utility class + } + + public static void setDataType(VfTypedNode node, DataType dataType) { + ((AbstractVFDataNode) node).setDataType(dataType); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java new file mode 100644 index 0000000000..b7a097c305 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java @@ -0,0 +1,175 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; + +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.vf.DataType; +import net.sourceforge.pmd.lang.vf.VfHandler; + +/** + * Visits {@link ASTExpression} nodes and stores type information for + * {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} children that represent an IdentifierDotted construct. An + * IdentifierDotted is of the form {@code MyObject__c.MyField__c}. + */ +class VfExpressionTypeVisitor extends VfParserVisitorAdapter { + private static final Logger LOGGER = Logger.getLogger(VfExpressionTypeVisitor.class.getName()); + + private static final String APEX_PAGE = "apex:page"; + private static final String CONTROLLER_ATTRIBUTE = "controller"; + private static final String STANDARD_CONTROLLER_ATTRIBUTE = "standardcontroller"; + private static final String EXTENSIONS_ATTRIBUTE = "extensions"; + + private final ApexClassPropertyTypes apexClassPropertyTypes; + private final ObjectFieldTypes objectFieldTypes; + private final String fileName; + + private String standardControllerName; + + /** + * List of all Apex Class names that the VF page might refer to. These values come from either the + * {@code controller} or {@code extensions} attribute. + */ + private final List apexClassNames; + private final List apexDirectories; + private final List objectsDirectories; + + VfExpressionTypeVisitor(ParserTask task) { + this.fileName = task.getFileDisplayName(); + this.apexDirectories = task.getProperties().getProperty(VfHandler.APEX_DIRECTORIES_DESCRIPTOR); + this.objectsDirectories = task.getProperties().getProperty(VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR); + this.apexClassNames = new ArrayList<>(); + this.apexClassPropertyTypes = new ApexClassPropertyTypes(); + this.objectFieldTypes = new ObjectFieldTypes(); + } + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + if (StringUtils.isBlank(fileName)) { + // Skip visiting if there isn't a file that can anchor the directories + return data; + } + + if (apexDirectories.isEmpty() && objectsDirectories.isEmpty()) { + // Skip visiting if there aren't any directories to look in + return data; + } + return super.visit(node, data); + } + + /** + * Gather names of Controller, Extensions, and StandardController. Each of these may contain the identifier + * referenced from the Visualforce page. + */ + @Override + public Object visit(ASTElement node, Object data) { + if (APEX_PAGE.equalsIgnoreCase(node.getName())) { + List attribs = node.findChildrenOfType(ASTAttribute.class); + + for (ASTAttribute attr : attribs) { + String lowerAttr = attr.getName().toLowerCase(Locale.ROOT); + if (CONTROLLER_ATTRIBUTE.equals(lowerAttr)) { + // Controller Name should always take precedence + apexClassNames.add(0, attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage()); + break; + } else if (STANDARD_CONTROLLER_ATTRIBUTE.equals(lowerAttr)) { + standardControllerName = attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage().toLowerCase(Locale.ROOT); + } else if (EXTENSIONS_ATTRIBUTE.equalsIgnoreCase(lowerAttr)) { + for (String extension : attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage().split(",")) { + apexClassNames.add(extension.trim()); + } + } + } + } + return super.visit(node, data); + } + + /** + * Invoke {@link ASTExpression#getDataNodes()} on all children of {@code node} and attempt to determine the + * {@link DataType} by looking at Apex or CustomField metadata. + */ + @Override + public Object visit(ASTElExpression node, Object data) { + for (Map.Entry entry : getDataNodeNames(node).entrySet()) { + String name = entry.getValue(); + DataType type = null; + String[] parts = name.split("\\."); + + // Apex extensions take precedence over Standard controllers. + // The example below will display "Name From Inner Class" instead of the Account name + // public class AccountExtension { + // public AccountExtension(ApexPages.StandardController controller) { + // } + // + // public InnerClass getAccount() { + // return new InnerClass(); + // } + // + // public class InnerClass { + // public String getName() { + // return 'Name From Inner Class'; + // } + // } + // } + // + // + // + + // Try to find the identifier in an Apex class + for (String apexClassName : apexClassNames) { + String fullName = apexClassName + "." + name; + type = apexClassPropertyTypes.getDataType(fullName, fileName, apexDirectories); + if (type != null) { + break; + } + } + + // Try to find the identifier in a CustomField if it wasn't found in an Apex class and the identifier corresponds + // to the StandardController. + if (type == null) { + if (parts.length >= 2 && standardControllerName != null && standardControllerName.equalsIgnoreCase(parts[0])) { + type = objectFieldTypes.getDataType(name, fileName, objectsDirectories); + } + } + + if (type != null) { + VfAstInternals.setDataType(entry.getKey(), type); + } else { + LOGGER.fine("Unable to determine type for: " + name); + } + } + return super.visit(node, data); + } + + /** + * Invoke {@link ASTExpression#getDataNodes()} for all {@link ASTExpression} children of {@code node} and return + * the consolidated results. + */ + private IdentityHashMap getDataNodeNames(ASTElExpression node) { + IdentityHashMap dataNodeToName = new IdentityHashMap<>(); + + for (ASTExpression expression : node.findChildrenOfType(ASTExpression.class)) { + try { + dataNodeToName.putAll(expression.getDataNodes()); + } catch (ASTExpression.DataNodeStateException ignore) { + // Intentionally left blank + continue; + } + } + + return dataNodeToName; + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java index 4bd8dc9a6b..c20b738497 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java @@ -29,7 +29,13 @@ public final class VfParser extends JjtreeParserAdapter { @Override protected ASTCompilationUnit parseImpl(CharStream cs, ParserTask task) throws ParseException { - return new VfParserImpl(cs).CompilationUnit().makeTaskInfo(task); + ASTCompilationUnit root = new VfParserImpl(cs).CompilationUnit().makeTaskInfo(task); + + // Add type information to the AST + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(task); + visitor.visit(root, null); + + return root; } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java new file mode 100644 index 0000000000..8d0d74b6d1 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java @@ -0,0 +1,22 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Represents a node that displays a piece of data. + */ +public interface VfTypedNode extends VfNode { + + /** + * Returns the data type this node refers to. A null value indicates that no matching Metadata was found for this + * node. null differs from {@link DataType#Unknown} which indicates that Metadata was found but it wasn't mappable + * to one of the enums. + * + *

Example XPath 1.0 and 2.0: {@code //Identifier[@DataType='DateTime']} + */ + DataType getDataType(); +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index fe71477123..1a26fa49e3 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -12,6 +12,7 @@ import java.util.Set; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTContent; @@ -25,6 +26,7 @@ import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.VfNode; +import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; /** @@ -53,7 +55,6 @@ public class VfUnescapeElRule extends AbstractVfRule { @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); - return super.visit(node, data); } @@ -409,8 +410,11 @@ public class VfUnescapeElRule extends AbstractVfRule { continue; } - final List ids = expr.findChildrenOfType(ASTIdentifier.class); + if (expressionContainsSafeDataNodes(expr)) { + continue; + } + final List ids = expr.findChildrenOfType(ASTIdentifier.class); for (final ASTIdentifier id : ids) { boolean isEscaped = false; @@ -442,6 +446,24 @@ public class VfUnescapeElRule extends AbstractVfRule { return !nonEscapedIds.isEmpty(); } + /** + * Return true if the type of all data nodes can be determined and none of them require escaping + */ + private boolean expressionContainsSafeDataNodes(ASTExpression expression) { + try { + for (VfTypedNode node : expression.getDataNodes().keySet()) { + DataType dataType = node.getDataType(); + if (dataType == null || dataType.requiresEscaping) { + return false; + } + } + + return true; + } catch (ASTExpression.DataNodeStateException e) { + return false; + } + } + private boolean containsSafeFields(final VfNode expression) { final ASTExpression ex = expression.getFirstChildOfType(ASTExpression.class); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java new file mode 100644 index 0000000000..0accbd9625 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java @@ -0,0 +1,38 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class DataTypeTest { + @Test + public void testFromString() { + assertEquals(DataType.AutoNumber, DataType.fromString("AutoNumber")); + assertEquals(DataType.AutoNumber, DataType.fromString("autonumber")); + assertEquals(DataType.Unknown, DataType.fromString("")); + assertEquals(DataType.Unknown, DataType.fromString(null)); + } + + @Test + public void testFromBasicType() { + assertEquals(DataType.Checkbox, DataType.fromBasicType(BasicType.BOOLEAN)); + assertEquals(DataType.Number, DataType.fromBasicType(BasicType.DECIMAL)); + assertEquals(DataType.Number, DataType.fromBasicType(BasicType.DOUBLE)); + assertEquals(DataType.Unknown, DataType.fromBasicType(BasicType.APEX_OBJECT)); + assertEquals(DataType.Unknown, DataType.fromBasicType(null)); + } + + @Test + public void testRequiresEncoding() { + assertFalse(DataType.AutoNumber.requiresEscaping); + assertTrue(DataType.Text.requiresEscaping); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java index f478885c12..3ffadc6a4a 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java @@ -5,7 +5,10 @@ package net.sourceforge.pmd.lang.vf; import net.sourceforge.pmd.AbstractRuleSetFactoryTest; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { - // no additional tests + public RuleSetFactoryTest() { + super(ApexLanguageModule.TERSE_NAME); + } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java new file mode 100644 index 0000000000..ea6980dcb2 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public final class VFTestUtils { + private VFTestUtils() { + } + + /** + * Salesforce metadata is stored in two different formats, the newer sfdx form and the older mdapi format. Used to + * locate metadata on the file system during unit tests. + */ + public enum MetadataFormat { + SFDX("sfdx"), + MDAPI("mdapi"); + + public final String directoryName; + + MetadataFormat(String directoryName) { + this.directoryName = directoryName; + } + } + + /** + * Represents the metadata types that are referenced from unit tests. Used to locate metadata on the file system + * during unit tests. + */ + public enum MetadataType { + Apex("classes"), + Objects("objects"), + Vf("pages"); + + public final String directoryName; + + MetadataType(String directoryName) { + this.directoryName = directoryName; + } + } + + /** + * @return the path of the directory that matches the given parameters. The directory path is constructed using the + * following convention: + * src/test/resources/_decomposed_test_package_name_/_test_class_name_minus_Test_/metadata/_metadata_format_/_metadata_type_ + */ + public static Path getMetadataPath(Object testClazz, MetadataFormat metadataFormat, MetadataType metadataType) { + Path path = Paths.get("src", "test", "resources"); + // Decompose the test's package structure into directories + for (String directory : testClazz.getClass().getPackage().getName().split("\\.")) { + path = path.resolve(directory); + } + // Remove 'Test' from the class name + path = path.resolve(testClazz.getClass().getSimpleName().replaceFirst("Test$", "")); + // Append additional directories based on the MetadataFormat and MetadataType + path = path.resolve("metadata").resolve(metadataFormat.directoryName); + if (metadataType != null) { + path = path.resolve(metadataType.directoryName); + } + + return path.toAbsolutePath(); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java new file mode 100644 index 0000000000..803b1e83c5 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java @@ -0,0 +1,240 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; + +public class ASTExpressionTest { + + /** + * Slightly different scenarios which cause different AST, but should return the same results. + */ + private static final String[] SNIPPET_TEMPLATES = new String[] { + "{!%s}", + "", + ""}; + + @Test + public void testExpressionWithApexGetter() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyValue")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("MyValue")); + assertTrue(template, map.get("MyValue") instanceof ASTIdentifier); + } + } + + @Test + public void testExpressionWithStandardController() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c.Text__c")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("MyObject__c.Text__c")); + assertTrue(template, map.get("MyObject__c.Text__c") instanceof ASTIdentifier); + } + } + + @Test + public void testSelectOptions() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "userOptions.0")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("userOptions.0")); + assertTrue(template, map.get("userOptions.0") instanceof ASTLiteral); + } + } + + @Test + public void testMultipleIdentifiers() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c.Text__c + ' this is a string' + MyObject__c.Text2__c")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 2, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 2, map.size()); + assertTrue(template, map.containsKey("MyObject__c.Text__c")); + assertTrue(template, map.get("MyObject__c.Text__c") instanceof ASTIdentifier); + assertTrue(template, map.containsKey("MyObject__c.Text2__c")); + assertTrue(template, map.get("MyObject__c.Text2__c") instanceof ASTIdentifier); + } + } + + @Test + public void testIdentifierWithRelation() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c.MyObject2__r.Text__c")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 1, map.size()); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text__c") instanceof ASTIdentifier); + } + } + + @Test + public void testMultipleIdentifiersWithRelation() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c.MyObject2__r.Text__c + ' this is a string' + MyObject1__c.MyObject2__r.Text2__c")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 2, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 2, map.size()); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text__c") instanceof ASTIdentifier); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text2__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text2__c") instanceof ASTIdentifier); + } + } + + /** + * The current implementation does not support expressing statements using array notation. This notation introduces + * complexities that may be addressed in a future release. + */ + @Test + public void testExpressionWithArrayIndexingNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c['Name']")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + @Test + public void testIdentifierWithRelationIndexedAsArrayNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c['MyObject2__r'].Text__c")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + @Test + public void testIdentifierWithComplexIndexedArrayNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "theLineItems[item.Id].UnitPrice")); + + List nodes = getExpressions(compilationUnit); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + private static List getExpressions(ASTCompilationUnit compilationUnit) { + return compilationUnit.descendants(ASTExpression.class).toList(it -> it); + } + + /** + * Invert the map to make it easier to unit test. + */ + private Map invertMap(Map map) { + Map result = map.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + // Ensure no values have been lost + assertEquals(map.size(), result.size()); + return result; + } + + private ASTCompilationUnit compile(String snippet) { + return compile(snippet, false); + } + + private ASTCompilationUnit compile(String snippet, boolean renderAST) { + ASTCompilationUnit node = VfParsingHelper.DEFAULT.parse( + "" + + snippet + + "" + ); + + if (renderAST) { + try { + new XmlTreeRenderer().renderSubtree(node, System.out); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return node; + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java similarity index 84% rename from pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java rename to pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java index d905a3817a..ab922a2abf 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vf.ast; -public abstract class AbstractVfNodesTest { +public abstract class AbstractVfTest { protected final VfParsingHelper vf = VfParsingHelper.DEFAULT.withResourceContext(getClass()); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java new file mode 100644 index 0000000000..ca0204d928 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java @@ -0,0 +1,74 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.vf.DataType; +import net.sourceforge.pmd.lang.vf.VFTestUtils; +import net.sourceforge.pmd.lang.vf.VfHandler; + +public class ApexClassPropertyTypesTest { + private static final Map EXPECTED_DATA_TYPES; + + static { + // Intentionally use the wrong case for property names to ensure that they can be found. The Apex class name + // must have the correct case since it is used to lookup the file. The Apex class name is guaranteed to be correct + // in the Visualforce page, but the property names are not + EXPECTED_DATA_TYPES = new HashMap<>(); + EXPECTED_DATA_TYPES.put("ApexController.accOuntIdProp", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.AcCountId", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.AcCountname", DataType.Text); + + // InnerController + // The class should be parsed to Unknown. It's not a valid expression on its own. + EXPECTED_DATA_TYPES.put("ApexController.innErController", DataType.Unknown); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountIdProp", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountid", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountnAme", DataType.Text); + + // Edge cases + // Invalid class should return null + EXPECTED_DATA_TYPES.put("unknownclass.invalidProperty", null); + // Invalid class property should return null + EXPECTED_DATA_TYPES.put("ApexController.invalidProperty", null); + /* + * It is possible to have a property and method with different types that resolve to the same Visualforce + * expression. An example is an Apex class with a property "public String Foo {get; set;}" and a method of + * "Integer getFoo() { return 1; }". These properties should map to {@link DataType#Unknown}. + */ + EXPECTED_DATA_TYPES.put("ApexController.ConflictingProp", DataType.Unknown); + } + + @Test + public void testApexClassIsProperlyParsed() { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); + ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); + + ObjectFieldTypesTest.validateDataTypes(EXPECTED_DATA_TYPES, apexClassPropertyTypes, vfPagePath, + VfHandler.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); + } + + @Test + public void testInvalidDirectoryDoesNotCauseAnException() { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); + String vfFileName = vfPagePath.toString(); + + List paths = Arrays.asList(Paths.get("..", "classes-does-not-exist").toString()); + ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); + assertNull(apexClassPropertyTypes.getDataType("ApexController.accOuntIdProp", vfFileName, paths)); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java new file mode 100644 index 0000000000..280c7e1956 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java @@ -0,0 +1,63 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Test; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +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.vf.VFTestUtils; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class ApexClassPropertyTypesVisitorTest { + @Test + public void testApexClassIsProperlyParsed() throws IOException { + LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); + + Path apexPath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Apex) + .resolve("ApexController.cls").toAbsolutePath(); + ParserTask task = new ParserTask(languageVersion, + apexPath.toString(), + IOUtils.toString(Files.newBufferedReader(apexPath)), + SemanticErrorReporter.noop()); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + parser.parse(task).acceptVisitor(visitor, null); + + List> variables = visitor.getVariables(); + assertEquals(7, variables.size()); + Map variableNameToVariableType = new Hashtable<>(); + for (Pair variable : variables) { + // Map the values and ensure there were no duplicates + BasicType previous = variableNameToVariableType.put(variable.getKey(), variable.getValue()); + assertNull(variable.getKey(), previous); + } + + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.AccountIdProp")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.AccountId")); + assertEquals(BasicType.STRING, variableNameToVariableType.get("ApexController.AccountName")); + assertEquals(BasicType.APEX_OBJECT, variableNameToVariableType.get("ApexController.InnerController")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.InnerController.InnerAccountIdProp")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.InnerController.InnerAccountId")); + assertEquals(BasicType.STRING, variableNameToVariableType.get("ApexController.InnerController.InnerAccountName")); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java new file mode 100644 index 0000000000..26de0aac96 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java @@ -0,0 +1,131 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.vf.DataType; +import net.sourceforge.pmd.lang.vf.VFTestUtils; +import net.sourceforge.pmd.lang.vf.VfHandler; + +public class ObjectFieldTypesTest { + private static final Map EXPECTED_SFDX_DATA_TYPES; + private static final Map EXPECTED_MDAPI_DATA_TYPES; + + static { + EXPECTED_SFDX_DATA_TYPES = new HashMap<>(); + EXPECTED_SFDX_DATA_TYPES.put("Account.Checkbox__c", DataType.Checkbox); + EXPECTED_SFDX_DATA_TYPES.put("Account.DateTime__c", DataType.DateTime); + EXPECTED_SFDX_DATA_TYPES.put("Account.LongTextArea__c", DataType.LongTextArea); + EXPECTED_SFDX_DATA_TYPES.put("Account.Picklist__c", DataType.Picklist); + EXPECTED_SFDX_DATA_TYPES.put("Account.Text__c", DataType.Text); + EXPECTED_SFDX_DATA_TYPES.put("Account.TextArea__c", DataType.TextArea); + // Edge Cases + // Invalid property should return null + EXPECTED_SFDX_DATA_TYPES.put("Account.DoesNotExist__c", null); + + EXPECTED_MDAPI_DATA_TYPES = new HashMap<>(); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDCheckbox__c", DataType.Checkbox); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDDateTime__c", DataType.DateTime); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDLongTextArea__c", DataType.LongTextArea); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDPicklist__c", DataType.Picklist); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDText__c", DataType.Text); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDTextArea__c", DataType.TextArea); + // Edge Cases + // Invalid property should return null + EXPECTED_MDAPI_DATA_TYPES.put("Account.DoesNotExist__c", null); + } + + /** + * Verify that CustomFields stored in sfdx project format are correctly parsed + */ + @Test + public void testSfdxAccountIsProperlyParsed() { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); + + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + } + + /** + * Verify that CustomFields stored in mdapi format are correctly parsed + */ + @Test + public void testMdapiAccountIsProperlyParsed() { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); + + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + validateMDAPIAccount(objectFieldTypes, vfPagePath, VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + } + + /** + * Verify that fields are found across multiple directories + */ + @Test + public void testFieldsAreFoundInMultipleDirectories() { + ObjectFieldTypes objectFieldTypes; + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); + + List paths = Arrays.asList(VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), + VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Objects).toString()); + objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, paths); + validateMDAPIAccount(objectFieldTypes, vfPagePath, paths); + + Collections.reverse(paths); + objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, paths); + validateMDAPIAccount(objectFieldTypes, vfPagePath, paths); + } + + @Test + public void testInvalidDirectoryDoesNotCauseAnException() { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); + String vfFileName = vfPagePath.toString(); + + List paths = Arrays.asList(Paths.get("..", "objects-does-not-exist").toString()); + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + assertNull(objectFieldTypes.getDataType("Account.DoesNotExist__c", vfFileName, paths)); + } + + /** + * Validate the expected results when the Account Fields are stored in decomposed sfdx format + */ + private void validateSfdxAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { + validateDataTypes(EXPECTED_SFDX_DATA_TYPES, objectFieldTypes, vfPagePath, paths); + } + + /** + * Validate the expected results when the Account Fields are stored in a single file MDAPI format + */ + private void validateMDAPIAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { + validateDataTypes(EXPECTED_MDAPI_DATA_TYPES, objectFieldTypes, vfPagePath, paths); + } + + /** + * Verify that return values of {@link SalesforceFieldTypes#getDataType(String, String, List)} using the keys of + * {@code expectedDataTypes} matches the values of {@code expectedDataTypes} + */ + public static void validateDataTypes(Map expectedDataTypes, SalesforceFieldTypes fieldTypes, + Path vfPagePath, List paths) { + String vfFileName = vfPagePath.toString(); + + for (Map.Entry entry : expectedDataTypes.entrySet()) { + assertEquals(entry.getKey(), entry.getValue(), fieldTypes.getDataType(entry.getKey(), vfFileName, paths)); + } + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java index 4fe6959f8f..7b0677d1d7 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java @@ -24,7 +24,7 @@ import org.junit.Test; * @author sergey.gorbaty - VF adaptation * */ -public class VfDocStyleTest extends AbstractVfNodesTest { +public class VfDocStyleTest extends AbstractVfTest { /** * Smoke test for VF parser. diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java new file mode 100644 index 0000000000..7a010cbd62 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java @@ -0,0 +1,185 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.vf.DataType; +import net.sourceforge.pmd.lang.vf.VFTestUtils; +import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; + +public class VfExpressionTypeVisitorTest { + private static final Map EXPECTED_CUSTOM_FIELD_DATA_TYPES; + private static final Map EXPECTED_APEX_DATA_TYPES; + + static { + EXPECTED_CUSTOM_FIELD_DATA_TYPES = new HashMap<>(); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("CreatedDate", DataType.DateTime); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("DateTime__c", DataType.DateTime); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Checkbox__c", DataType.Checkbox); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Name", DataType.Text); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Text__c", DataType.Text); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("TextArea__c", DataType.TextArea); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("LongTextArea__c", DataType.LongTextArea); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Picklist__c", DataType.Picklist); + + EXPECTED_APEX_DATA_TYPES = new HashMap<>(); + EXPECTED_APEX_DATA_TYPES.put("AccountIdProp", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("AccountId", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountId", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountIdProp", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("AccountName", DataType.Text); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountName", DataType.Text); + EXPECTED_APEX_DATA_TYPES.put("ConflictingProp", DataType.Unknown); + } + + /** + * Strings that use dot notation(Account.CreatedDate) result in ASTIdentifier nodes + */ + @Test + public void testXpathQueryForCustomFieldIdentifiers() { + Node rootNode = compile("StandardAccount.page"); + + for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { + List nodes = getIdentifiers(rootNode, entry); + + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(entry.getKey(), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTIdentifier); + ASTIdentifier identifier = (ASTIdentifier) node; + assertEquals(entry.getKey(), entry.getValue(), identifier.getDataType()); + } + } + } + + /** + * Strings that use array notation, Account['CreatedDate') don't have a DataType added. This type of notation + * creates ambiguous situations with Apex methods that return Maps. This may be addressed in a future release. + */ + @Test + public void testXpathQueryForCustomFieldLiteralsHaveNullDataType() { + Node rootNode = compile("StandardAccount.page"); + + for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { + List nodes = rootNode.descendants(ASTLiteral.class) + // Literals are surrounded by apostrophes + .filterMatching(ASTLiteral::getImage, "'" + entry.getKey() + "'") + .filterMatching(ASTLiteral::getDataType, null) + .toList(); + + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(String.format("'%s'", entry.getKey()), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTLiteral); + ASTLiteral literal = (ASTLiteral) node; + assertEquals(entry.getKey(), null, literal.getDataType()); + } + } + } + + /** + * Nodes where the DataType can't be determined should have a null DataType + */ + @Test + public void testDataTypeForCustomFieldsNotFound() { + Node rootNode = compile("StandardAccount.page"); + + checkNodes(rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, "NotFoundField__c")); + checkNodes(rootNode.descendants(ASTLiteral.class) + .filterMatching(ASTLiteral::getImage, "'NotFoundField__c'")); + } + + private void checkNodes(NodeStream nodeStream) { + // Each string appears twice, it is set on a "value" attribute and inline + List nodes = nodeStream.toList(); + assertEquals(2, nodes.size()); + for (VfTypedNode node : nodes) { + assertNull(node.getDataType()); + } + } + + /** + * Apex properties result in ASTIdentifier nodes + */ + @Test + public void testXpathQueryForProperties() { + Node rootNode = compile("ApexController.page"); + + for (Map.Entry entry : EXPECTED_APEX_DATA_TYPES.entrySet()) { + List nodes = getIdentifiers(rootNode, entry); + + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(entry.getKey(), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTIdentifier); + ASTIdentifier identifier = (ASTIdentifier) node; + assertEquals(entry.getKey(), entry.getValue(), identifier.getDataType()); + } + } + } + + private List getIdentifiers(Node rootNode, Entry entry) { + return rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, entry.getKey()) + .filterMatching(ASTIdentifier::getDataType, entry.getValue()) + .toList(); + } + + /** + * Nodes where the DataType can't be determined should have a null DataType + */ + @Test + public void testDataTypeForApexPropertiesNotFound() { + Node rootNode = compile("ApexController.page"); + + // Each string appears twice, it is set on a "value" attribute and inline + checkNodes(rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, "NotFoundProp")); + } + + private Node compile(String pageName) { + return compile(pageName, false); + } + + private Node compile(String pageName, boolean renderAST) { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve(pageName); + return compile(vfPagePath, renderAST); + } + + private Node compile(Path vfPagePath, boolean renderAST) { + Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath); + assertNotNull(node); + + if (renderAST) { + try { + new XmlTreeRenderer().renderSubtree(node, System.out); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return node; + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java index 77287f6e42..e5578adc46 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java @@ -10,7 +10,7 @@ import java.util.List; import org.junit.Test; -public class VfPageStyleTest extends AbstractVfNodesTest { +public class VfPageStyleTest extends AbstractVfTest { /** * Test parsing of a EL expression. diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java index 6ce22de09c..76a609c289 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java @@ -11,7 +11,7 @@ import net.sourceforge.pmd.lang.ast.ParseException; /** * @author sergey.gorbaty */ -public class VfParserTest extends AbstractVfNodesTest { +public class VfParserTest extends AbstractVfTest { @Test public void testSingleDoubleQuoteAndEL() { diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java index 40212b810e..3cd0019f9b 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java @@ -4,8 +4,142 @@ package net.sourceforge.pmd.lang.vf.rule.security; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.VFTestUtils; +import net.sourceforge.pmd.lang.vf.ast.VfParsingHelper; import net.sourceforge.pmd.testframework.PmdRuleTst; +import net.sourceforge.pmd.util.datasource.FileDataSource; public class VfUnescapeElTest extends PmdRuleTst { - // no additional unit tests + public static final String EXPECTED_RULE_MESSAGE = "Avoid unescaped user controlled content in EL"; + + /** + * Verify that CustomFields stored in sfdx project format are correctly parsed + */ + @Test + public void testSfdxCustomFields() throws Exception { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("StandardAccount.page"); + + Report report = runRule(vfPagePath); + List ruleViolations = report.getViolations(); + assertEquals("Number of violations", 20, ruleViolations.size()); + + int firstLineWithErrors = 14; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + int expectedLineNumber = firstLineWithErrors + i; + if ((ruleViolations.size() + firstLineWithErrors - 1) == expectedLineNumber) { + // The last line has two errors on the same page + expectedLineNumber = expectedLineNumber - 1; + } + assertEquals("Line Number", expectedLineNumber, ruleViolation.getBeginLine()); + } + } + + /** + * Verify that CustomFields stored in mdapi format are correctly parsed + */ + @Test + public void testMdapiCustomFields() throws Exception { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("StandardAccount.page"); + + Report report = runRule(vfPagePath); + List ruleViolations = report.getViolations(); + assertEquals("Number of violations", 6, ruleViolations.size()); + int firstLineWithErrors = 8; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals("Line Number", firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Tests a page with a single Apex controller + */ + @Test + public void testApexController() throws Exception { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("ApexController.page"); + + Report report = runRule(vfPagePath); + List ruleViolations = report.getViolations(); + assertEquals("Number of violations", 2, ruleViolations.size()); + int firstLineWithErrors = 9; + for (int i = 0; i < ruleViolations.size(); i++) { + // There should start at line 9 + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals("Line Number", firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Tests a page with a standard controller and two Apex extensions + */ + @Test + public void testExtensions() throws Exception { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve(Paths.get("StandardAccountWithExtensions.page")); + + Report report = runRule(vfPagePath); + List ruleViolations = report.getViolations(); + assertEquals(8, ruleViolations.size()); + int firstLineWithErrors = 9; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Runs a rule against a Visualforce page on the file system. + */ + private Report runRule(Path vfPagePath) throws Exception { + Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath); + assertNotNull(node); + + PMDConfiguration config = new PMDConfiguration(); + config.setIgnoreIncrementalAnalysis(true); + // simple class loader, that doesn't delegate to parent. + // this allows us in the tests to simulate PMD run without + // auxclasspath, not even the classes from the test dependencies + // will be found. + config.setClassLoader(new ClassLoader() { + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("java.") || name.startsWith("javax.")) { + return super.loadClass(name, resolve); + } + throw new ClassNotFoundException(name); + } + }); + Rule rule = findRule("category/vf/security.xml", "VfUnescapeEl"); + + return PMD.processFiles( + config, + listOf(RuleSet.forSingleRule(rule)), + listOf(new FileDataSource(vfPagePath.toAbsolutePath().toFile())), + Collections.emptyList() + ); + } } diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..76fdac2c62 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,44 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + public String ConflictingProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public Integer getConflictingProp() { + return ''; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page new file mode 100644 index 0000000000..c0090110b0 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page @@ -0,0 +1,3 @@ + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..df449e3e6f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,39 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/objects/Account.object new file mode 100644 index 0000000000..5c37ef3c93 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/objects/Account.object @@ -0,0 +1,445 @@ + + + + CallHighlightAction + Default + + + CallHighlightAction + Large + Default + + + CallHighlightAction + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + EmailHighlightAction + Default + + + EmailHighlightAction + Large + Default + + + EmailHighlightAction + Small + Default + + + EnableCustomerPortalUser + Default + + + EnableCustomerPortalUser + Large + Default + + + EnableCustomerPortalUser + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + ListClean + Default + + + ListClean + Large + Default + + + ListClean + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + RequestUpdate + Default + + + RequestUpdate + Large + Default + + + RequestUpdate + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + SmsHighlightAction + Default + + + SmsHighlightAction + Large + Default + + + SmsHighlightAction + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + + ViewCustomerPortalUser + Default + + + ViewCustomerPortalUser + Large + Default + + + ViewCustomerPortalUser + Small + Default + + + WebsiteHighlightAction + Default + + + WebsiteHighlightAction + Large + Default + + + WebsiteHighlightAction + Small + Default + + SYSTEM + true + false + Private + + ACCOUNT.NAME + ACCOUNT.ADDRESS1_CITY + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + ACCOUNT.PHONE1 + CORE.USERS.ALIAS + + ReadWrite + + AccountNumber + false + + + AccountSource + false + Picklist + + + AnnualRevenue + false + + + BillingAddress + false + + + MDCheckbox__c + false + false + + false + Checkbox + + + CleanStatus + false + + + DandbCompanyId + false + Lookup + + + MDDateTime__c + false + + false + false + DateTime + + + Description + false + + + DunsNumber + false + + + Fax + false + + + Industry + false + Picklist + + + Jigsaw + false + + + NaicsCode + false + + + NaicsDesc + false + + + Name + true + + + NumberOfEmployees + false + + + OperatingHoursId + false + Lookup + + + OwnerId + true + Lookup + + + Ownership + false + Picklist + + + ParentId + false + Hierarchy + + + Phone + false + + + MDPicklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + + + Rating + false + Picklist + + + ShippingAddress + false + + + Sic + false + + + SicDesc + false + + + Site + false + + + MDLongTextArea__c + false + + 32768 + false + LongTextArea + 3 + + + MDTextArea__c + false + + false + false + TextArea + + + MDText__c + false + + 255 + false + false + Text + false + + + TickerSymbol + false + + + Tradestyle + false + + + Type + false + Picklist + + + Website + false + + + YearStarted + false + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page new file mode 100644 index 0000000000..c0090110b0 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page @@ -0,0 +1,3 @@ + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page new file mode 100644 index 0000000000..c0090110b0 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page @@ -0,0 +1,3 @@ + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..76fdac2c62 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,44 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + public String ConflictingProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public Integer getConflictingProp() { + return ''; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page new file mode 100644 index 0000000000..b876d5f46b --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page @@ -0,0 +1,18 @@ + + + {!AccountIdProp} + + {!AccountId} + + {!InnerController.InnerAccountId} + + {!InnerController.InnerAccountIdProp} + + {!AccountName} + + {!InnerController.InnerAccountName} + + {!ConflictingProp} + + {!NotFoundProp} + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page new file mode 100644 index 0000000000..67851687c1 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page @@ -0,0 +1,41 @@ + + + + {!Account.CreatedDate} + + {!Account.Checkbox__c} + + {!Account.DateTime__c} + + {!Account.Name} + + {!Account.Text__c} + + {!Account.TextArea__c} + + {!Account.LongTextArea__c} + + {!Account.Picklist__c} + + {!Account.NotFoundField__c} + + + + {!Account['CreatedDate']} + + {!Account['Checkbox__c']} + + {!Account['DateTime__c']} + + {!Account['Name']} + + {!Account['Text__c']} + + {!Account['TextArea__c']} + + {!Account['LongTextArea__c']} + + {!Account['Picklist__c']} + + {!Account['NotFoundField__c']} + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object new file mode 100644 index 0000000000..5c37ef3c93 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object @@ -0,0 +1,445 @@ + + + + CallHighlightAction + Default + + + CallHighlightAction + Large + Default + + + CallHighlightAction + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + EmailHighlightAction + Default + + + EmailHighlightAction + Large + Default + + + EmailHighlightAction + Small + Default + + + EnableCustomerPortalUser + Default + + + EnableCustomerPortalUser + Large + Default + + + EnableCustomerPortalUser + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + ListClean + Default + + + ListClean + Large + Default + + + ListClean + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + RequestUpdate + Default + + + RequestUpdate + Large + Default + + + RequestUpdate + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + SmsHighlightAction + Default + + + SmsHighlightAction + Large + Default + + + SmsHighlightAction + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + + ViewCustomerPortalUser + Default + + + ViewCustomerPortalUser + Large + Default + + + ViewCustomerPortalUser + Small + Default + + + WebsiteHighlightAction + Default + + + WebsiteHighlightAction + Large + Default + + + WebsiteHighlightAction + Small + Default + + SYSTEM + true + false + Private + + ACCOUNT.NAME + ACCOUNT.ADDRESS1_CITY + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + ACCOUNT.PHONE1 + CORE.USERS.ALIAS + + ReadWrite + + AccountNumber + false + + + AccountSource + false + Picklist + + + AnnualRevenue + false + + + BillingAddress + false + + + MDCheckbox__c + false + false + + false + Checkbox + + + CleanStatus + false + + + DandbCompanyId + false + Lookup + + + MDDateTime__c + false + + false + false + DateTime + + + Description + false + + + DunsNumber + false + + + Fax + false + + + Industry + false + Picklist + + + Jigsaw + false + + + NaicsCode + false + + + NaicsDesc + false + + + Name + true + + + NumberOfEmployees + false + + + OperatingHoursId + false + Lookup + + + OwnerId + true + Lookup + + + Ownership + false + Picklist + + + ParentId + false + Hierarchy + + + Phone + false + + + MDPicklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + + + Rating + false + Picklist + + + ShippingAddress + false + + + Sic + false + + + SicDesc + false + + + Site + false + + + MDLongTextArea__c + false + + 32768 + false + LongTextArea + 3 + + + MDTextArea__c + false + + false + false + TextArea + + + MDText__c + false + + 255 + false + false + Text + false + + + TickerSymbol + false + + + Tradestyle + false + + + Type + false + Picklist + + + Website + false + + + YearStarted + false + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page new file mode 100644 index 0000000000..8413160a63 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..df449e3e6f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,39 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension1.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension1.cls new file mode 100644 index 0000000000..764474cb46 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension1.cls @@ -0,0 +1,4 @@ +public class ApexExtension1 { + public String StringFromExtension1 {get; set;} + public Id IdFromExtension1 {get; set;} +} diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension2.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension2.cls new file mode 100644 index 0000000000..6f6ff84d65 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension2.cls @@ -0,0 +1,4 @@ +public class ApexExtension2 { + public String StringFromExtension2 {get; set;} + public Id IdFromExtension2 {get; set;} +} diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/ApexController.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/ApexController.page new file mode 100644 index 0000000000..2939c23f16 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/ApexController.page @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page new file mode 100644 index 0000000000..c95635785f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page @@ -0,0 +1,33 @@ + + + + {!Account.CreatedDate} + + {!Account.Checkbox__c} + + + + {!Account.DateTime__c} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page new file mode 100644 index 0000000000..0fcc494275 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java index b2bafd61c0..cac346da69 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.vm; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.vm.ast.VmParser; @@ -17,7 +16,7 @@ public class VmHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(final ParserOptions parserOptions) { + public Parser getParser() { return new VmParser(); } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java deleted file mode 100644 index e348d84583..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.wsdl.rule; - -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.wsdl.WsdlLanguageModule; -import net.sourceforge.pmd.lang.xml.rule.AbstractXmlRule; - -/** - * Created by bernardo-macedo on 24.06.15. - */ -public class AbstractWsdlRule extends AbstractXmlRule { - - public AbstractWsdlRule() { - super(LanguageRegistry.getLanguage(WsdlLanguageModule.NAME)); - } - -} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java index fb0154fd3b..3e0a138800 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.xml; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; /** @@ -14,13 +13,8 @@ import net.sourceforge.pmd.lang.ast.Parser; public class XmlHandler extends AbstractPmdLanguageVersionHandler { @Override - public ParserOptions getDefaultParserOptions() { - return new XmlParserOptions(); - } - - @Override - public Parser getParser(ParserOptions parserOptions) { - return new XmlParser((XmlParserOptions) parserOptions); + public Parser getParser() { + return new XmlParser(); } } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java index 9ba03ea0d6..58b91d35c2 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java @@ -13,15 +13,10 @@ import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl.RootXmlNode; * Adapter for the XmlParser. */ public class XmlParser implements Parser { - private final XmlParserOptions parserOptions; - - public XmlParser(XmlParserOptions parserOptions) { - this.parserOptions = parserOptions; - } @Override public RootXmlNode parse(ParserTask task) throws ParseException { - return new XmlParserImpl(parserOptions).parse(task); + return new XmlParserImpl().parse(task); } } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java deleted file mode 100644 index b752d32b90..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import java.io.ByteArrayInputStream; -import java.util.Objects; - -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; - -/** - * @deprecated Parser options will be removed with 7.0, these options - * will assume their default values then. - */ -@Deprecated -public class XmlParserOptions extends ParserOptions { - - // Note: The UI order values are chosen to be larger than those built into - // XPathRule. - public static final PropertyDescriptor COALESCING_DESCRIPTOR = - PropertyFactory.booleanProperty("coalescing") - .desc("deprecated!Specifies that the XML parser convert CDATA nodes to Text nodes and append it to the adjacent (if any) text node.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor EXPAND_ENTITY_REFERENCES_DESCRIPTOR = - PropertyFactory.booleanProperty("expandEntityReferences") - .desc("deprecated!Specifies that the XML parser expand entity reference nodes.") - .defaultValue(true) - .build(); - public static final PropertyDescriptor IGNORING_COMMENTS_DESCRIPTOR = - PropertyFactory.booleanProperty("ignoringComments") - .desc("deprecated!Specifies that the XML parser ignore comments.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR = - PropertyFactory.booleanProperty("ignoringElementContentWhitespace") - .desc("deprecated!Specifies that the XML parser eliminate whitespace in element content. Setting this to 'true' will force validating.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor NAMESPACE_AWARE_DESCRIPTOR = - PropertyFactory.booleanProperty("namespaceAware") - .desc("deprecated!Specifies that the XML parser will provide support for XML namespaces.") - .defaultValue(true) - .build(); - public static final PropertyDescriptor VALIDATING_DESCRIPTOR = - PropertyFactory.booleanProperty("validating") - .desc("deprecated!Specifies that the XML parser will validate documents as they are parsed. This only works for DTDs.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor XINCLUDE_AWARE_DESCRIPTOR = - PropertyFactory.booleanProperty("xincludeAware") - .desc("deprecated!Specifies that the XML parser will process XInclude markup.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor LOOKUP_DESCRIPTOR_DTD = - PropertyFactory.booleanProperty("xincludeAware") - .desc("deprecated!Specifies whether XML parser will attempt to lookup the DTD.") - .defaultValue(false) - .build(); - - public static final EntityResolver SILENT_ENTITY_RESOLVER = (publicId, systemId) -> new InputSource(new ByteArrayInputStream("".getBytes())); - - private boolean coalescing; - private boolean expandEntityReferences; - private boolean ignoringComments; - private boolean ignoringElementContentWhitespace; - private boolean namespaceAware; - private boolean validating; - private boolean xincludeAware; - private boolean lookupDescriptorDoc; - - public XmlParserOptions() { - this.coalescing = COALESCING_DESCRIPTOR.defaultValue(); - this.expandEntityReferences = EXPAND_ENTITY_REFERENCES_DESCRIPTOR.defaultValue(); - this.ignoringComments = IGNORING_COMMENTS_DESCRIPTOR.defaultValue(); - this.ignoringElementContentWhitespace = IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR.defaultValue(); - this.namespaceAware = NAMESPACE_AWARE_DESCRIPTOR.defaultValue(); - this.validating = VALIDATING_DESCRIPTOR.defaultValue(); - this.xincludeAware = XINCLUDE_AWARE_DESCRIPTOR.defaultValue(); - this.lookupDescriptorDoc = LOOKUP_DESCRIPTOR_DTD.defaultValue(); - } - - public XmlParserOptions(Rule rule) { - this.coalescing = rule.getProperty(COALESCING_DESCRIPTOR); - this.expandEntityReferences = rule.getProperty(EXPAND_ENTITY_REFERENCES_DESCRIPTOR); - this.ignoringComments = rule.getProperty(IGNORING_COMMENTS_DESCRIPTOR); - this.ignoringElementContentWhitespace = rule.getProperty(IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR); - this.namespaceAware = rule.getProperty(NAMESPACE_AWARE_DESCRIPTOR); - this.validating = rule.getProperty(VALIDATING_DESCRIPTOR); - this.xincludeAware = rule.getProperty(XINCLUDE_AWARE_DESCRIPTOR); - this.lookupDescriptorDoc = rule.getProperty(LOOKUP_DESCRIPTOR_DTD); - } - - /** - * - * @return the configured entity resolver. If {@link #lookupDescriptorDoc} - * is false it would normally force the XML parser to use its own - * resolver - */ - public EntityResolver getEntityResolver() { - if (!lookupDescriptorDoc) { - return SILENT_ENTITY_RESOLVER; - } else { - return null; - } - } - - public boolean isLookupDescriptorDoc() { - return lookupDescriptorDoc; - } - - public void setLookupDescriptorDoc(boolean lookupDescriptorDoc) { - this.lookupDescriptorDoc = lookupDescriptorDoc; - } - - public boolean isCoalescing() { - return this.coalescing; - } - - public void setCoalescing(boolean coalescing) { - this.coalescing = coalescing; - } - - public boolean isExpandEntityReferences() { - return this.expandEntityReferences; - } - - public void setExpandEntityReferences(boolean expandEntityReferences) { - this.expandEntityReferences = expandEntityReferences; - } - - public boolean isIgnoringComments() { - return this.ignoringComments; - } - - public void setIgnoringComments(boolean ignoringComments) { - this.ignoringComments = ignoringComments; - } - - public boolean isIgnoringElementContentWhitespace() { - return this.ignoringElementContentWhitespace; - } - - public void setIgnoringElementContentWhitespace(boolean ignoringElementContentWhitespace) { - this.ignoringElementContentWhitespace = ignoringElementContentWhitespace; - } - - public boolean isNamespaceAware() { - return this.namespaceAware; - } - - public void setNamespaceAware(boolean namespaceAware) { - this.namespaceAware = namespaceAware; - } - - public boolean isValidating() { - return this.validating; - } - - public void setValidating(boolean validating) { - this.validating = validating; - } - - public boolean isXincludeAware() { - return this.xincludeAware; - } - - public void setXincludeAware(boolean xincludeAware) { - this.xincludeAware = xincludeAware; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (coalescing ? 1231 : 1237); - result = prime * result + (expandEntityReferences ? 1231 : 1237); - result = prime * result + (ignoringComments ? 1231 : 1237); - result = prime * result + (ignoringElementContentWhitespace ? 1231 : 1237); - result = prime * result + (namespaceAware ? 1231 : 1237); - result = prime * result + (validating ? 1231 : 1237); - result = prime * result + (xincludeAware ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final XmlParserOptions that = (XmlParserOptions) obj; - return Objects.equals(this.getSuppressMarker(), that.getSuppressMarker()) - && this.coalescing == that.coalescing && this.expandEntityReferences == that.expandEntityReferences - && this.ignoringComments == that.ignoringComments - && this.ignoringElementContentWhitespace == that.ignoringElementContentWhitespace - && this.namespaceAware == that.namespaceAware && this.validating == that.validating - && this.xincludeAware == that.xincludeAware; - } -} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index 29da1feffc..e7c22dddf9 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -21,37 +21,29 @@ import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; import net.sourceforge.pmd.lang.xml.ast.XmlNode; -public class XmlParserImpl { +public final class XmlParserImpl { - private final XmlParserOptions parserOptions; private final Map nodeCache = new HashMap<>(); - public XmlParserImpl(XmlParserOptions parserOptions) { - this.parserOptions = parserOptions; - } - - private Document parseDocument(String xmlData) throws ParseException { nodeCache.clear(); try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(parserOptions.isNamespaceAware()); - dbf.setValidating(parserOptions.isValidating()); - dbf.setIgnoringComments(parserOptions.isIgnoringComments()); - dbf.setIgnoringElementContentWhitespace(parserOptions.isIgnoringElementContentWhitespace()); - dbf.setExpandEntityReferences(parserOptions.isExpandEntityReferences()); - dbf.setCoalescing(parserOptions.isCoalescing()); - dbf.setXIncludeAware(parserOptions.isXincludeAware()); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + dbf.setIgnoringComments(false); + dbf.setIgnoringElementContentWhitespace(false); + dbf.setExpandEntityReferences(true); + dbf.setCoalescing(false); + dbf.setXIncludeAware(false); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); - documentBuilder.setEntityResolver(parserOptions.getEntityResolver()); return documentBuilder.parse(new InputSource(new StringReader(xmlData))); } catch (ParserConfigurationException | SAXException | IOException e) { throw new ParseException(e); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java deleted file mode 100644 index 13f3ebb65a..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import org.w3c.dom.Attr; -import org.w3c.dom.CharacterData; -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.Entity; -import org.w3c.dom.EntityReference; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.Notation; -import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -/** - * This is a base class for XML Java bases rules that which to visit the nodes - * using the DOM. Subclasses should override the DOM appropriate method and can - * call super to visit children. - */ -public class AbstractDomXmlRule extends AbstractXmlRule { - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - final Node domNode = node.getNode(); - - // Visit the node - visitDomNode(node, domNode, ctx); - - // Visit attributes - visitAttributeNodes(node, domNode, ctx); - } - - protected void visitDomNode(XmlNode node, Node domNode, RuleContext ctx) { - switch (domNode.getNodeType()) { - case Node.CDATA_SECTION_NODE: - visit(node, (CharacterData) domNode, ctx); - break; - case Node.COMMENT_NODE: - visit(node, (Comment) domNode, ctx); - break; - case Node.DOCUMENT_NODE: - visit(node, (Document) domNode, ctx); - break; - case Node.DOCUMENT_TYPE_NODE: - visit(node, (DocumentType) domNode, ctx); - break; - case Node.ELEMENT_NODE: - visit(node, (Element) domNode, ctx); - break; - case Node.ENTITY_NODE: - visit(node, (Entity) domNode, ctx); - break; - case Node.ENTITY_REFERENCE_NODE: - visit(node, (EntityReference) domNode, ctx); - break; - case Node.NOTATION_NODE: - visit(node, (Notation) domNode, ctx); - break; - case Node.PROCESSING_INSTRUCTION_NODE: - visit(node, (ProcessingInstruction) domNode, ctx); - break; - case Node.TEXT_NODE: - visit(node, (Text) domNode, ctx); - break; - default: - throw new RuntimeException("Unexpected node type: " + domNode.getNodeType() + " on node: " + domNode); - } - } - - protected void visitAttributeNodes(XmlNode node, Node domNode, RuleContext ctx) { - NamedNodeMap attributes = domNode.getAttributes(); - if (attributes != null) { - for (int i = 0; i < attributes.getLength(); i++) { - visit(node, (Attr) attributes.item(i), ctx); - } - } - } - - protected void visit(XmlNode node, Attr attr, RuleContext ctx) { - // does nothing by default since attributes are leaf nodes - } - - protected void visit(XmlNode node, CharacterData characterData, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Comment comment, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Document document, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, DocumentType documentType, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Element element, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Entity entity, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, EntityReference entityReference, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Notation notation, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, ProcessingInstruction processingInstruction, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Text text, RuleContext ctx) { - super.visit(node, ctx); - } -} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java index 136ba4634a..6b2d9be243 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java @@ -4,74 +4,12 @@ package net.sourceforge.pmd.lang.xml.rule; -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.xml.XmlLanguageModule; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; -import net.sourceforge.pmd.properties.PropertyDescriptor; /** - * This is a base class for XML Java bases rules. Subclasses should override - * {@link #visit(XmlNode, RuleContext)} and can call super to visit - * children. + * This is a base class for XML Java bases rules. */ -public class AbstractXmlRule extends AbstractRule { +public abstract class AbstractXmlRule extends AbstractRule { - @Deprecated - public static final PropertyDescriptor COALESCING_DESCRIPTOR = XmlParserOptions.COALESCING_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor EXPAND_ENTITY_REFERENCES_DESCRIPTOR = XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor IGNORING_COMMENTS_DESCRIPTOR = XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR = XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor NAMESPACE_AWARE_DESCRIPTOR = XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor VALIDATING_DESCRIPTOR = XmlParserOptions.VALIDATING_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor XINCLUDE_AWARE_DESCRIPTOR = XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR; - public AbstractXmlRule() { - super.setLanguage(LanguageRegistry.getLanguage(XmlLanguageModule.NAME)); - defineProperties(); - } - - protected AbstractXmlRule(Language language) { - super.setLanguage(language); - defineProperties(); - } - - private void defineProperties() { - definePropertyDescriptor(COALESCING_DESCRIPTOR); - definePropertyDescriptor(EXPAND_ENTITY_REFERENCES_DESCRIPTOR); - definePropertyDescriptor(IGNORING_COMMENTS_DESCRIPTOR); - definePropertyDescriptor(IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR); - definePropertyDescriptor(NAMESPACE_AWARE_DESCRIPTOR); - definePropertyDescriptor(VALIDATING_DESCRIPTOR); - definePropertyDescriptor(XINCLUDE_AWARE_DESCRIPTOR); - } - - @Override - public ParserOptions getParserOptions() { - return new XmlParserOptions(this); - } - - @Override - public void apply(Node target, RuleContext ctx) { - visit((XmlNode) target, ctx); - } - - protected void visit(XmlNode node, RuleContext ctx) { - final int numChildren = node.getNumChildren(); - for (int i = 0; i < numChildren; i++) { - XmlNode child = (XmlNode) node.getChild(i); - visit(child, ctx); - } - } } diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java deleted file mode 100644 index 62578df6f3..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.wsdl.rule; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractWsdlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = ""; - XmlNode xmlNode = XmlParsingHelper.WSDL.parse(source); - - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertEquals(3, rule.visitedNodes.size()); - assertEquals("document", rule.visitedNodes.get(0).toString()); - assertEquals("foo", rule.visitedNodes.get(1).toString()); - assertEquals("bar", rule.visitedNodes.get(2).toString()); - } - - private static class MyRule extends AbstractWsdlRule { - final List visitedNodes = new ArrayList<>(); - - MyRule() { - } - - @Override - public void start(RuleContext ctx) { - visitedNodes.clear(); - } - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - visitedNodes.add(node); - super.visit(node, ctx); - } - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java deleted file mode 100644 index 7cfa36572b..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import static net.sourceforge.pmd.lang.ParserOptionsTest.verifyOptionsEqualsHashcode; -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 java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.lang.xml.rule.AbstractXmlRule; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -public class XmlParserOptionsTest { - - @Test - public void testDefaults() { - XmlParserOptions options = new XmlParserOptions(); - assertFalse(options.isCoalescing()); - assertTrue(options.isExpandEntityReferences()); - assertFalse(options.isIgnoringComments()); - assertFalse(options.isIgnoringElementContentWhitespace()); - assertTrue(options.isNamespaceAware()); - assertFalse(options.isValidating()); - assertFalse(options.isXincludeAware()); - - MyRule rule = new MyRule(); - options = (XmlParserOptions) rule.getParserOptions(); - assertFalse(options.isCoalescing()); - assertTrue(options.isExpandEntityReferences()); - assertFalse(options.isIgnoringComments()); - assertFalse(options.isIgnoringElementContentWhitespace()); - assertTrue(options.isNamespaceAware()); - assertFalse(options.isValidating()); - assertFalse(options.isXincludeAware()); - } - - @Test - public void testConstructor() { - MyRule rule = new MyRule(); - - rule.setProperty(XmlParserOptions.COALESCING_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isCoalescing()); - rule.setProperty(XmlParserOptions.COALESCING_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isCoalescing()); - - rule.setProperty(XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isExpandEntityReferences()); - rule.setProperty(XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isExpandEntityReferences()); - - rule.setProperty(XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isIgnoringComments()); - rule.setProperty(XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isIgnoringComments()); - - rule.setProperty(XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isIgnoringElementContentWhitespace()); - rule.setProperty(XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isIgnoringElementContentWhitespace()); - - rule.setProperty(XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isNamespaceAware()); - rule.setProperty(XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isNamespaceAware()); - - rule.setProperty(XmlParserOptions.VALIDATING_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isValidating()); - rule.setProperty(XmlParserOptions.VALIDATING_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isValidating()); - - rule.setProperty(XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isXincludeAware()); - rule.setProperty(XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isXincludeAware()); - } - - @Test - public void testSetters() { - XmlParserOptions options = new XmlParserOptions(); - - options.setSuppressMarker("foo"); - assertEquals("foo", options.getSuppressMarker()); - - options.setCoalescing(true); - assertTrue(options.isCoalescing()); - options.setCoalescing(false); - assertFalse(options.isCoalescing()); - - options.setExpandEntityReferences(true); - assertTrue(options.isExpandEntityReferences()); - options.setExpandEntityReferences(false); - assertFalse(options.isExpandEntityReferences()); - - options.setIgnoringComments(true); - assertTrue(options.isIgnoringComments()); - options.setIgnoringComments(false); - assertFalse(options.isIgnoringComments()); - - options.setIgnoringElementContentWhitespace(true); - assertTrue(options.isIgnoringElementContentWhitespace()); - options.setIgnoringElementContentWhitespace(false); - assertFalse(options.isIgnoringElementContentWhitespace()); - - options.setNamespaceAware(true); - assertTrue(options.isNamespaceAware()); - options.setNamespaceAware(false); - assertFalse(options.isNamespaceAware()); - - options.setValidating(true); - assertTrue(options.isValidating()); - options.setValidating(false); - assertFalse(options.isValidating()); - - options.setXincludeAware(true); - assertTrue(options.isXincludeAware()); - options.setXincludeAware(false); - assertFalse(options.isXincludeAware()); - } - - @Test - public void testEqualsHashcode() throws Exception { - List> properties = listOf(XmlParserOptions.COALESCING_DESCRIPTOR, - XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, - XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, - XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, - XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, - XmlParserOptions.VALIDATING_DESCRIPTOR, - XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR); - - for (PropertyDescriptor property : properties) { - MyRule rule = new MyRule(); - rule.setProperty(property, true); - ParserOptions options1 = rule.getParserOptions(); - rule.setProperty(property, false); - ParserOptions options2 = rule.getParserOptions(); - rule.setProperty(property, true); - ParserOptions options3 = rule.getParserOptions(); - rule.setProperty(property, false); - ParserOptions options4 = rule.getParserOptions(); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); - } - - XmlParserOptions options1 = new XmlParserOptions(); - options1.setSuppressMarker("foo"); - XmlParserOptions options2 = new XmlParserOptions(); - options2.setSuppressMarker("bar"); - XmlParserOptions options3 = new XmlParserOptions(); - options3.setSuppressMarker("foo"); - XmlParserOptions options4 = new XmlParserOptions(); - options4.setSuppressMarker("bar"); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); - } - - private static final class MyRule extends AbstractXmlRule { - } - - public static junit.framework.Test suite() { - return new junit.framework.JUnit4TestAdapter(XmlParserOptionsTest.class); - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java deleted file mode 100644 index 591fad6ee9..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java +++ /dev/null @@ -1,431 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import static net.sourceforge.pmd.lang.xml.XmlParsingHelper.XML; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.util.Iterator; -import java.util.Locale; - -import org.junit.Assert; -import org.junit.Test; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.xpath.Attribute; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; -import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl; -import net.sourceforge.pmd.util.StringUtil; - -/** - * Unit test for the {@link XmlParserImpl}. - */ -public class XmlParserTest { - - private static final String XML_TEST = "\n" + "\n" + "\n" - + "\n" + "\n" + "\n" - + "\n" + "]\n" + ">\n" + "\n" - + " \n" + " entity: &pmd;\n" + " \n" - + " \n" + " \n" + " \n" + ""; - - private static final String XML_NAMESPACE_TEST = "\n" - + "\n" + " \n" - + " entity: &\n" + " \n" + " \n" - + " \n" + " \n" + ""; - - private static final String XML_INVALID_WITH_DTD = "\n" + "\n" + "\n" + "]\n" + ">\n" + "\n" - + " \n" + ""; - - /** - * See bug #1054: XML Rules ever report a line -1 and not the line/column - * where the error occurs - * - */ - @Test - public void testLineNumbers() { - Node document = XML.parse(XML_TEST); - - assertNode(document, "document", 2); - assertLineNumbers(document, 1, 1, 19, 14); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - assertLineNumbers(dtdElement, 2, 1, 11, 1); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertLineNumbers(rootElement, 12, 1, 19, 14); - assertTextNode(rootElement.getChild(0), "\\n "); - assertLineNumbers(rootElement.getChild(0), 12, 14, 13, 4); - assertNode(rootElement.getChild(1), "comment", 0); - assertLineNumbers(rootElement.getChild(1), 13, 5, 13, 29); - assertTextNode(rootElement.getChild(2), "\\n "); - assertLineNumbers(rootElement.getChild(2), 13, 30, 14, 4); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertLineNumbers(child1, 14, 5, 15, 13); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertLineNumbers(child1.getChild(0), 14, 22, 15, 4); - assertTextNode(rootElement.getChild(4), "\\n "); - assertLineNumbers(rootElement.getChild(4), 15, 14, 16, 4); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertLineNumbers(child2, 16, 5, 18, 13); - assertTextNode(child2.getChild(0), "\\n "); - assertLineNumbers(child2.getChild(0), 16, 13, 17, 6); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertLineNumbers(child2.getChild(1), 17, 7, 17, 33); - assertTextNode(child2.getChild(2), "\\n "); - assertLineNumbers(child2.getChild(2), 17, 34, 18, 4); - assertTextNode(rootElement.getChild(6), "\\n"); - assertLineNumbers(rootElement.getChild(6), 18, 14, 18, 14); - } - - /** - * Verifies the default parsing behavior of the XML parser. - */ - @Test - public void testDefaultParsing() { - Node document = XML.parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser with coalescing enabled. - */ - @Test - public void testParsingCoalescingEnabled() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setCoalescing(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 1); - assertTextNode(child2.getChild(0), "\\n cdata section \\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if entities are not - * expanded. - */ - @Test - public void testParsingDoNotExpandEntities() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setExpandEntityReferences(false); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 3, "test", "1"); - assertTextNode(child1.getChild(0), "entity: "); - assertNode(child1.getChild(1), "pmd", 0); - // with java13, expandEntityReferences=false works correctly, and the - // entity &pmd; is not expanded - String text = child1.getChild(2).getImage(); - if ("\n ".equals(text)) { - // java13 and later - assertTextNode(child1.getChild(2), "\\n "); - } else { - assertTextNode(child1.getChild(2), "Copyright: PMD\\n "); - } - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if ignoring comments. - */ - @Test - public void testParsingIgnoreComments() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setIgnoringComments(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 5); - assertTextNode(rootElement.getChild(0), "\\n \\n "); - Node child1 = rootElement.getChild(1); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child2 = rootElement.getChild(3); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(4), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if ignoring whitespaces - * in elements. - */ - @Test - public void testParsingIgnoreElementContentWhitespace() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setIgnoringElementContentWhitespace(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 3); - assertNode(rootElement.getChild(0), "comment", 0); - Node child1 = rootElement.getChild(1); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - Node child2 = rootElement.getChild(2); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - } - - /** - * Verifies the default parsing behavior of the XML parser with namespaces. - */ - @Test - public void testDefaultParsingNamespaces() { - Node document = XML.parse(XML_NAMESPACE_TEST); - - assertNode(document, "document", 1); - Node rootElement = document.getChild(0); - assertNode(rootElement, "pmd:rootElement", 7, "xmlns:pmd", "http://pmd.sf.net"); - Assert.assertEquals("http://pmd.sf.net", ((XmlNode) rootElement).getNode().getNamespaceURI()); - Assert.assertEquals("pmd", ((XmlNode) rootElement).getNode().getPrefix()); - Assert.assertEquals("rootElement", ((XmlNode) rootElement).getNode().getLocalName()); - Assert.assertEquals("pmd:rootElement", ((XmlNode) rootElement).getNode().getNodeName()); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "pmd:child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: &\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "pmd:child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the default parsing behavior of the XML parser with namespaces - * but not namespace aware. - */ - @Test - public void testParsingNotNamespaceAware() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setNamespaceAware(false); - Node document = XML.withParserOptions(parserOptions).parse(XML_NAMESPACE_TEST); - - assertNode(document, "document", 1); - Node rootElement = document.getChild(0); - assertNode(rootElement, "pmd:rootElement", 7, "xmlns:pmd", "http://pmd.sf.net"); - Assert.assertNull(((XmlNode) rootElement).getNode().getNamespaceURI()); - Assert.assertNull(((XmlNode) rootElement).getNode().getPrefix()); - Assert.assertNull(((XmlNode) rootElement).getNode().getLocalName()); - Assert.assertEquals("pmd:rootElement", ((XmlNode) rootElement).getNode().getNodeName()); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "pmd:child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: &\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "pmd:child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser with validation on. - * - * @throws UnsupportedEncodingException - * error - */ - @Test - public void testParsingWithValidation() throws UnsupportedEncodingException { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setValidating(true); - PrintStream oldErr = System.err; - Locale oldLocale = Locale.getDefault(); - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - System.setErr(new PrintStream(bos)); - Locale.setDefault(Locale.ENGLISH); - Node document = XML.withParserOptions(parserOptions).parse(XML_INVALID_WITH_DTD); - Assert.assertNotNull(document); - String output = bos.toString("UTF-8"); - Assert.assertTrue(output.contains("Element type \"invalidChild\" must be declared.")); - Assert.assertTrue(output.contains("The content of element type \"rootElement\" must match \"(child)\".")); - Assert.assertEquals(2, document.getNumChildren()); - Assert.assertEquals("invalidChild", String.valueOf(document.getChild(1).getChild(1))); - } finally { - System.setErr(oldErr); - Locale.setDefault(oldLocale); - } - } - - @Test - public void testWithProcessingInstructions() { - String xml = "]>TEXT>&myentity;<"; - XmlParserOptions options = new XmlParserOptions(); - options.setExpandEntityReferences(false); - Node document = XmlParsingHelper.XML.withParserOptions(options).parse(xml); - Assert.assertNotNull(document); - assertNode(document.getChild(0), "mypi", 0); - assertLineNumbers(document.getChild(0), 1, 22, 1, 29); - } - - @Test - public void testBug1518() throws Exception { - XML.parseResource("parsertests/bug1518.xml"); - } - - @Test - public void testAutoclosingElementLength() { - final String xml = ""; - assertLineNumbers(XML.parse(xml), 1, 1, 1, xml.length()); - } - - /** - * Asserts a single node inclusive attributes. - * - * @param node - * the node - * @param toString - * the to String representation to expect - * @param childs - * number of childs - * @param atts - * attributes - each object pair forms one attribute: first name, - * then value. - */ - private void assertNode(Node node, String toString, int childs, Object... atts) { - Assert.assertEquals(toString, String.valueOf(node)); - Assert.assertEquals(childs, node.getNumChildren()); - Iterator attributeIterator = node.getXPathAttributesIterator(); - if (atts != null) { - for (int i = 0; i < atts.length; i += 2) { - Assert.assertTrue(attributeIterator.hasNext()); - String name = String.valueOf(atts[i]); - Object value = atts[i + 1]; - Attribute attribute = attributeIterator.next(); - Assert.assertEquals(name, attribute.getName()); - Assert.assertEquals(value, attribute.getValue()); - } - } - Assert.assertFalse(attributeIterator.hasNext()); - } - - /** - * Assert a single text node. - * - * @param node - * the node to check - * @param text - * the text to expect - */ - private void assertTextNode(Node node, String text) { - assertTextNode(node, text, "text"); - } - - /** - * Assert a single text node. - * - * @param node - * the node to check - * @param text - * the text to expect - * @param toString - * the to string representation - */ - private void assertTextNode(Node node, String text, String toString) { - Assert.assertEquals(toString, String.valueOf(node)); - Assert.assertEquals(0, node.getNumChildren()); - Assert.assertEquals(text, StringUtil.escapeWhitespace(node.getImage())); - Iterator attributeIterator = node.getXPathAttributesIterator(); - Assert.assertTrue(attributeIterator.hasNext()); - Attribute attribute = attributeIterator.next(); - Assert.assertEquals("Image", attribute.getName()); - Assert.assertEquals(text, StringUtil.escapeWhitespace(attribute.getValue())); - Assert.assertFalse(attributeIterator.hasNext()); - } - - /** - * Assert the line numbers of a node. - * - * @param node - * the node - * @param beginLine - * the begin line - * @param beginColumn - * the begin column - * @param endLine - * the end line - * @param endColumn - * the end column - */ - private void assertLineNumbers(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { - Assert.assertEquals("begin line wrong", beginLine, node.getBeginLine()); - Assert.assertEquals("begin column wrong", beginColumn, node.getBeginColumn()); - Assert.assertEquals("end line wrong", endLine, node.getEndLine()); - Assert.assertEquals("end column wrong", endColumn, node.getEndColumn()); - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java new file mode 100644 index 0000000000..9a7ce14dba --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.ast; + +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertPosition; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.CoordinatesPrinter; +import net.sourceforge.pmd.lang.xml.XmlParsingHelper; + +public class XmlCoordinatesTest extends BaseTreeDumpTest { + + public XmlCoordinatesTest() { + super(CoordinatesPrinter.INSTANCE, ".xml"); + } + + @Override + public @NonNull BaseParsingHelper getParser() { + return XmlParsingHelper.XML.withResourceContext(getClass(), "testdata"); + } + + /** + * See bug #1054: XML Rules ever report a line -1 and not the line/column + * where the error occurs + */ + @Test + public void testLineNumbers() { + doTest("xmlCoords"); + } + + @Test + public void testAutoclosingElementLength() { + final String xml = ""; + assertPosition(XmlParsingHelper.XML.parse(xml), 1, 1, 1, xml.length()); + } + +} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java new file mode 100644 index 0000000000..c2dc80c646 --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter; +import net.sourceforge.pmd.lang.xml.XmlParsingHelper; + +public class XmlParserTest extends BaseTreeDumpTest { + + public XmlParserTest() { + super(new RelevantAttributePrinter(), ".xml"); + } + + @Override + public @NonNull BaseParsingHelper getParser() { + return XmlParsingHelper.XML.withResourceContext(getClass(), "testdata"); + } + + /** + * Verifies the default parsing behavior of the XML parser. + */ + @Test + public void testDefaultParsing() { + doTest("sampleXml"); + } + + @Test + public void testNamespaces() { + doTest("sampleNs"); + } + + @Test + public void testBug1518() { + doTest("bug1518"); + } +} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java deleted file mode 100644 index 19d8ea3c70..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java +++ /dev/null @@ -1,213 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.CharacterData; -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.Entity; -import org.w3c.dom.EntityReference; -import org.w3c.dom.Notation; -import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractDomXmlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = "]>TEXT>&entity;<"; - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setExpandEntityReferences(false); - - XmlNode xmlNode = XmlParsingHelper.XML.withParserOptions(parserOptions).parse(source); - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - List visited = rule.visitedNodes.get("Attr"); - assertEquals(1, visited.size()); - assertEquals("abc", visited.get(0).getLocalName()); - - visited = rule.visitedNodes.get("CharacterData"); - assertEquals(1, visited.size()); - assertEquals("cdata!", ((CharacterData) visited.get(0)).getData()); - - visited = rule.visitedNodes.get("Comment"); - assertEquals("Comment", ((Comment) visited.get(0)).getData()); - - visited = rule.visitedNodes.get("Document"); - assertEquals(1, visited.size()); - - visited = rule.visitedNodes.get("DocumentType"); - assertEquals("testDoc", ((DocumentType) visited.get(0)).getName()); - - visited = rule.visitedNodes.get("Element"); - assertEquals(2, visited.size()); - assertEquals("foo", visited.get(0).getLocalName()); - assertEquals("bar", visited.get(1).getLocalName()); - - // TODO Figure out how to trigger this. - // visited = rule.visitedNodes.get("Entity"); - // assertEquals(0, visited.size()); - - visited = rule.visitedNodes.get("EntityReference"); - assertEquals(1, visited.size()); - assertEquals("entity", ((EntityReference) visited.get(0)).getNodeName()); - - // TODO Figure out how to trigger this. - // visited = rule.visitedNodes.get("Notation"); - // assertEquals(0, visited.size()); - - visited = rule.visitedNodes.get("ProcessingInstruction"); - assertEquals(1, visited.size()); - assertEquals("mypi", ((ProcessingInstruction) visited.get(0)).getTarget()); - - visited = rule.visitedNodes.get("Text"); - assertEquals(3, visited.size()); - assertEquals("TEXT", ((Text) visited.get(0)).getData()); - assertEquals(">", ((Text) visited.get(1)).getData()); - String text = ((Text) visited.get(2)).getData(); - if ("<".equals(text)) { - // java13 and later don't expand entities if setExpandEntityReferences==false - assertEquals("<", ((Text) visited.get(2)).getData()); - } else { - assertEquals("e<", ((Text) visited.get(2)).getData()); - } - } - - @Test - public void dtdIsNotLookedUp() { - String source = "" + ""; - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setLookupDescriptorDoc(false); - XmlNode xmlNode = XmlParsingHelper.XML.withParserOptions(parserOptions).parse(source); - // no exception should be thrown - - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - // first element is still parsed - assertNotNull(rule.visitedNodes.get("Element")); - - } - - @Test - public void xsdIsNotLookedUp() { - String source = " " - + "" + ""; - XmlNode xmlNode = XmlParsingHelper.XML.parse(source); - // no exception should be thrown - // first element is still parsed - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertNotNull(rule.visitedNodes.get("Element")); - - } - - private static class MyRule extends AbstractDomXmlRule { - final Map> visitedNodes = new HashMap<>(); - - MyRule() { - - } - - private void visit(String key, org.w3c.dom.Node node) { - List nodes = visitedNodes.get(key); - if (nodes == null) { - nodes = new ArrayList<>(); - visitedNodes.put(key, nodes); - } - nodes.add(node); - } - - @Override - protected void visit(XmlNode node, Attr attr, RuleContext ctx) { - visit("Attr", attr); - super.visit(node, attr, ctx); - } - - @Override - protected void visit(XmlNode node, CharacterData characterData, RuleContext ctx) { - visit("CharacterData", characterData); - super.visit(node, characterData, ctx); - } - - @Override - protected void visit(XmlNode node, Comment comment, RuleContext ctx) { - visit("Comment", comment); - super.visit(node, comment, ctx); - } - - @Override - protected void visit(XmlNode node, Document document, RuleContext ctx) { - visit("Document", document); - super.visit(node, document, ctx); - } - - @Override - protected void visit(XmlNode node, DocumentType documentType, RuleContext ctx) { - visit("DocumentType", documentType); - super.visit(node, documentType, ctx); - } - - @Override - protected void visit(XmlNode node, Element element, RuleContext ctx) { - visit("Element", element); - super.visit(node, element, ctx); - } - - @Override - protected void visit(XmlNode node, Entity entity, RuleContext ctx) { - visit("Entity", entity); - super.visit(node, entity, ctx); - } - - @Override - protected void visit(XmlNode node, EntityReference entityReference, RuleContext ctx) { - visit("EntityReference", entityReference); - super.visit(node, entityReference, ctx); - } - - @Override - protected void visit(XmlNode node, Notation notation, RuleContext ctx) { - visit("Notation", notation); - super.visit(node, notation, ctx); - } - - @Override - protected void visit(XmlNode node, ProcessingInstruction processingInstruction, RuleContext ctx) { - visit("ProcessingInstruction", processingInstruction); - super.visit(node, processingInstruction, ctx); - } - - @Override - protected void visit(XmlNode node, Text text, RuleContext ctx) { - visit("Text", text); - super.visit(node, text, ctx); - } - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java deleted file mode 100644 index f8d7d63016..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractXmlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = ""; - XmlNode xmlNode = XmlParsingHelper.XML.parse(source); - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertEquals(3, rule.visitedNodes.size()); - assertEquals("document", rule.visitedNodes.get(0).toString()); - assertEquals("foo", rule.visitedNodes.get(1).toString()); - assertEquals("bar", rule.visitedNodes.get(2).toString()); - } - - private static class MyRule extends AbstractXmlRule { - final List visitedNodes = new ArrayList<>(); - - MyRule() { - } - - @Override - public void start(RuleContext ctx) { - visitedNodes.clear(); - } - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - visitedNodes.add(node); - super.visit(node, ctx); - } - } -} diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt new file mode 100644 index 0000000000..238556b80b --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt @@ -0,0 +1,69 @@ ++- document[] + +- deployment-plan[@global-variables = "false", @xmlns = "http://xmlns.oracle.com/weblogic/deployment-plan", @xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance", @xsi:schemaLocation = "http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd"] + +- text[@Image = " + "] + +- application-name[] + | +- text[@Image = "app"] + +- text[@Image = " + "] + +- variable-definition[] + | +- text[@Image = " + "] + | +- variable[] + | | +- text[@Image = " + "] + | | +- name[] + | | | +- text[@Image = "Application_Module_Web_ContextRoot"] + | | +- text[@Image = " + "] + | | +- value[@xsi:nil = "false"] + | | | +- text[@Image = "ikb.adf.kreda.abw.webapp-Abnahmetest-ohne-SSO"] + | | +- text[@Image = " + "] + | +- text[@Image = " + "] + +- text[@Image = " + "] + +- module-override[] + | +- text[@Image = " + "] + | +- module-name[] + | | +- text[@Image = "ikb.adf.kreda.abw.ear"] + | +- text[@Image = " + "] + | +- module-type[] + | | +- text[@Image = "ear"] + | +- text[@Image = " + "] + | +- module-descriptor[@external = "false"] + | | +- text[@Image = " + "] + | | +- root-element[] + | | | +- text[@Image = "application"] + | | +- text[@Image = " + "] + | | +- uri[] + | | | +- text[@Image = "META-INF/application.xml"] + | | +- text[@Image = " + "] + | | +- variable-assignment[] + | | | +- text[@Image = " + "] + | | | +- name[] + | | | | +- text[@Image = "Application_Module_Web_ContextRoot"] + | | | +- text[@Image = " + "] + | | | +- xpath[] + | | | | +- text[@Image = "/application/module/web/[context-root="ikb.adf.kreda.abw.webapp-Local-ohne-SSO"]"] + | | | +- text[@Image = " + "] + | | | +- operation[] + | | | | +- text[@Image = "replace"] + | | | +- text[@Image = " + "] + | | +- text[@Image = " + "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/parsertests/bug1518.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.xml similarity index 100% rename from pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/parsertests/bug1518.xml rename to pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.xml diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt new file mode 100644 index 0000000000..14d8a5cf04 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt @@ -0,0 +1,22 @@ ++- document[] + +- comment[] + +- rootElement[] + +- rootElement[] + +- text[@Image = " + "] + +- comment[] + +- text[@Image = " + "] + +- child1[@test = "1"] + | +- text[@Image = "entity: Copyright: PMD + "] + +- text[@Image = " + "] + +- child2[] + | +- text[@Image = " + "] + | +- cdata-section[@Image = " cdata section "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml new file mode 100644 index 0000000000..3d242f0de8 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml @@ -0,0 +1,21 @@ + + + + + + + + + + ] + > + + + entity: &pmd; + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt new file mode 100644 index 0000000000..967dc4b8a5 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt @@ -0,0 +1,20 @@ ++- document[] + +- pmd:rootElement[@xmlns:pmd = "http://pmd.sf.net"] + +- text[@Image = " + "] + +- comment[] + +- text[@Image = " + "] + +- pmd:child1[@test = "1"] + | +- text[@Image = "entity: & + "] + +- text[@Image = " + "] + +- pmd:child2[] + | +- text[@Image = " + "] + | +- cdata-section[@Image = " cdata section "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml new file mode 100644 index 0000000000..6e0e76f927 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml @@ -0,0 +1,9 @@ + + + + entity: & + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt new file mode 100644 index 0000000000..c946430434 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt @@ -0,0 +1,17 @@ ++- document[@BeginColumn = 1, @BeginLine = 1, @EndColumn = 14, @EndLine = 22] + +- comment[@BeginColumn = 1, @BeginLine = 2, @EndColumn = 12, @EndLine = 2] + +- rootElement[@BeginColumn = 1, @BeginLine = 4, @EndColumn = 9, @EndLine = 13] + +- rootElement[@BeginColumn = 1, @BeginLine = 14, @EndColumn = 14, @EndLine = 22] + +- text[@BeginColumn = 14, @BeginLine = 14, @EndColumn = 4, @EndLine = 15] + +- comment[@BeginColumn = 5, @BeginLine = 15, @EndColumn = 29, @EndLine = 15] + +- text[@BeginColumn = 30, @BeginLine = 15, @EndColumn = 4, @EndLine = 16] + +- child1[@BeginColumn = 5, @BeginLine = 16, @EndColumn = 13, @EndLine = 17] + | +- text[@BeginColumn = 22, @BeginLine = 16, @EndColumn = 4, @EndLine = 17] + +- text[@BeginColumn = 14, @BeginLine = 17, @EndColumn = 4, @EndLine = 18] + +- child2[@BeginColumn = 5, @BeginLine = 18, @EndColumn = 13, @EndLine = 20] + | +- text[@BeginColumn = 13, @BeginLine = 18, @EndColumn = 8, @EndLine = 19] + | +- cdata-section[@BeginColumn = 9, @BeginLine = 19, @EndColumn = 35, @EndLine = 19] + | +- text[@BeginColumn = 36, @BeginLine = 19, @EndColumn = 4, @EndLine = 20] + +- text[@BeginColumn = 14, @BeginLine = 20, @EndColumn = 4, @EndLine = 21] + +- child3[@BeginColumn = 5, @BeginLine = 21, @EndColumn = 20, @EndLine = 21] + +- text[@BeginColumn = 21, @BeginLine = 21, @EndColumn = 21, @EndLine = 21] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml new file mode 100644 index 0000000000..a3ed9805ee --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml @@ -0,0 +1,22 @@ + + + + + + + + + + ] + > + + + entity: &pmd; + + + + + + diff --git a/pom.xml b/pom.xml index f4f387483d..4650ae96c4 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ - 2020-10-24T08:17:24Z + 2020-12-12T08:42:10Z 8 @@ -94,7 +94,7 @@ 3.0.0-M5 8.30 3.1.1 - 3.13.0 + 3.14.0 1.10.8 3.2.0 4.7.2 @@ -320,7 +320,7 @@ false - ${project.basedir}/../pmd-lang-test/target/dokka/pmd-lang-test + ${project.basedir}/../pmd-lang-test/target/dokkaJavadocJar ../../pmd-lang-test/${project.version} @@ -683,7 +683,7 @@ net.sf.saxon Saxon-HE - 10.1 + 10.2 org.mozilla @@ -719,7 +719,7 @@ org.codehaus.groovy groovy - 2.4.7 + 2.4.21