diff --git a/.gitignore b/.gitignore
index 5edd8ada36..69b2c1e6d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ bin/
*.patch
*/src/site/site.xml
pmd-core/dependency-reduced-pom.xml
+.bundle
+vendor
diff --git a/.travis.yml b/.travis.yml
index 7c5909a066..a52bb4c806 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,8 +29,12 @@ before_install:
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
- bash .travis/setup-secrets.sh
- bash .travis/configure-maven.sh
+ - rvm install 2.4.1
+ - rvm use 2.4.1
# Install OracleJDK 10 - see https://sormuras.github.io/blog/2018-03-20-jdk-matrix.html
-install: . ./install-jdk.sh -F 10 -L BCL
+install:
+ - . ./install-jdk.sh -F 10 -L BCL
+ - bundle install --without=release_notes_preprocessing
before_script: true
script: source .travis/build-$BUILD.sh
after_success: true
@@ -67,6 +71,7 @@ notifications:
cache:
directories:
- "$HOME/.m2"
+ - vendor/bundle
# Secure Keys, that need to be set for snapshot builds
@@ -79,7 +84,6 @@ cache:
# PMD_SF_USER - the sourceforge user, which is used to upload created binaries to sf files section. Note: an ssh key is
# required. See "before_install".
#
-#
# Secure Keys, that need to be set for releases:
#
# PMD_SF_APIKEY - used to make the new release the default file in the files section. See https://sourceforge.net/auth/preferences/
diff --git a/.travis/all-java.xml b/.travis/all-java.xml
new file mode 100644
index 0000000000..bb560b5be1
--- /dev/null
+++ b/.travis/all-java.xml
@@ -0,0 +1,18 @@
+
+
+
+ Every java rule in PMD which is used for the regression tests with pmdtester
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.travis/build-deploy.sh b/.travis/build-deploy.sh
index 526e1953bf..bad3f4eb92 100755
--- a/.travis/build-deploy.sh
+++ b/.travis/build-deploy.sh
@@ -27,8 +27,23 @@ TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE}"
fi
}
+function upload_baseline() {
+ log_info "Generating and uploading baseline for pmdtester..."
+ cd ..
+ pmdtester -m single -r ./pmd -p ${TRAVIS_BRANCH} -pc ./pmd/.travis/all-java.xml -l ./pmd/.travis/project-list.xml -f
+ cd target/reports
+ BRANCH_FILENAME="${TRAVIS_BRANCH/\//_}"
+ zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/
+ rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/
+ if [ $? -ne 0 ]; then
+ log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!"
+ log_error "Please upload manually: https://sourceforge.net/projects/pmd/files/pmd-regression-tester/"
+ else
+ log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to sourceforge"
+ fi
+}
-VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec | tail -1)
+VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec)
log_info "Building PMD ${VERSION} on branch ${TRAVIS_BRANCH}"
MVN_BUILD_FLAGS="-B -V"
@@ -37,6 +52,11 @@ if travis_isPullRequest; then
log_info "This is a pull-request build"
./mvnw verify $MVN_BUILD_FLAGS
+ (
+ set +e
+ log_info "Running danger"
+ bundle exec danger --verbose
+ )
elif travis_isPush; then
@@ -77,6 +97,9 @@ elif travis_isPush; then
else
log_success "Successfully uploaded release_notes.md as ReadMe.md to sourceforge"
fi
+
+ upload_baseline
+
true
)
diff --git a/.travis/project-list.xml b/.travis/project-list.xml
new file mode 100644
index 0000000000..5471d6f2e3
--- /dev/null
+++ b/.travis/project-list.xml
@@ -0,0 +1,27 @@
+
+
+
+ Standard Projects
+
+
+ checkstyle
+ git
+ https://github.com/checkstyle/checkstyle
+ checkstyle-8.10
+
+
+
+ spring-framework
+ git
+ https://github.com/spring-projects/spring-framework
+ v5.0.6.RELEASE
+
+
+
+
diff --git a/.travis/release.sh b/.travis/release.sh
index c6f9e12a62..883f9721d4 100755
--- a/.travis/release.sh
+++ b/.travis/release.sh
@@ -28,11 +28,16 @@ if [ "${BUILD}" = "deploy" ]; then
true
)
+# install the gems required for rendering the release notes
+bundle install --with=release_notes_preprocessing
+
+# renders, and skips the first 6 lines - the Jekyll front-matter
+RENDERED_RELEASE_NOTES=$(bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6)
# Assumes, the release has already been created by travis github releases provider
RELEASE_ID=$(curl -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" https://api.github.com/repos/pmd/pmd/releases/tags/pmd_releases/${RELEASE_VERSION}|jq ".id")
RELEASE_NAME="PMD ${RELEASE_VERSION} ($(date -u +%d-%B-%Y))"
-RELEASE_BODY=$(tail -n +6 docs/pages/release_notes.md) # skips the first 6 lines - the heading 'PMD Release Notes'
+RELEASE_BODY="$RENDERED_RELEASE_NOTES"
RELEASE_BODY="${RELEASE_BODY//'\'/\\\\}"
RELEASE_BODY="${RELEASE_BODY//$'\r'/}"
RELEASE_BODY="${RELEASE_BODY//$'\n'/\\r\\n}"
@@ -79,6 +84,7 @@ mkdir pmd.github.io
git pull --depth=1 origin master
log_info "Copying documentation from ../docs/pmd-doc-${RELEASE_VERSION}/ ..."
rsync -ah --stats ../docs/pmd-doc-${RELEASE_VERSION}/ pmd-${RELEASE_VERSION}/
+ git status
git add pmd-${RELEASE_VERSION}
git commit -q -m "Added pmd-${RELEASE_VERSION}"
@@ -87,6 +93,7 @@ mkdir pmd.github.io
git add latest
git commit -q -m "Copying pmd-${RELEASE_VERSION} to latest"
+ log_info "Generating sitemap.xml"
../.travis/sitemap_generator.sh > sitemap.xml
git add sitemap.xml
git commit -q -m "Generated sitemap.xml"
@@ -103,7 +110,7 @@ mkdir pmd.github.io
log_info "Uploading the new release to pmd.sourceforge.net which serves as an archive..."
- travis_wait rsync -ah --stats pmd-doc-${VERSION}/ ${PMD_SF_USER}@web.sourceforge.net:/home/project-web/pmd/htdocs/pmd-${RELEASE_VERSION}/
+ travis_wait rsync -ah --stats docs/pmd-doc-${RELEASE_VERSION}/ ${PMD_SF_USER}@web.sourceforge.net:/home/project-web/pmd/htdocs/pmd-${RELEASE_VERSION}/
if [ $? -ne 0 ]; then
log_error "Uploading documentation to pmd.sourceforge.net failed..."
diff --git a/.travis/render_release_notes.rb b/.travis/render_release_notes.rb
new file mode 100755
index 0000000000..d8acab0e74
--- /dev/null
+++ b/.travis/render_release_notes.rb
@@ -0,0 +1,42 @@
+#!/usr/bin/env ruby
+
+# Renders the release notes for Github releases,
+# and prints them to standard output
+
+# Doesn't trim the header, which is done in shell
+
+# Args:
+# ARGV[0] : location of the file to render
+
+require "liquid"
+require "safe_yaml"
+
+# include some custom liquid extensions
+require_relative "../docs/_plugins/rule_tag"
+require_relative "../docs/_plugins/custom_filters"
+
+# explicitly setting safe mode to get rid of the warning
+SafeYAML::OPTIONS[:default_mode] = :safe
+
+# START OF THE SCRIPT
+
+unless ARGV.length == 1 && File.exists?(ARGV[0])
+ print "\e[31m[ERROR] In #{$0}: The first arg must be a valid file name\e[0m"
+ exit 1
+end
+
+release_notes_file = ARGV[0]
+
+liquid_env = {
+ # wrap the config under a "site." namespace because that's how jekyll does it
+ 'site' => YAML.load_file("docs/_config.yml"),
+ # This signals the links in {% rule %} tags that they should be rendered as absolute
+ 'is_release_notes_processor' => true
+}
+
+
+to_render = File.read(release_notes_file)
+rendered = Liquid::Template.parse(to_render).render(liquid_env)
+
+
+print(rendered)
diff --git a/Dangerfile b/Dangerfile
new file mode 100644
index 0000000000..07a9aef9a2
--- /dev/null
+++ b/Dangerfile
@@ -0,0 +1,57 @@
+require 'pmdtester'
+require 'time'
+require 'logger'
+
+@logger = Logger.new(STDOUT)
+
+def run_pmdtester
+ Dir.chdir('..') do
+ argv = ['-r', './pmd', '-b', "#{ENV['TRAVIS_BRANCH']}", '-p', 'FETCH_HEAD', '-m', 'online', '-a']
+ Process.fork do
+ begin
+ runner = PmdTester::Runner.new(argv)
+ introduce_new_pmd_errors = runner.run
+ warn("The PR may introduce new PMD errors!") if introduce_new_pmd_errors
+ rescue StandardError => e
+ warn("Running pmdtester failed, this message is mainly used to remind the maintainers of PMD.")
+ @logger.error "Running pmdtester failed: #{e.inspect}"
+ exit 1
+ end
+ end
+ Process.wait
+
+ upload_report if $?.success?
+ end
+end
+
+def upload_report
+ Dir.chdir('target/reports') do
+ tar_filename = "pr-#{ENV['TRAVIS_PULL_REQUEST']}-diff-report-#{Time.now.strftime("%Y-%m-%dT%H-%M-%SZ")}.tar"
+ unless Dir.exist?('diff/')
+ message("No java rules are changed!", sticky: true)
+ return
+ end
+
+ `tar -cf #{tar_filename} diff/`
+ report_url = `curl -u #{ENV['CHUNK_TOKEN']} -T #{tar_filename} chunk.io`
+ if $?.success?
+ @logger.info "Successfully uploaded #{tar_filename} to chunk.io"
+
+ # set value of sticky to true and the message is kept after new commits are submited to the PR
+ message("Please check the [regression diff report](#{report_url.chomp}/diff/index.html) to make sure that everything is expected", sticky: true)
+ else
+ @logger.error "Error while uploading #{tar_filename} to chunk.io: #{report_url}"
+ warn("Uploading the diff report failed, this message is mainly used to remind the maintainers of PMD.")
+ end
+ end
+end
+
+# Perform regression testing
+can_merge = github.pr_json['mergeable']
+if can_merge
+ run_pmdtester
+else
+ warn("This PR cannot be merged yet.", sticky: false)
+end
+
+# vim: syntax=ruby
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000..ed76d93bfe
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,12 @@
+source 'https://rubygems.org/'
+
+gem 'pmdtester', '~> 1.0.0.pre.beta3'
+gem 'danger', '~> 5.6', '>= 5.6'
+
+# This group is only needed during release (via .travis/release.sh and do-release.sh)
+group :release_notes_preprocessing do
+ gem 'liquid', '>=4.0.0'
+ gem 'safe_yaml', '>=1.0'
+end
+
+# vim: syntax=ruby
diff --git a/do-release.sh b/do-release.sh
index 41a858b6fc..5417ffd455 100755
--- a/do-release.sh
+++ b/do-release.sh
@@ -62,19 +62,20 @@ echo "Press enter to continue..."
read
+# install bundles needed for rendering release notes
+bundle install with=release_notes_preprocessing --path vendor/bundle
+
+
export RELEASE_VERSION
export DEVELOPMENT_VERSION
export CURRENT_BRANCH
RELEASE_RULESET="pmd-core/src/main/resources/rulesets/releases/${RELEASE_VERSION//\./}.xml"
-echo "* Update version/release info in **docs/pages/release_notes.md**."
-echo
-echo " ## $(date -u +%d-%B-%Y) - ${RELEASE_VERSION}"
-echo
echo "* Update date info in **docs/_config.yml**."
+echo " date: $(date -u +%d-%B-%Y)"
echo
-echo "* Ensure all the new rules are listed in a the proper file:"
+echo "* Ensure all the new rules are listed in the proper file:"
echo " ${RELEASE_RULESET}"
echo
echo "* Update **../pmd.github.io/_config.yml** to mention the new release"
@@ -109,18 +110,6 @@ echo
echo "Tag has been pushed.... now check travis build: "
echo
echo
-echo "Submit news to SF on page. You can use"
-echo "the following template:"
-echo
-cat < docs/pages/release_notes_old.md
echo "$NEW_RELEASE_NOTES" >> docs/pages/release_notes_old.md
echo >> docs/pages/release_notes_old.md
@@ -154,11 +143,11 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
-## ????? - ${DEVELOPMENT_VERSION}
+## {{ site.pmd.date }} - {{ site.pmd.version | append_unless: is_release_version, "-SNAPSHOT" }}
-The PMD team is pleased to announce PMD ${DEVELOPMENT_VERSION%-SNAPSHOT}.
+The PMD team is pleased to announce PMD {{ site.pmd.version }}.
-This is a minor release.
+This is a {{ site.pmd.release_type }} release.
### Table Of Contents
@@ -185,15 +174,16 @@ echo
echo
echo "Verify the new release on github: "
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:"
echo
-echo "Send out an announcement mail to the mailing list:"
echo "To: PMD Developers List "
echo "Subject: [ANNOUNCE] PMD ${RELEASE_VERSION} Released"
echo
echo " * Downloads: https://github.com/pmd/pmd/releases/tag/pmd_releases%2F${RELEASE_VERSION}"
echo " * Documentation: https://pmd.github.io/pmd-${RELEASE_VERSION}/"
echo
-echo " And Copy-Paste the release notes"
+echo "$NEW_RELEASE_NOTES"
echo
echo
echo
@@ -203,4 +193,3 @@ echo "------------------------------------------"
echo
-
diff --git a/docs/_config.yml b/docs/_config.yml
index 10a16227aa..86838109bd 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,8 +1,9 @@
repository: pmd/pmd
pmd:
- version: 6.6.0
+ version: 6.7.0
date: 2018-??-??
+ release_type: minor
output: web
# this property is useful for conditional filtering of content that is separate from the PDF.
diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml
index 760a09fc4f..7e01f5c0c4 100644
--- a/docs/_data/sidebars/pmd_sidebar.yml
+++ b/docs/_data/sidebars/pmd_sidebar.yml
@@ -325,6 +325,9 @@ entries:
- title: How PMD works
url: /pmd_devdocs_how_pmd_works.html
output: web, pdf
+ - title: Pmdtester
+ url: /pmd_devdocs_pmdtester.html
+ output: web, pdf
- title: null
output: web, pdf
subfolders:
diff --git a/docs/_plugins/custom_filters.rb b/docs/_plugins/custom_filters.rb
index 3d77f8af0c..42ca4b0fef 100644
--- a/docs/_plugins/custom_filters.rb
+++ b/docs/_plugins/custom_filters.rb
@@ -68,6 +68,19 @@ module CustomFilters
end
end
+ # Append the suffix only if the condition argument is truthy
+ def append_if(str, condition, suffix)
+ if condition
+ str + suffix
+ else
+ str
+ end
+ end
+
+ def append_unless(str, condition, suffix)
+ append_if(str, !condition, suffix)
+ end
+
def render_markdown(input)
if input
res = input
diff --git a/docs/_plugins/rule_tag.rb b/docs/_plugins/rule_tag.rb
new file mode 100644
index 0000000000..0e81e64bdd
--- /dev/null
+++ b/docs/_plugins/rule_tag.rb
@@ -0,0 +1,73 @@
+
+
+# Tag to reference a rule
+#
+# Usage:
+# {% rule "java/codestyle/LinguisticNaming" %} works from anywhere
+# If inside the doc page of a ruleset/category, the language and
+# category segment can be dropped, they're taken to be the same.
+#
+# That means rule descriptions can also reference rules e.g. by simply
+# saying {% rule AvoidFinalLocalVars %} if they're in the same category
+# This could allow deprecated rule notices to link to the replacement rule
+
+class RuleTag < Liquid::Tag
+ def initialize(tag_name, rule_ref, tokens)
+ super
+
+ if %r!(?:(?:(\w+)/)?(\w+)/)?(\w+)! =~ rule_ref
+
+ @lang_name = $1
+ @category_name = $2
+ @rule_name = $3
+
+ else
+ fail "Invalid rule reference format"
+ end
+
+ end
+
+ def render(context)
+
+
+
+ if /pmd_rules_(\w+)_(\w+)\.html/ =~ context["page.permalink"]
+ # If we're in a page describing a ruleset,
+ # omitted language or category are taken to be that of this page
+ @lang_name = @lang_name || $1
+ @category_name = @category_name || $2
+ end
+
+
+ unless @category_name
+ fail "no category for rule reference, and no implicit category name available"
+ end
+
+ unless @lang_name
+ fail "no language for rule reference, and no implicit language name available"
+ end
+
+
+ url_prefix = ""
+ # This is passed from the release notes processing script
+ # When generating links for the release notes, the links should be absolute
+ if context["is_release_notes_processor"]
+ url_prefix = "https://pmd.github.io/pmd-#{context["site.pmd.version"]}/"
+ end
+
+ markup_link(@rule_name, url_prefix + relativelink(@lang_name, @category_name, @rule_name))
+ end
+
+ private
+
+ def relativelink(lang, cat, rname)
+ "pmd_rules_#{lang}_#{cat}.html##{rname.downcase}"
+ end
+
+ def markup_link(rname, link)
+ "[`#{rname}`](#{link})"
+ end
+
+end
+
+Liquid::Template.register_tag('rule', RuleTag)
diff --git a/docs/pages/pmd/devdocs/pmdtester.md b/docs/pages/pmd/devdocs/pmdtester.md
new file mode 100644
index 0000000000..407ddd6de5
--- /dev/null
+++ b/docs/pages/pmd/devdocs/pmdtester.md
@@ -0,0 +1,24 @@
+---
+title: Pmdtester
+tags: [devdocs]
+permalink: pmd_devdocs_pmdtester.html
+author: Binguo Bao
+---
+
+## Introduction
+Pmdtester is a regression testing tool that ensures no new problems and unexpected behaviors will be introduced to PMD after fixing an issue.
+It can also be used to verify, that new rules work as expected.It has been integrated into travis CI and is actually used automatically for PRs.
+Regression difference reports are commented back to the PR for the reviewer's information e.g. https://github.com/pmd/pmd/pull/1265#issuecomment-408945709
+
+## Run pmdtester locally
+**Install pmdtester**
+
+`gem install pmdtester --pre`
+
+**Verifying your local changes and generate a diff-report locally**
+
+`pmdtester -r YOUR_LOCAL_PMD_GIT_REPO_ROOT_DIR -b master -p YOUR_DEVELOPMENT_BRANCH`
+
+The regression difference report is placed in the `YOUR_WORKING_DIR/target/reports/diff` directory.
+
+For more documentation on pmdtester, see [README.rdoc](https://github.com/pmd/pmd-regression-tester/blob/master/README.rdoc)
diff --git a/docs/pages/pmd/projectdocs/committers/releasing.md b/docs/pages/pmd/projectdocs/committers/releasing.md
index 622b88fe93..5800289cac 100644
--- a/docs/pages/pmd/projectdocs/committers/releasing.md
+++ b/docs/pages/pmd/projectdocs/committers/releasing.md
@@ -25,12 +25,9 @@ Make sure code is up to date and everything is committed and pushed with git:
### The Release Notes and docs
-At a very minimum, the current date must be noted in the release notes and the download section. Also, the version
-must be adjusted. E.g. by removing "-SNAPSHOT".
-
You can find the release notes here: `docs/pages/release_notes.md`.
-The date for the download section is to be entered in `docs/_config.yml`, e.g.
+The date and the version must be updated in `docs/_config.yml`, e.g.
```
pmd:
@@ -140,6 +137,7 @@ the following template:
* Move version/release info from **docs/pages/release_notes.md** to **docs/pages/release_notes_old.md**.
* Update version/release info in **docs/pages/release_notes.md**. Use the following template:
+{%raw%}
```
---
title: PMD Release Notes
@@ -147,11 +145,11 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
-## ????? - ${DEVELOPMENT_VERSION}
+## {{ site.pmd.date }} - {{ site.pmd.version | append_unless: is_release_version, "-SNAPSHOT" }}
-The PMD team is pleased to announce PMD ${DEVELOPMENT_VERSION%-SNAPSHOT}.
+The PMD team is pleased to announce PMD {{ site.pmd.version }}.
-This is a bug fixing release.
+This is a {{ site.pmd.release_type }} release.
### Table Of Contents
@@ -169,6 +167,8 @@ This is a bug fixing release.
### External Contributions
```
+{%endraw%}
+
Commit and push
diff --git a/docs/pages/pmd/rules/java.md b/docs/pages/pmd/rules/java.md
index 2ead22586f..48387179af 100644
--- a/docs/pages/pmd/rules/java.md
+++ b/docs/pages/pmd/rules/java.md
@@ -28,7 +28,7 @@ folder: pmd/rules
* [JUnit4TestShouldUseBeforeAnnotation](pmd_rules_java_bestpractices.html#junit4testshouldusebeforeannotation): In JUnit 3, the setUp method was used to set up all data entities required in running tests. JUni...
* [JUnit4TestShouldUseTestAnnotation](pmd_rules_java_bestpractices.html#junit4testshouldusetestannotation): In JUnit 3, the framework executed all methods which started with the word test as a unit test. I...
* [JUnitAssertionsShouldIncludeMessage](pmd_rules_java_bestpractices.html#junitassertionsshouldincludemessage): JUnit assertions should include an informative message - i.e., use the three-argument version of ...
-* [JUnitTestContainsTooManyAsserts](pmd_rules_java_bestpractices.html#junittestcontainstoomanyasserts): JUnit tests should not contain too many asserts. Many asserts are indicative of a complex test, ...
+* [JUnitTestContainsTooManyAsserts](pmd_rules_java_bestpractices.html#junittestcontainstoomanyasserts): Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, fo...
* [JUnitTestsShouldIncludeAssert](pmd_rules_java_bestpractices.html#junittestsshouldincludeassert): JUnit tests should include at least one assertion. This makes the tests more robust, and using a...
* [JUnitUseExpected](pmd_rules_java_bestpractices.html#junituseexpected): In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions.
* [LooseCoupling](pmd_rules_java_bestpractices.html#loosecoupling): The use of implementation types (i.e., HashSet) as object references limits your ability to use a...
@@ -79,6 +79,7 @@ folder: pmd/rules
* [EmptyMethodInAbstractClassShouldBeAbstract](pmd_rules_java_codestyle.html#emptymethodinabstractclassshouldbeabstract): Empty or auto-generated methods in an abstract class should be tagged as abstract. This helps to ...
* [ExtendsObject](pmd_rules_java_codestyle.html#extendsobject): No need to explicitly extend Object.
* [FieldDeclarationsShouldBeAtStartOfClass](pmd_rules_java_codestyle.html#fielddeclarationsshouldbeatstartofclass): Fields should be declared at the top of the class, before any method declarations, constructors, ...
+* [FieldNamingConventions](pmd_rules_java_codestyle.html#fieldnamingconventions): Configurable naming conventions for field declarations. This rule reports variable declarations ...
* [ForLoopShouldBeWhileLoop](pmd_rules_java_codestyle.html#forloopshouldbewhileloop): Some for loops can be simplified to while loops, this makes them more concise.
* [ForLoopsMustUseBraces](pmd_rules_java_codestyle.html#forloopsmustusebraces): Deprecated Avoid using 'for' statements without using curly braces. If the code formatting or indentation is...
* [FormalParameterNamingConventions](pmd_rules_java_codestyle.html#formalparameternamingconventions): Configurable naming conventions for formal parameters of methods and lambdas. This rul...
@@ -86,6 +87,7 @@ folder: pmd/rules
* [IdenticalCatchBranches](pmd_rules_java_codestyle.html#identicalcatchbranches): Identical 'catch' branches use up vertical space and increase the complexity of code without ...
* [IfElseStmtsMustUseBraces](pmd_rules_java_codestyle.html#ifelsestmtsmustusebraces): Deprecated Avoid using if..else statements without using surrounding braces. If the code formatting or inden...
* [IfStmtsMustUseBraces](pmd_rules_java_codestyle.html#ifstmtsmustusebraces): Deprecated Avoid using if statements without using braces to surround the code block. If the code formatting...
+* [LinguisticNaming](pmd_rules_java_codestyle.html#linguisticnaming): This rule finds Linguistic Naming Antipatterns. It checks for fields, that are named, as if they ...
* [LocalHomeNamingConvention](pmd_rules_java_codestyle.html#localhomenamingconvention): The Local Home interface of a Session EJB should be suffixed by 'LocalHome'.
* [LocalInterfaceSessionNamingConvention](pmd_rules_java_codestyle.html#localinterfacesessionnamingconvention): The Local Interface of a Session EJB should be suffixed by 'Local'.
* [LocalVariableCouldBeFinal](pmd_rules_java_codestyle.html#localvariablecouldbefinal): A local variable assigned only once can be declared final.
diff --git a/docs/pages/pmd/rules/java/bestpractices.md b/docs/pages/pmd/rules/java/bestpractices.md
index 9b76e94176..56759de176 100644
--- a/docs/pages/pmd/rules/java/bestpractices.md
+++ b/docs/pages/pmd/rules/java/bestpractices.md
@@ -456,8 +456,8 @@ through the @RunWith(Suite.class) annotation.
**This rule is defined by the following XPath expression:**
``` xpath
//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='suite']]
-[MethodDeclaration/ResultType/Type/ReferenceType/ClassOrInterfaceType[@Image='Test' or @Image = 'junit.framework.Test']]
-[not(MethodDeclaration/Block//ClassOrInterfaceType[@Image='JUnit4TestAdapter'])]
+[MethodDeclaration/ResultType/Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('junit.framework.Test')]]
+[not(MethodDeclaration/Block//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.JUnit4TestAdapter')])]
```
**Example(s):**
@@ -488,13 +488,18 @@ public class GoodTest {
**Priority:** Medium (3)
In JUnit 3, the tearDown method was used to clean up all data entities required in running tests.
-JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test
+JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test.
+JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after each test or after all tests in the class, respectively.
**This rule is defined by the following XPath expression:**
``` xpath
-//CompilationUnit[not(ImportDeclaration/Name[starts-with(@Image, "org.testng")])]
-//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='tearDown']]
-[count(Annotation//Name[@Image='After'])=0]
+//ClassOrInterfaceBodyDeclaration
+ [MethodDeclaration/MethodDeclarator[@Image='tearDown']]
+ [count(Annotation//Name[
+ pmd-java:typeIs('org.junit.After')
+ or pmd-java:typeIs('org.junit.jupiter.api.AfterEach')
+ or pmd-java:typeIs('org.junit.jupiter.api.AfterAll')
+ or pmd-java:typeIs('org.testng.annotations.AfterMethod')])=0]
```
**Example(s):**
@@ -524,13 +529,18 @@ public class MyTest2 {
**Priority:** Medium (3)
In JUnit 3, the setUp method was used to set up all data entities required in running tests.
-JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests
+JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests.
+JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods before each test or before all tests in the class, respectively.
**This rule is defined by the following XPath expression:**
``` xpath
-//CompilationUnit[not(ImportDeclaration/Name[starts-with(@Image, "org.testng")])]
-//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='setUp']]
-[count(Annotation//Name[@Image='Before'])=0]
+//ClassOrInterfaceBodyDeclaration
+ [MethodDeclaration/MethodDeclarator[@Image='setUp']]
+ [count(Annotation//Name[
+ pmd-java:typeIs('org.junit.Before')
+ or pmd-java:typeIs('org.junit.jupiter.api.BeforeEach')
+ or pmd-java:typeIs('org.junit.jupiter.api.BeforeAll')
+ or pmd-java:typeIs('org.testng.annotations.BeforeMethod')])=0]
```
**Example(s):**
@@ -561,6 +571,7 @@ public class MyTest2 {
In JUnit 3, the framework executed all methods which started with the word test as a unit test.
In JUnit 4, only methods annotated with the @Test annotation are executed.
+In JUnit 5, one of the following annotations should be used for tests: @Test, @RepeatedTest, @TestFactory, @TestTemplate or @ParameterizedTest.
**This rule is defined by the following XPath expression:**
``` xpath
@@ -569,7 +580,12 @@ In JUnit 4, only methods annotated with the @Test annotation are executed.
or ExtendsList/ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]]
/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration[MethodDeclaration[@Public=true()]/MethodDeclarator[starts-with(@Image, 'test')]]
- [not(Annotation//Name[pmd-java:typeIs('org.junit.Test')])]
+ [not(Annotation//Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ])]
```
**Example(s):**
@@ -633,13 +649,24 @@ public class Foo extends TestCase {
**Priority:** Medium (3)
-JUnit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which
-it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios.
+Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which
+it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios.
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".
+
**This rule is defined by the following XPath expression:**
``` xpath
-//MethodDeclarator[(@Image[fn:matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[@Image='Test']) and count(..//PrimaryPrefix/Name[@Image[fn:matches(.,'^assert')]]) > $maximumAsserts]
+//MethodDeclarator[@Image[fn:matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ or pmd-java:typeIs('org.testng.annotations.Test')
+ ]]
+ [count(..//PrimaryPrefix/Name[@Image[fn:matches(.,'^assert')]]) > $maximumAsserts]
```
**Example(s):**
@@ -1312,7 +1339,14 @@ This rule detects JUnit assertions in object equality. These assertions should b
PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name
[ends-with(@Image, '.equals')]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
@@ -1350,7 +1384,14 @@ more specific methods, like assertNull, assertNotNull.
Expression/EqualityExpression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral
]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
@@ -1390,7 +1431,14 @@ by more specific methods, like assertSame, assertNotSame.
[PrimarySuffix/Arguments
/ArgumentList/Expression
/EqualityExpression[count(.//NullLiteral) = 0]]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/java/codestyle.md b/docs/pages/pmd/rules/java/codestyle.md
index fb83a279c4..b6f2a4db50 100644
--- a/docs/pages/pmd/rules/java/codestyle.md
+++ b/docs/pages/pmd/rules/java/codestyle.md
@@ -5,7 +5,7 @@ permalink: pmd_rules_java_codestyle.html
folder: pmd/rules/java
sidebaractiveurl: /pmd_rules_java.html
editmepath: ../pmd-java/src/main/resources/category/java/codestyle.xml
-keywords: Code Style, AbstractNaming, AtLeastOneConstructor, AvoidDollarSigns, AvoidFinalLocalVariable, AvoidPrefixingMethodParameters, AvoidProtectedFieldInFinalClass, AvoidProtectedMethodInFinalClassNotExtending, AvoidUsingNativeCode, BooleanGetMethodName, CallSuperInConstructor, ClassNamingConventions, CommentDefaultAccessModifier, ConfusingTernary, ControlStatementBraces, DefaultPackage, DontImportJavaLang, DuplicateImports, EmptyMethodInAbstractClassShouldBeAbstract, ExtendsObject, FieldDeclarationsShouldBeAtStartOfClass, ForLoopShouldBeWhileLoop, ForLoopsMustUseBraces, FormalParameterNamingConventions, GenericsNaming, IdenticalCatchBranches, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, LocalHomeNamingConvention, LocalInterfaceSessionNamingConvention, LocalVariableCouldBeFinal, LocalVariableNamingConventions, LongVariable, MDBAndSessionBeanNamingConvention, MethodArgumentCouldBeFinal, MethodNamingConventions, MIsLeadingVariableName, NoPackage, OnlyOneReturn, PackageCase, PrematureDeclaration, RemoteInterfaceNamingConvention, RemoteSessionInterfaceNamingConvention, ShortClassName, ShortMethodName, ShortVariable, SuspiciousConstantFieldName, TooManyStaticImports, UnnecessaryAnnotationValueElement, UnnecessaryConstructor, UnnecessaryFullyQualifiedName, UnnecessaryLocalBeforeReturn, UnnecessaryModifier, UnnecessaryReturn, UselessParentheses, UselessQualifiedThis, VariableNamingConventions, WhileLoopsMustUseBraces
+keywords: Code Style, AbstractNaming, AtLeastOneConstructor, AvoidDollarSigns, AvoidFinalLocalVariable, AvoidPrefixingMethodParameters, AvoidProtectedFieldInFinalClass, AvoidProtectedMethodInFinalClassNotExtending, AvoidUsingNativeCode, BooleanGetMethodName, CallSuperInConstructor, ClassNamingConventions, CommentDefaultAccessModifier, ConfusingTernary, ControlStatementBraces, DefaultPackage, DontImportJavaLang, DuplicateImports, EmptyMethodInAbstractClassShouldBeAbstract, ExtendsObject, FieldDeclarationsShouldBeAtStartOfClass, FieldNamingConventions, ForLoopShouldBeWhileLoop, ForLoopsMustUseBraces, FormalParameterNamingConventions, GenericsNaming, IdenticalCatchBranches, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, LinguisticNaming, LocalHomeNamingConvention, LocalInterfaceSessionNamingConvention, LocalVariableCouldBeFinal, LocalVariableNamingConventions, LongVariable, MDBAndSessionBeanNamingConvention, MethodArgumentCouldBeFinal, MethodNamingConventions, MIsLeadingVariableName, NoPackage, OnlyOneReturn, PackageCase, PrematureDeclaration, RemoteInterfaceNamingConvention, RemoteSessionInterfaceNamingConvention, ShortClassName, ShortMethodName, ShortVariable, SuspiciousConstantFieldName, TooManyStaticImports, UnnecessaryAnnotationValueElement, UnnecessaryConstructor, UnnecessaryFullyQualifiedName, UnnecessaryLocalBeforeReturn, UnnecessaryModifier, UnnecessaryReturn, UselessParentheses, UselessQualifiedThis, VariableNamingConventions, WhileLoopsMustUseBraces
language: Java
---
## AbstractNaming
@@ -405,11 +405,11 @@ public class Éléphant {}
|Name|Default Value|Description|Multivalued|
|----|-------------|-----------|-----------|
-|classPattern|[A-Z][a-zA-Z0-9]+|Regex which applies to concrete class names|no|
-|abstractClassPattern|[A-Z][a-zA-Z0-9]+|Regex which applies to abstract class names|no|
-|interfacePattern|[A-Z][a-zA-Z0-9]+|Regex which applies to interface names|no|
-|enumPattern|[A-Z][a-zA-Z0-9]+|Regex which applies to enum names|no|
-|annotationPattern|[A-Z][a-zA-Z0-9]+|Regex which applies to annotation names|no|
+|classPattern|[A-Z][a-zA-Z0-9]*|Regex which applies to concrete class names|no|
+|abstractClassPattern|[A-Z][a-zA-Z0-9]*|Regex which applies to abstract class names|no|
+|interfacePattern|[A-Z][a-zA-Z0-9]*|Regex which applies to interface names|no|
+|enumPattern|[A-Z][a-zA-Z0-9]*|Regex which applies to enum names|no|
+|annotationPattern|[A-Z][a-zA-Z0-9]*|Regex which applies to annotation names|no|
|utilityClassPattern|[A-Z][a-zA-Z0-9]+(Utils?\|Helper)|Regex which applies to utility class names|no|
**Use this rule by referencing it:**
@@ -756,6 +756,58 @@ public class HelloWorldBean {
```
+## FieldNamingConventions
+
+**Since:** PMD 6.7.0
+
+**Priority:** High (1)
+
+Configurable naming conventions for field declarations. This rule reports variable declarations
+which do not match the regex that applies to their specific kind ---e.g. constants (static final),
+enum constant, final field. Each regex can be configured through properties.
+
+By default this rule uses the standard Java naming convention (Camel case), and uses the ALL_UPPER
+convention for constants and enum constants.
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codestyle.FieldNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java)
+
+**Example(s):**
+
+``` java
+class Foo {
+ int myField = 1; // This is in camel case, so it's ok
+ int my_Field = 1; // This contains an underscore, it's not ok by default
+ // but you may allow it, or even require the "my_" prefix
+
+ final int FinalField = 1; // you may configure a different convention for final fields,
+ // e.g. here PascalCase: [A-Z][a-zA-Z0-9]*
+
+ interface Interface {
+ double PI = 3.14; // interface "fields" use the constantPattern property
+ }
+
+ enum AnEnum {
+ ORG, NET, COM; // These use a separate property but are set to ALL_UPPER by default
+ }
+ }
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|publicConstantPattern|[A-Z][A-Z_0-9]*|Regex which applies to public constant names|no|
+|constantPattern|[A-Z][A-Z_0-9]*|Regex which applies to non-public static final field names|no|
+|enumConstantPattern|[A-Z][A-Z_0-9]*|Regex which applies to enum constant names|no|
+|finalFieldPattern|[a-z][a-zA-Z0-9]*|Regex which applies to final field names|no|
+|staticFieldPattern|[a-z][a-zA-Z0-9]*|Regex which applies to static field names|no|
+|defaultFieldPattern|[a-z][a-zA-Z0-9]*|Regex which applies to field names|no|
+
+**Use this rule by referencing it:**
+``` xml
+
+```
+
## ForLoopShouldBeWhileLoop
**Since:** PMD 1.02
@@ -860,10 +912,10 @@ class Foo {
|Name|Default Value|Description|Multivalued|
|----|-------------|-----------|-----------|
-|methodParameterPattern|[a-z][a-zA-Z0-9]+|Regex which applies to formal parameter names|no|
-|finalMethodParameterPattern|[a-z][a-zA-Z0-9]+|Regex which applies to final formal parameter names|no|
-|lambdaParameterPattern|[a-z][a-zA-Z0-9]+|Regex which applies to inferred-type lambda parameter names|no|
-|explicitLambdaParameterPattern|[a-z][a-zA-Z0-9]+|Regex which applies to explicitly-typed lambda parameter names|no|
+|methodParameterPattern|[a-z][a-zA-Z0-9]*|Regex which applies to formal parameter names|no|
+|finalMethodParameterPattern|[a-z][a-zA-Z0-9]*|Regex which applies to final formal parameter names|no|
+|lambdaParameterPattern|[a-z][a-zA-Z0-9]*|Regex which applies to inferred-type lambda parameter names|no|
+|explicitLambdaParameterPattern|[a-z][a-zA-Z0-9]*|Regex which applies to explicitly-typed lambda parameter names|no|
**Use this rule by referencing it:**
``` xml
@@ -1020,6 +1072,87 @@ if (foo) { // preferred approach
```
+## LinguisticNaming
+
+**Since:** PMD 6.7.0
+
+**Priority:** Medium (3)
+
+This rule finds Linguistic Naming Antipatterns. It checks for fields, that are named, as if they should
+be boolean but have a different type. It also checks for methods, that according to their name, should
+return a boolean, but don't. Further, it checks, that getters return something and setters won't.
+Finally, it checks that methods, that start with "to" - so called transform methods - actually return
+something, since according to their name, they should convert or transform one object into another.
+There is additionally an option, to check for methods that contain "To" in their name - which are
+also transform methods. However, this is disabled by default, since this detection is prone to
+false positives.
+
+For more information, see [Linguistic Antipatterns - What They Are and How
+Developers Perceive Them](https://doi.org/10.1007/s10664-014-9350-8).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codestyle.LinguisticNamingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LinguisticNamingRule.java)
+
+**Example(s):**
+
+``` java
+public class LinguisticNaming {
+ int isValid; // the field name indicates a boolean, but it is an int.
+ boolean isTrue; // correct type of the field
+
+ void myMethod() {
+ int hasMoneyLocal; // the local variable name indicates a boolean, but it is an int.
+ boolean hasSalaryLocal; // correct naming and type
+ }
+
+ // the name of the method indicates, it is a boolean, but the method returns an int.
+ int isValid() {
+ return 1;
+ }
+ // correct naming and return type
+ boolean isSmall() {
+ return true;
+ }
+
+ // the name indicates, this is a setter, but it returns something
+ int setName() {
+ return 1;
+ }
+
+ // the name indicates, this is a getter, but it doesn't return anything
+ void getName() {
+ // nothing to return?
+ }
+
+ // the name indicates, it transforms an object and should return the result
+ void toDataType() {
+ // nothing to return?
+ }
+ // the name indicates, it transforms an object and should return the result
+ void grapeToWine() {
+ // nothing to return?
+ }
+}
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|booleanFieldPrefixes|is \| has \| can \| have \| will \| should|the prefixes of fields and variables that indicate boolean|yes. Delimiter is '\|'.|
+|checkVariables|true|Check local variable names and types for inconsistent naming|no|
+|checkFields|true|Check field names and types for inconsistent naming|no|
+|booleanMethodPrefixes|is \| has \| can \| have \| will \| should|the prefixes of methods that return boolean|yes. Delimiter is '\|'.|
+|checkPrefixedTransformMethods|true|Check return type of methods whose names start with 'to'|no|
+|checkTransformMethods|false|Check return type of methods which contain 'To' in their name|no|
+|checkSetters|true|Check return type of setters|no|
+|checkGetters|true|Check return type of getters|no|
+|checkBooleanMethod|true|Check method names and types for inconsistent naming|no|
+
+**Use this rule by referencing it:**
+``` xml
+
+```
+
## LocalHomeNamingConvention
**Since:** PMD 4.0
@@ -1157,9 +1290,9 @@ class Foo {
|Name|Default Value|Description|Multivalued|
|----|-------------|-----------|-----------|
-|localVarPattern|[a-z][a-zA-Z0-9]+|Regex which applies to non-final local variable names|no|
-|finalVarPattern|[a-z][a-zA-Z0-9]+|Regex which applies to final local variable names|no|
-|catchParameterPattern|[a-z][a-zA-Z0-9]+|Regex which applies to exception block parameter names|no|
+|localVarPattern|[a-z][a-zA-Z0-9]*|Regex which applies to non-final local variable names|no|
+|finalVarPattern|[a-z][a-zA-Z0-9]*|Regex which applies to final local variable names|no|
+|catchParameterPattern|[a-z][a-zA-Z0-9]*|Regex which applies to exception block parameter names|no|
**Use this rule by referencing it:**
``` xml
@@ -1298,11 +1431,11 @@ public class Foo {
|Name|Default Value|Description|Multivalued|
|----|-------------|-----------|-----------|
|checkNativeMethods|true|Deprecated Check native methods|no|
-|methodPattern|[a-z][a-zA-Z0-9]+|Regex which applies to instance method names|no|
-|staticPattern|[a-z][a-zA-Z0-9]+|Regex which applies to static method names|no|
-|nativePattern|[a-z][a-zA-Z0-9]+|Regex which applies to native method names|no|
+|methodPattern|[a-z][a-zA-Z0-9]*|Regex which applies to instance method names|no|
+|staticPattern|[a-z][a-zA-Z0-9]*|Regex which applies to static method names|no|
+|nativePattern|[a-z][a-zA-Z0-9]*|Regex which applies to native method names|no|
|junit3TestPattern|test[A-Z0-9][a-zA-Z0-9]*|Regex which applies to JUnit 3 test method names|no|
-|junit4TestPattern|[a-z][a-zA-Z0-9]+|Regex which applies to JUnit 4 test method names|no|
+|junit4TestPattern|[a-z][a-zA-Z0-9]*|Regex which applies to JUnit 4 test method names|no|
**Use this rule by referencing it:**
``` xml
@@ -1936,6 +2069,7 @@ Useless parentheses should be removed.
[not(./CastExpression)]
[not(./ConditionalExpression)]
[not(./AdditiveExpression)]
+ [not(./AssignmentOperator)]
|
//Expression[not(parent::PrimaryPrefix)]/PrimaryExpression[count(*)=1]
/PrimaryPrefix/Expression
diff --git a/docs/pages/pmd/rules/java/design.md b/docs/pages/pmd/rules/java/design.md
index c9d7d9cae9..f0eb4ed843 100644
--- a/docs/pages/pmd/rules/java/design.md
+++ b/docs/pages/pmd/rules/java/design.md
@@ -23,7 +23,7 @@ protected constructor in order to prevent instantiation than make the class misl
//ClassOrInterfaceDeclaration
[@Abstract = 'true']
[count(//MethodDeclaration) + count(//ConstructorDeclaration) = 0]
- [not(../Annotation/MarkerAnnotation/Name[typeIs('com.google.auto.value.AutoValue')])]
+ [not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('com.google.auto.value.AutoValue')])]
```
**Example(s):**
@@ -259,13 +259,13 @@ Exception, or Error, use a subclassed exception or error instead.
``` xpath
//ThrowStatement//AllocationExpression
/ClassOrInterfaceType[
- typeIsExactly('java.lang.Throwable')
+ pmd-java:typeIsExactly('java.lang.Throwable')
or
- typeIsExactly('java.lang.Exception')
+ pmd-java:typeIsExactly('java.lang.Exception')
or
- typeIsExactly('java.lang.Error')
+ pmd-java:typeIsExactly('java.lang.Error')
or
- typeIsExactly('java.lang.RuntimeException')
+ pmd-java:typeIsExactly('java.lang.RuntimeException')
]
```
@@ -518,7 +518,7 @@ Errors are system exceptions. Do not extend them.
**This rule is defined by the following XPath expression:**
``` xpath
//ClassOrInterfaceDeclaration/ExtendsList/ClassOrInterfaceType
- [typeIs('java.lang.Error')]
+ [pmd-java:typeIs('java.lang.Error')]
```
**Example(s):**
@@ -1406,7 +1406,14 @@ PrimaryExpression/PrimarySuffix/Arguments/ArgumentList
/Expression/UnaryExpressionNotPlusMinus[@Image='!']
/PrimaryExpression/PrimaryPrefix
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/java/documentation.md b/docs/pages/pmd/rules/java/documentation.md
index e80a77f7d9..98d9988dec 100644
--- a/docs/pages/pmd/rules/java/documentation.md
+++ b/docs/pages/pmd/rules/java/documentation.md
@@ -132,7 +132,7 @@ and unintentional empty constructors.
``` xpath
//ConstructorDeclaration[@Private='false']
[count(BlockStatement) = 0 and ($ignoreExplicitConstructorInvocation = 'true' or not(ExplicitConstructorInvocation)) and @containsComment = 'false']
- [not(../Annotation/MarkerAnnotation/Name[typeIs('javax.inject.Inject')])]
+ [not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('javax.inject.Inject')])]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/java/errorprone.md b/docs/pages/pmd/rules/java/errorprone.md
index bfa1e4a5f9..225226cc72 100644
--- a/docs/pages/pmd/rules/java/errorprone.md
+++ b/docs/pages/pmd/rules/java/errorprone.md
@@ -796,9 +796,9 @@ Super should be called at the start of the method
/Block[not(
(BlockStatement[1]/Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image]))]
[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[
- typeIs('android.app.Activity') or
- typeIs('android.app.Application') or
- typeIs('android.app.Service')
+ pmd-java:typeIs('android.app.Activity') or
+ pmd-java:typeIs('android.app.Application') or
+ pmd-java:typeIs('android.app.Service')
]]]
```
@@ -839,9 +839,9 @@ Super should be called at the end of the method
/Block/BlockStatement[last()]
[not(Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image])]
[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[
- typeIs('android.app.Activity') or
- typeIs('android.app.Application') or
- typeIs('android.app.Service')
+ pmd-java:typeIs('android.app.Activity') or
+ pmd-java:typeIs('android.app.Application') or
+ pmd-java:typeIs('android.app.Service')
]]]
```
@@ -2176,7 +2176,14 @@ Some JUnit framework methods are easy to misspell.
or (not(@Image = 'tearDown')
and translate(@Image, 'TEARdOWN', 'tearDown') = 'tearDown')]
[FormalParameters[count(*) = 0]]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
@@ -2208,7 +2215,14 @@ The suite() method in a JUnit test needs to be both public and static.
//MethodDeclaration[not(@Static='true') or not(@Public='true')]
[MethodDeclarator/@Image='suite']
[MethodDeclarator/FormalParameters/@ParameterCount=0]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
@@ -2399,16 +2413,10 @@ Serializable classes should provide a serialVersionUID field.
**This rule is defined by the following XPath expression:**
``` xpath
//ClassOrInterfaceDeclaration
- [
- count(ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration
- /FieldDeclaration/VariableDeclarator/VariableDeclaratorId[@Image='serialVersionUID']) = 0
-and
- count(ImplementsList
- [ClassOrInterfaceType/@Image='Serializable'
- or ClassOrInterfaceType/@Image='java.io.Serializable']) =1
-and
- @Abstract = 'false'
-]
+ [@Abstract = 'false']
+ [count(ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration
+ /FieldDeclaration/VariableDeclarator/VariableDeclaratorId[@Image='serialVersionUID']) = 0]
+ [(ImplementsList | ExtendsList)/ClassOrInterfaceType[pmd-java:typeIs('java.io.Serializable')]]
```
**Example(s):**
@@ -3196,7 +3204,14 @@ or
UnaryExpressionNotPlusMinus[@Image='!']
/PrimaryExpression/PrimaryPrefix[Literal/BooleanLiteral or Name[count(../../*)=1]]]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/java/multithreading.md b/docs/pages/pmd/rules/java/multithreading.md
index 9b6107e238..6c673f40a8 100644
--- a/docs/pages/pmd/rules/java/multithreading.md
+++ b/docs/pages/pmd/rules/java/multithreading.md
@@ -166,8 +166,8 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co
[
./Name[ends-with(@Image, '.run') or @Image = 'run']
and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image
- [../../../Type/ReferenceType/ClassOrInterfaceType[typeIs('java.lang.Thread')]]
- or (./AllocationExpression/ClassOrInterfaceType[typeIs('java.lang.Thread')]
+ [../../../Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]]
+ or (./AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]
and ../PrimarySuffix[@Image = 'run'])
]
]
diff --git a/docs/pages/pmd/rules/java/performance.md b/docs/pages/pmd/rules/java/performance.md
index 38315f4ab8..d86c5214a5 100644
--- a/docs/pages/pmd/rules/java/performance.md
+++ b/docs/pages/pmd/rules/java/performance.md
@@ -132,10 +132,10 @@ The FileReader and FileWriter constructors instantiate FileInputStream and FileO
**This rule is defined by the following XPath expression:**
``` xpath
//PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[
- typeIs('java.io.FileInputStream')
- or typeIs('java.io.FileOutputStream')
- or typeIs('java.io.FileReader')
- or typeIs('java.io.FileWriter')
+ pmd-java:typeIs('java.io.FileInputStream')
+ or pmd-java:typeIs('java.io.FileOutputStream')
+ or pmd-java:typeIs('java.io.FileReader')
+ or pmd-java:typeIs('java.io.FileWriter')
]
```
@@ -208,10 +208,10 @@ adverse impacts on performance.
``` xpath
//FieldDeclaration/Type/PrimitiveType[@Image = 'short']
|
-//ClassOrInterfaceBodyDeclaration[not(Annotation/MarkerAnnotation/Name[typeIs('java.lang.Override')])]
+//ClassOrInterfaceBodyDeclaration[not(Annotation/MarkerAnnotation/Name[pmd-java:typeIs('java.lang.Override')])]
/MethodDeclaration/ResultType/Type/PrimitiveType[@Image = 'short']
|
-//ClassOrInterfaceBodyDeclaration[not(Annotation/MarkerAnnotation/Name[typeIs('java.lang.Override')])]
+//ClassOrInterfaceBodyDeclaration[not(Annotation/MarkerAnnotation/Name[pmd-java:typeIs('java.lang.Override')])]
/MethodDeclaration/MethodDeclarator/FormalParameters/FormalParameter/Type/PrimitiveType[@Image = 'short']
|
//LocalVariableDeclaration/Type/PrimitiveType[@Image = 'short']
@@ -300,7 +300,7 @@ Note that new Byte() is deprecated since JDK 9 for that reason.
``` xpath
//AllocationExpression
[not (ArrayDimsAndInits)
-and ClassOrInterfaceType[typeIs('java.lang.Byte')]]
+and ClassOrInterfaceType[pmd-java:typeIs('java.lang.Byte')]]
```
**Example(s):**
@@ -498,7 +498,7 @@ Note that new Integer() is deprecated since JDK 9 for that reason.
``` xpath
//AllocationExpression
[not (ArrayDimsAndInits)
- and ClassOrInterfaceType[typeIs('java.lang.Integer')]]
+ and ClassOrInterfaceType[pmd-java:typeIs('java.lang.Integer')]]
```
**Example(s):**
@@ -528,7 +528,7 @@ Note that new Long() is deprecated since JDK 9 for that reason.
``` xpath
//AllocationExpression
[not (ArrayDimsAndInits)
-and ClassOrInterfaceType[typeIs('java.lang.Long')]]
+and ClassOrInterfaceType[pmd-java:typeIs('java.lang.Long')]]
```
**Example(s):**
@@ -647,7 +647,7 @@ Note that new Short() is deprecated since JDK 9 for that reason.
``` xpath
//AllocationExpression
[not (ArrayDimsAndInits)
-and ClassOrInterfaceType[typeIs('java.lang.Short')]]
+and ClassOrInterfaceType[pmd-java:typeIs('java.lang.Short')]]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/plsql.md b/docs/pages/pmd/rules/plsql.md
index 5697383c35..4761c81638 100644
--- a/docs/pages/pmd/rules/plsql.md
+++ b/docs/pages/pmd/rules/plsql.md
@@ -16,6 +16,7 @@ folder: pmd/rules
{% include callout.html content="Rules which enforce a specific coding style." %}
+* [ForLoopNaming](pmd_rules_plsql_codestyle.html#forloopnaming): In case you have loops please name the loop variables more meaningful.
* [MisplacedPragma](pmd_rules_plsql_codestyle.html#misplacedpragma): Oracle states that the PRAQMA AUTONOMOUS_TRANSACTION must be in the declaration block,but the cod...
## Design
diff --git a/docs/pages/pmd/rules/plsql/codestyle.md b/docs/pages/pmd/rules/plsql/codestyle.md
index 93abe1ef55..17cabf92bf 100644
--- a/docs/pages/pmd/rules/plsql/codestyle.md
+++ b/docs/pages/pmd/rules/plsql/codestyle.md
@@ -5,9 +5,73 @@ permalink: pmd_rules_plsql_codestyle.html
folder: pmd/rules/plsql
sidebaractiveurl: /pmd_rules_plsql.html
editmepath: ../pmd-plsql/src/main/resources/category/plsql/codestyle.xml
-keywords: Code Style, MisplacedPragma
+keywords: Code Style, MisplacedPragma, ForLoopNaming
language: PLSQL
---
+## ForLoopNaming
+
+**Since:** PMD 6.7.0
+
+**Priority:** Medium (3)
+
+In case you have loops please name the loop variables more meaningful.
+
+**This rule is defined by the following XPath expression:**
+``` xpath
+//CursorForLoopStatement[
+ $allowSimpleLoops = 'false' or
+ (Statement//CursorForLoopStatement or ancestor::CursorForLoopStatement)
+]
+/ForIndex[not(matches(@Image, $cursorPattern))]
+|
+//ForStatement[
+ $allowSimpleLoops = 'false' or
+ (Statement//ForStatement or ancestor::ForStatement)
+]
+/ForIndex[not(matches(@Image, $indexPattern))]
+```
+
+**Example(s):**
+
+``` sql
+-- good example
+BEGIN
+FOR company IN (SELECT * FROM companies) LOOP
+ FOR contact IN (SELECT * FROM contacts) LOOP
+ FOR party IN (SELECT * FROM parties) LOOP
+ NULL;
+ END LOOP;
+ END LOOP;
+END LOOP;
+END;
+/
+
+-- bad example
+BEGIN
+FOR c1 IN (SELECT * FROM companies) LOOP
+ FOR c2 IN (SELECT * FROM contacts) LOOP
+ FOR c3 IN (SELECT * FROM parties) LOOP
+ NULL;
+ END LOOP;
+ END LOOP;
+END LOOP;
+END;
+/
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|allowSimpleLoops|false|Ignore simple loops, that are not nested|no|
+|cursorPattern|[a-zA-Z_0-9]{5,}|The pattern used for the curosr loop variable|no|
+|indexPattern|[a-zA-Z_0-9]{5,}|The pattern used for the index loop variable|no|
+
+**Use this rule by referencing it:**
+``` xml
+
+```
+
## MisplacedPragma
**Since:** PMD 5.5.2
diff --git a/docs/pages/pmd/userdocs/making_rulesets.md b/docs/pages/pmd/userdocs/making_rulesets.md
index 54f176d24e..dc6a87b032 100644
--- a/docs/pages/pmd/userdocs/making_rulesets.md
+++ b/docs/pages/pmd/userdocs/making_rulesets.md
@@ -24,7 +24,7 @@ The first step is to create a new empty ruleset. You can use the following templ
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.net/ruleset_2_0_0.xsd">
My custom rules
diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index 23bf24e76d..a324c2ee28 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -4,11 +4,11 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
-## ????? - 6.6.0-SNAPSHOT
+## {{ site.pmd.date }} - {{ site.pmd.version | append_unless: is_release_version, "-SNAPSHOT" }}
-The PMD team is pleased to announce PMD 6.6.0.
+The PMD team is pleased to announce PMD {{ site.pmd.version }}.
-This is a minor release.
+This is a {{ site.pmd.release_type }} release.
### Table Of Contents
@@ -22,37 +22,58 @@ This is a minor release.
#### New Rules
-* The new Java rule [`LocalVariableNamingConventions`](pmd_rules_java_codestyle.html#localvariablenamingconventions) (`java-codestlye`)
- detects local variable names that don't comply to a given convention. It defaults to standrd Java convention of using camelCase,
- but can be configured. Special cases can be configured for final variables and catched exceptions' names.
+* The new Java rule {% rule java/codestyle/LinguisticNaming %} (`java-codestyle`)
+ detects cases, when a method name indicates it returns a boolean (such as `isSmall()`) but it doesn't.
+ Besides method names, the rule also checks field and variable names. It also checks, that getters return
+ something but setters won't. The rule has several properties with which it can be customized.
-* The new Java rule [`FormalParameterNamingConventions`](pmd_rules_java_codestyle.html#formalparameternamingconventions) (`java-codestlye`)
- detects formal parameter names that don't comply to a given convention. It defaults to standrd Java convention of using camelCase,
- but can be configured. Special cases can be configured for final parameters and lambda parameters (considering wether they are
- explicitly typed or not)
+* The new PL/SQL rule {% rule plsql/codestyle/ForLoopNaming %} (`plsql-codestyle`)
+ enforces a naming convention for "for loops". Both "cursor for loops" and "index for loops" are covered.
+ The rule can be customized via patterns. By default, short variable names are reported.
+
+* The new Java rule {% rule java/codestyle/FieldNamingConventions %} (`java-codestyle`)
+ detects field names that don't comply to a given convention. It defaults to standard Java convention of using camelCase,
+ but can be configured with ease for e.g. constants or static fields.
### Fixed Issues
-* doc
- * [#1215](https://github.com/pmd/pmd/issues/1215): \[doc] TOC links don't work?
+* core
+ * [#1191](https://github.com/pmd/pmd/issues/1191): \[core] Test Framework: Sort violations by line/column
+ * [#1283](https://github.com/pmd/pmd/issues/1283): \[core] Deprecate ReportTree
+ * [#1288](https://github.com/pmd/pmd/issues/1288): \[core] No supported build listeners found with Gradle
+ * [#1300](https://github.com/pmd/pmd/issues/1300): \[core] PMD stops processing file completely, if one rule in a rule chain fails
+* java-bestpractices
+ * [#940](https://github.com/pmd/pmd/issues/940): \[java] JUnit 4 false positives for JUnit 5 tests
+ * [#1267](https://github.com/pmd/pmd/pull/1267): \[java] MissingOverrideRule: Avoid NoClassDefFoundError with incomplete classpath
* java-codestyle
- * [#1211](https://github.com/pmd/pmd/issues/1211): \[java] CommentDefaultAccessModifier false positive with nested interfaces (regression from 6.4.0)
- * [#1216](https://github.com/pmd/pmd/issues/1216): \[java] UnnecessaryFullyQualifiedName false positive for the same name method
-* java-design
- * [#1217](https://github.com/pmd/pmd/issues/1217): \[java] CyclomaticComplexityRule counts ?-operator twice
+ * [#1255](https://github.com/pmd/pmd/issues/1255): \[java] UnnecessaryFullyQualifiedName false positive: static method on shadowed implicitly imported class
+ * [#1258](https://github.com/pmd/pmd/issues/1285): \[java] False positive "UselessParentheses" for parentheses that contain assignment
+* java-errorprone
+ * [#1078](https://github.com/pmd/pmd/issues/1078): \[java] MissingSerialVersionUID rule does not seem to catch inherited classes
+* java-performance
+ * [#1298](https://github.com/pmd/pmd/issues/1298): \[java] RedundantFieldInitializer - NumberFormatException with Long
+* jsp
+ * [#1274](https://github.com/pmd/pmd/issues/1274): \[jsp] Support EL in tag attributes
+ * [#1276](https://github.com/pmd/pmd/issues/1276): \[jsp] add support for jspf and tag extensions
* plsql
- * [#980](https://github.com/pmd/pmd/issues/980): \[plsql] ParseException for CREATE TABLE
- * [#981](https://github.com/pmd/pmd/issues/981): \[plsql] ParseException when parsing VIEW
- * [#1047](https://github.com/pmd/pmd/issues/1047): \[plsql] ParseException when parsing EXECUTE IMMEDIATE
-* ui
- * [#1233](https://github.com/pmd/pmd/issues/1233): \[ui] XPath autocomplete arrows on first and last items
+ * [#681](https://github.com/pmd/pmd/issues/681): \[plsql] Parse error with Cursor For Loop
### API Changes
-* The `findDescendantsOfType` methods in `net.sourceforge.pmd.lang.ast.AbstractNode` no longer search for exact type matches, but will
- match subclasses too. That means, it's now possible to look for abstract node types such as `AbstractJavaTypeNode` and not only for it's concrete subtypes.
+* All classes in the package `net.sourceforge.pmd.lang.dfa.report` have been deprecated and will be removed
+ with PMD 7.0.0. This includes the class `net.sourceforge.pmd.lang.dfa.report.ReportTree`. The reason is,
+ that this class is very specific to Java and not suitable for other languages. It has only been used for
+ `YAHTMLRenderer`, which has been rewritten to work without these classes.
### External Contributions
-* [#1182](https://github.com/pmd/pmd/pull/1182): \[ui] XPath AutoComplete - [Akshat Bahety](https://github.com/akshatbahety)
-* [#1231](https://github.com/pmd/pmd/pull/1231): \[doc] Minor typo fix in installation.md - [Ashish Rana](https://github.com/ashishrana160796)
+* [#109](https://github.com/pmd/pmd/pull/109): \[java] Add two linguistics rules under naming - [Arda Aslan](https://github.com/ardaasln)
+* [#1254](https://github.com/pmd/pmd/pull/1254): \[ci] \[GSoC] Integrating the danger and pmdtester to travis CI - [BBG](https://github.com/djydewang)
+* [#1258](https://github.com/pmd/pmd/pull/1258): \[java] Use typeof in MissingSerialVersionUID - [krichter722](https://github.com/krichter722)
+* [#1264](https://github.com/pmd/pmd/pull/1264): \[cpp] Fix NullPointerException in CPPTokenizer:99 - [Rafael Cortês](https://github.com/mrfyda)
+* [#1277](https://github.com/pmd/pmd/pull/1277): \[jsp] #1276 add support for jspf and tag extensions - [Jordi Llach](https://github.com/jordillachmrf)
+* [#1275](https://github.com/pmd/pmd/pull/1275): \[jsp] Issue #1274 - Support EL in tag attributes - [Jordi Llach](https://github.com/jordillachmrf)
+* [#1278](https://github.com/pmd/pmd/pull/1278): \[ci] \[GSoC] Use pmdtester 1.0.0.pre.beta3 - [BBG](https://github.com/djydewang)
+* [#1289](https://github.com/pmd/pmd/pull/1289): \[java] UselessParentheses: Fix false positive with assignments - [cobratbq](https://github.com/cobratbq)
+* [#1290](https://github.com/pmd/pmd/pull/1290): \[docs] \[GSoC] Create the documentation about pmdtester - [BBG](https://github.com/djydewang)
+* [#1256](https://github.com/pmd/pmd/pull/1256): \[java] #940 Avoid JUnit 4 false positives for JUnit 5 tests - [Alex Shesterov](https://github.com/vovkss)
diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md
index 7d8ba85e46..3c3cd9e857 100644
--- a/docs/pages/release_notes_old.md
+++ b/docs/pages/release_notes_old.md
@@ -6,6 +6,83 @@ permalink: pmd_release_notes_old.html
Previous versions of PMD can be downloaded here:
http://sourceforge.net/projects/pmd/files/pmd/
+## 29-July-2018 - 6.6.0
+
+The PMD team is pleased to announce PMD 6.6.0.
+
+This is a minor release.
+
+### Table Of Contents
+
+* [New and noteworthy](#new-and-noteworthy)
+ * [Java 11 Support](#java-11-support)
+ * [New Rules](#new-rules)
+ * [Modified Rules](#modified-rules)
+* [Fixed Issues](#fixed-issues)
+* [API Changes](#api-changes)
+* [External Contributions](#external-contributions)
+
+### New and noteworthy
+
+#### Java 11 Support
+
+PMD is now able to parse the local-variable declaration syntax `var xxx`, that has been
+extended for lambda parameters with Java 11 via
+[JEP 323: Local-Variable Syntax for Lambda Parameters](http://openjdk.java.net/jeps/323).
+
+#### New Rules
+
+* The new Java rule [`LocalVariableNamingConventions`](pmd_rules_java_codestyle.html#localvariablenamingconventions)
+ (`java-codestyle`) detects local variable names that don't comply to a given convention. It defaults to standard
+ Java convention of using camelCase, but can be configured. Special cases can be configured for final variables
+ and caught exceptions' names.
+
+* The new Java rule [`FormalParameterNamingConventions`](pmd_rules_java_codestyle.html#formalparameternamingconventions)
+ (`java-codestyle`) detects formal parameter names that don't comply to a given convention. It defaults to
+ standard Java convention of using camelCase, but can be configured. Special cases can be configured for final
+ parameters and lambda parameters (considering whether they are explicitly typed or not).
+
+#### Modified Rules
+
+* The Java rules [`AccessorClassGeneration`](pmd_rules_java_bestpracices.html#accessorclassgeneration) and
+ [`AccessorMethodGeneration`](pmd_rules_java_bestpracices.html#accessormethodgeneration) (both in category
+ `java-bestpractices`) have been modified to be only valid up until Java 10. Java 11 adds support for
+ [JEP 181: Nest-Based Access Control](http://openjdk.java.net/jeps/181) which avoids the generation of
+ accessor classes / methods altogether.
+
+### Fixed Issues
+
+* core
+ * [#1178](https://github.com/pmd/pmd/issues/1178): \[core] "Unsupported build listener" in gradle build
+ * [#1225](https://github.com/pmd/pmd/issues/1225): \[core] Error in sed expression on line 82 of run.sh while detecting installed version of Java
+* doc
+ * [#1215](https://github.com/pmd/pmd/issues/1215): \[doc] TOC links don't work?
+* java-codestyle
+ * [#1211](https://github.com/pmd/pmd/issues/1211): \[java] CommentDefaultAccessModifier false positive with nested interfaces (regression from 6.4.0)
+ * [#1216](https://github.com/pmd/pmd/issues/1216): \[java] UnnecessaryFullyQualifiedName false positive for the same name method
+* java-design
+ * [#1217](https://github.com/pmd/pmd/issues/1217): \[java] CyclomaticComplexityRule counts ?-operator twice
+ * [#1226](https://github.com/pmd/pmd/issues/1226): \[java] NPath complexity false negative due to overflow
+* plsql
+ * [#980](https://github.com/pmd/pmd/issues/980): \[plsql] ParseException for CREATE TABLE
+ * [#981](https://github.com/pmd/pmd/issues/981): \[plsql] ParseException when parsing VIEW
+ * [#1047](https://github.com/pmd/pmd/issues/1047): \[plsql] ParseException when parsing EXECUTE IMMEDIATE
+* ui
+ * [#1233](https://github.com/pmd/pmd/issues/1233): \[ui] XPath autocomplete arrows on first and last items
+
+### API Changes
+
+* The `findDescendantsOfType` methods in `net.sourceforge.pmd.lang.ast.AbstractNode` no longer search for
+ exact type matches, but will match subclasses, too. That means, it's now possible to look for abstract node
+ types such as `AbstractJavaTypeNode` and not only for it's concrete subtypes.
+
+### External Contributions
+
+* [#1182](https://github.com/pmd/pmd/pull/1182): \[ui] XPath AutoComplete - [Akshat Bahety](https://github.com/akshatbahety)
+* [#1231](https://github.com/pmd/pmd/pull/1231): \[doc] Minor typo fix in installation.md - [Ashish Rana](https://github.com/ashishrana160796)
+* [#1250](https://github.com/pmd/pmd/pull/1250): \[ci] \[GSoC] Upload baseline of pmdtester automatically - [BBG](https://github.com/djydewang)
+
+
## 26-June-2018 - 6.5.0
The PMD team is pleased to announce PMD 6.5.0.
diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml
index 0298d90241..f53d989da7 100644
--- a/pmd-apex-jorje/pom.xml
+++ b/pmd-apex-jorje/pom.xml
@@ -8,7 +8,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml
index cbea57a45f..1fc38e5979 100644
--- a/pmd-apex/pom.xml
+++ b/pmd-apex/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml
index 5140684ed5..5147f55afa 100644
--- a/pmd-core/pom.xml
+++ b/pmd-core/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/AbstractReportNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/AbstractReportNode.java
index 0c22b3d49b..bcf8866405 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/AbstractReportNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/AbstractReportNode.java
@@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.dfa.report;
import java.util.ArrayList;
import java.util.List;
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public abstract class AbstractReportNode {
private List childNodes = new ArrayList<>();
private AbstractReportNode parentNode = null;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ClassNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ClassNode.java
index 747be9e52a..eb6ec4d588 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ClassNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ClassNode.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.dfa.report;
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public class ClassNode extends AbstractReportNode {
private String className;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/PackageNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/PackageNode.java
index 24d2e9ac5a..8ba7c1ba4c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/PackageNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/PackageNode.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.dfa.report;
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public class PackageNode extends AbstractReportNode {
private String packageName;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportHTMLPrintVisitor.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportHTMLPrintVisitor.java
index a7d664eb7e..4fe31734c2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportHTMLPrintVisitor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportHTMLPrintVisitor.java
@@ -25,6 +25,7 @@ import net.sourceforge.pmd.RuleViolation;
*
* @author raik
*/
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public class ReportHTMLPrintVisitor extends ReportVisitor {
@SuppressWarnings("PMD.AvoidStringBufferField")
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportTree.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportTree.java
index 18d58771d1..988a29e21d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportTree.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportTree.java
@@ -8,6 +8,14 @@ import java.util.Iterator;
import net.sourceforge.pmd.RuleViolation;
+/**
+ *
+ * @deprecated This class will be removed with PMD 7.0.0 without replacement.
+ * It is very specific for Java as it tries to recreate the package hierarchy
+ * of the analyzed classes and put the found violations in there.
+ * So it is of limited use for any other language.
+ */
+@Deprecated // will be removed with PMD 7.0.0 without replacement
public class ReportTree implements Iterable {
private PackageNode rootNode = new PackageNode("");
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportVisitor.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportVisitor.java
index 6c9adf413b..0794187d13 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportVisitor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ReportVisitor.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.dfa.report;
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public abstract class ReportVisitor {
public void visit(AbstractReportNode node) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ViolationNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ViolationNode.java
index 184d7d701c..4b0c7cb683 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ViolationNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/dfa/report/ViolationNode.java
@@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.dfa.report;
import net.sourceforge.pmd.RuleViolation;
+@Deprecated // will be removed with PMD 7.0.0 without replacement. See net.sourceforge.pmd.lang.dfa.report.ReportTree for details.
public class ViolationNode extends AbstractReportNode {
private RuleViolation ruleViolation;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRuleChainVisitor.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRuleChainVisitor.java
index f08c44635a..bb3c5fb3d8 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRuleChainVisitor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRuleChainVisitor.java
@@ -12,7 +12,10 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
@@ -27,6 +30,8 @@ import net.sourceforge.pmd.lang.ast.Node;
* expressed interest in.
*/
public abstract class AbstractRuleChainVisitor implements RuleChainVisitor {
+ private static final Logger LOG = Logger.getLogger(AbstractRuleChainVisitor.class.getName());
+
/**
* These are all the rules participating in the RuleChain, grouped by
* RuleSet.
@@ -93,6 +98,17 @@ public abstract class AbstractRuleChainVisitor implements RuleChainVisitor {
visits += ns.size();
}
rcto.close(visits);
+ } catch (RuntimeException e) {
+ if (ctx.isIgnoreExceptions()) {
+ ctx.getReport().addError(new Report.ProcessingError(e, ctx.getSourceCodeFilename()));
+
+ if (LOG.isLoggable(Level.WARNING)) {
+ LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ + ctx.getSourceCodeFilename() + ", continuing with next rule", e);
+ }
+ } else {
+ throw e;
+ }
}
}
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java
index 1fd00730fa..f716b536e4 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java
@@ -4,11 +4,20 @@
package net.sourceforge.pmd.renderers;
+import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.lang.dfa.report.ReportHTMLPrintVisitor;
-import net.sourceforge.pmd.lang.dfa.report.ReportTree;
+import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.properties.StringProperty;
/**
@@ -17,9 +26,10 @@ import net.sourceforge.pmd.properties.StringProperty;
public class YAHTMLRenderer extends AbstractAccumulatingRenderer {
public static final String NAME = "yahtml";
-
public static final StringProperty OUTPUT_DIR = new StringProperty("outputDir", "Output directory.", null, 0);
+ private SortedMap reportNodesByPackage = new TreeMap<>();
+
public YAHTMLRenderer() {
// YA = Yet Another?
super(NAME, "Yet Another HTML format.");
@@ -31,12 +41,211 @@ public class YAHTMLRenderer extends AbstractAccumulatingRenderer {
return "html";
}
+ private void addViolation(RuleViolation violation) {
+ String packageName = violation.getPackageName();
+
+ // report each part of the package name: e.g. net.sf.pmd.test will create nodes for
+ // net, net.sf, net.sf.pmd, and net.sf.pmd.test
+ int index = packageName.indexOf('.', 0);
+ while (index > -1) {
+ String currentPackage = packageName.substring(0, index);
+ ReportNode reportNode = reportNodesByPackage.get(currentPackage);
+ if (reportNode == null) {
+ reportNode = new ReportNode(currentPackage);
+ reportNodesByPackage.put(currentPackage, reportNode);
+ }
+ reportNode.incrementViolations();
+
+ int oldIndex = index;
+ index = packageName.indexOf('.', index + 1);
+ if (index == -1 && oldIndex != packageName.length()) {
+ index = packageName.length();
+ }
+ }
+
+ // add one node per class collecting the actual violations
+ String fqClassName = packageName + "." + violation.getClassName();
+ ReportNode classNode = reportNodesByPackage.get(fqClassName);
+ if (classNode == null) {
+ classNode = new ReportNode(packageName, violation.getClassName());
+ reportNodesByPackage.put(fqClassName, classNode);
+ }
+ classNode.addRuleViolation(violation);
+
+ // count the overall violations in the root node
+ ReportNode rootNode = reportNodesByPackage.get(ReportNode.ROOT_NODE_NAME);
+ if (rootNode == null) {
+ rootNode = new ReportNode("Aggregate");
+ reportNodesByPackage.put(ReportNode.ROOT_NODE_NAME, rootNode);
+ }
+ rootNode.incrementViolations();
+ }
+
@Override
public void end() throws IOException {
String outputDir = getProperty(OUTPUT_DIR);
- ReportTree tree = report.getViolationTree();
- tree.getRootNode().accept(new ReportHTMLPrintVisitor(outputDir == null ? ".." : outputDir));
+
+ Iterator violations = report.iterator();
+ while (violations.hasNext()) {
+ addViolation(violations.next());
+ }
+
+ renderIndex(outputDir);
+ renderClasses(outputDir);
+
writer.write("
The HTML files are located "
+ (outputDir == null ? "above the project directory" : "in '" + outputDir + '\'') + ".
+ */
public class ASTClassOrInterfaceBody extends AbstractJavaNode {
public ASTClassOrInterfaceBody(int id) {
super(id);
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
index 74851a3547..9f1ed15a26 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
@@ -13,17 +13,17 @@ import net.sourceforge.pmd.util.CollectionUtil;
/**
- * Represents class and interface declarations.
+ * Represents class and interface declarations. This is a {@linkplain Node#isFindBoundary() find boundary}
+ * for tree traversal methods.
*
*
+ */
public class ASTEnumConstant extends AbstractJavaNode implements JavaQualifiableNode {
private JavaTypeQualifiedName qualifiedName;
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java
index 877d327f52..15e396118d 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java
@@ -5,10 +5,36 @@
package net.sourceforge.pmd.lang.java.ast;
+import java.util.Iterator;
+
import net.sourceforge.pmd.lang.ast.SignedNode;
import net.sourceforge.pmd.lang.java.multifile.signature.JavaFieldSignature;
-public class ASTFieldDeclaration extends AbstractJavaAccessTypeNode implements Dimensionable, SignedNode {
+
+/**
+ * Represents a field declaration in the body of a type declaration.
+ *
+ *
This statement may define several variables, possibly of different types (see {@link ASTVariableDeclaratorId#getType()}).
+ * The nodes corresponding to the declared variables are accessible through {@link #iterator()}.
+ *
+ *
{@link AccessNode} methods take into account the syntactic context of the
+ * declaration, e.g. {@link #isPublic()} will always return true if the field is
+ * declared inside an interface, regardless of whether the {@code public} modifier
+ * was specified or not. If you want to know whether the modifier was explicitly
+ * stated, use e.g {@link #isSyntacticallyPublic()}.
+ *
+ *
+ */
+public class ASTFieldDeclaration extends AbstractJavaAccessTypeNode implements Dimensionable, SignedNode, Iterable {
private JavaFieldSignature signature;
@@ -152,4 +178,15 @@ public class ASTFieldDeclaration extends AbstractJavaAccessTypeNode implements D
return signature;
}
+
+
+ /**
+ * Returns an iterator over the ids of the fields
+ * declared in this statement.
+ */
+ @Override
+ public Iterator iterator() {
+ return ASTVariableDeclarator.iterateIds(this);
+ }
+
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java
index 681b4268bd..b2d5420c4c 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java
@@ -14,6 +14,7 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin
* production of {@link ASTMethodDeclarator} to represent a
* method's formal parameter. Also used in the {@link ASTCatchStatement}
* production to represent the declared exception variable.
+ * Also used in LambdaExpressions for the LambdaParameters.
*
* ( "final" | Annotation )* Type ( "|" Type )* [ "..." ] VariableDeclaratorId
*
@@ -63,6 +64,16 @@ public class ASTFormalParameter extends AbstractJavaAccessTypeNode implements Di
return getVariableDeclaratorId().isExplicitReceiverParameter();
}
+ /**
+ * If true, this formal parameter represents one without explit types.
+ * This can appear as part of a lambda expression with java11 using "var".
+ *
+ * @see ASTVariableDeclaratorId#isTypeInferred()
+ */
+ public boolean isTypeInferred() {
+ return getTypeNode() == null;
+ }
+
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
@@ -93,7 +104,9 @@ public class ASTFormalParameter extends AbstractJavaAccessTypeNode implements Di
*/
@Override
public boolean isArray() {
- return isVarargs() || getTypeNode().isArray() || getVariableDeclaratorId().isArray();
+ return isVarargs()
+ || getTypeNode() != null && getTypeNode().isArray()
+ || getVariableDeclaratorId().isArray();
}
@Override
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLiteral.java
index 16e0d4db5b..2a18732390 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLiteral.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLiteral.java
@@ -5,6 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
+import java.math.BigInteger;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -146,11 +147,14 @@ public class ASTLiteral extends AbstractJavaTypeNode {
}
public int getValueAsInt() {
- return (int) getValueAsLong(); // the downcast allows to parse 0x80000000+ numbers as negative instead of a NumberFormatException
+ // the downcast allows to parse 0x80000000+ numbers as negative instead of a NumberFormatException
+ return (int) getValueAsLong();
}
public long getValueAsLong() {
- return Long.parseLong(stripIntValue(), getIntBase());
+ // Using BigInteger to allow parsing 0x8000000000000000+ numbers as negative instead of a NumberFormatException
+ BigInteger bigInt = new BigInteger(stripIntValue(), getIntBase());
+ return bigInt.longValue();
}
public float getValueAsFloat() {
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java
index 58d741a26e..d0089bdb32 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java
@@ -18,11 +18,11 @@ import net.sourceforge.pmd.Rule;
*
This statement may define several variables, possibly of different types (see {@link ASTVariableDeclaratorId#getType()}).
* The nodes corresponding to the declared variables are accessible through {@link #iterator()}.
*
- *
+ *
*/
public class ASTLocalVariableDeclaration extends AbstractJavaAccessNode implements Dimensionable, CanSuppressWarnings, Iterable {
@@ -127,6 +127,6 @@ public class ASTLocalVariableDeclaration extends AbstractJavaAccessNode implemen
*/
@Override
public Iterator iterator() {
- return new NodeChildrenIterator<>(this, ASTVariableDeclaratorId.class);
+ return ASTVariableDeclarator.iterateIds(this);
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclarator.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclarator.java
index 60fdd00c24..7d28205df6 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclarator.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclarator.java
@@ -5,20 +5,96 @@
package net.sourceforge.pmd.lang.java.ast;
+import java.util.Iterator;
+
+import net.sourceforge.pmd.lang.ast.Node;
+
+
+/**
+ * Groups a variable ID and its initializer if it exists.
+ * May be found as a child of {@linkplain ASTFieldDeclaration field declarations} and
+ * {@linkplain ASTLocalVariableDeclaration local variable declarations}.
+ *
+ *
+ */
public class ASTVariableDeclarator extends AbstractJavaTypeNode {
public ASTVariableDeclarator(int id) {
super(id);
}
+
public ASTVariableDeclarator(JavaParser p, int id) {
super(p, id);
}
- /**
- * Accept the visitor. *
- */
+
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
+
+
+ /**
+ * Returns the name of the declared variable.
+ */
+ public String getName() {
+ // first child will be VariableDeclaratorId
+ return jjtGetChild(0).getImage();
+ }
+
+
+ /**
+ * Returns the id of the declared variable.
+ */
+ public ASTVariableDeclaratorId getVariableId() {
+ return (ASTVariableDeclaratorId) jjtGetChild(0);
+ }
+
+
+ /**
+ * Returns true if the declared variable is initialized.
+ * Otherwise, {@link #getInitializer()} returns null.
+ */
+ public boolean hasInitializer() {
+ return jjtGetNumChildren() > 1;
+ }
+
+
+ /**
+ * Returns the initializer, of the variable, or null if it doesn't exist.
+ */
+ public ASTVariableInitializer getInitializer() {
+ return hasInitializer() ? (ASTVariableInitializer) jjtGetChild(1) : null;
+ }
+
+
+ /* only for LocalVarDeclaration and FieldDeclaration */
+ static Iterator iterateIds(Node parent) {
+ // TODO this can be made clearer with iterator mapping (Java 8)
+ final Iterator declarators = new NodeChildrenIterator<>(parent, ASTVariableDeclarator.class);
+
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return declarators.hasNext();
+ }
+
+
+ @Override
+ public ASTVariableDeclaratorId next() {
+ return declarators.next().getVariableId();
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java
index 8322be0b26..9e4cb65496 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java
@@ -205,8 +205,7 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements Dim
* since the type node is absent.
*/
public boolean isTypeInferred() {
- // TODO think about supporting var for lambda parameters
- return isLambdaParamWithNoType() || isLocalVariableTypeInferred();
+ return isLambdaParamWithNoType() || isLocalVariableTypeInferred() || isLambdaTypeInferred();
}
@@ -222,6 +221,11 @@ public class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements Dim
return false;
}
+ private boolean isLambdaTypeInferred() {
+ return getNthParent(3) instanceof ASTLambdaExpression
+ && jjtGetParent().getFirstChildOfType(ASTType.class) == null;
+ }
+
/**
* Returns the first child of the node returned by {@link #getTypeNode()}.
* The image of that node can usually be interpreted as the image of the
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/NpathBaseVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/NpathBaseVisitor.java
index ba88b112f6..328d64b23b 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/NpathBaseVisitor.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/NpathBaseVisitor.java
@@ -39,7 +39,16 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
JavaNode n = (JavaNode) node.jjtGetChild(i);
- product *= (Integer) n.jjtAccept(this, data);
+ int childComplexity = (int) n.jjtAccept(this, data);
+
+ int newProduct = product * childComplexity;
+ if (newProduct >= product) {
+ product = newProduct;
+ } else {
+ // Overflow happened
+ product = Integer.MAX_VALUE;
+ break;
+ }
}
return product;
@@ -52,7 +61,16 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
JavaNode n = (JavaNode) node.jjtGetChild(i);
- sum += (Integer) n.jjtAccept(this, data);
+ int childComplexity = (int) n.jjtAccept(this, data);
+
+ int newSum = sum + childComplexity;
+ if (newSum >= sum) {
+ sum = newSum;
+ } else {
+ // Overflow happened
+ sum = Integer.MAX_VALUE;
+ break;
+ }
}
return sum;
@@ -81,7 +99,7 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
int complexity = node.hasElse() ? 0 : 1;
for (ASTStatement element : statementChildren) {
- complexity += (Integer) element.jjtAccept(this, data);
+ complexity += (int) element.jjtAccept(this, data);
}
int boolCompIf = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
@@ -95,7 +113,7 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
int boolCompWhile = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
- int nPathWhile = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
+ int nPathWhile = (int) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return boolCompWhile + nPathWhile + 1;
}
@@ -107,7 +125,7 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
int boolCompDo = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
- int nPathDo = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
+ int nPathDo = (int) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return boolCompDo + nPathDo + 1;
}
@@ -119,7 +137,7 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
int boolCompFor = CycloMetric.booleanExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
- int nPathFor = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
+ int nPathFor = (int) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return boolCompFor + nPathFor + 1;
}
@@ -162,7 +180,7 @@ public class NpathBaseVisitor extends JavaParserVisitorReducedAdapter {
npath += caseRange;
caseRange = 1;
} else {
- Integer complexity = (Integer) n.jjtAccept(this, data);
+ int complexity = (int) n.jjtAccept(this, data);
caseRange *= complexity;
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/JavaTypeQualifiedName.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/JavaTypeQualifiedName.java
index 0556ca050b..9df796b3ad 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/JavaTypeQualifiedName.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/JavaTypeQualifiedName.java
@@ -41,9 +41,9 @@ public final class JavaTypeQualifiedName extends JavaQualifiedName {
private Class> representedType;
private boolean typeLoaded;
- private ClassLoader classLoader;
+ private final ClassLoader classLoader;
- JavaTypeQualifiedName(ImmutableList packages, ImmutableList classes, ImmutableList localIndices) {
+ JavaTypeQualifiedName(ImmutableList packages, ImmutableList classes, ImmutableList localIndices, ClassLoader classLoader) {
Objects.requireNonNull(packages);
Objects.requireNonNull(classes);
Objects.requireNonNull(localIndices);
@@ -55,16 +55,8 @@ public final class JavaTypeQualifiedName extends JavaQualifiedName {
this.packages = packages;
this.classes = classes;
this.localIndices = localIndices;
- }
-
- /**
- * Sets the classloader to be used when resolving the actual type of this qualified name.
- * @see #getType()
- */
- JavaTypeQualifiedName withClassLoader(ClassLoader classLoader) {
- this.classLoader = classLoader;
- return this;
+ this.classLoader = classLoader; // classLoader may be null
}
@@ -177,17 +169,15 @@ public final class JavaTypeQualifiedName extends JavaQualifiedName {
*/
public Class> getType() {
synchronized (this) {
- if (typeLoaded) {
- return representedType;
- } else {
+ if (!typeLoaded) {
typeLoaded = true;
try {
representedType = loadType();
} catch (ClassNotFoundException e) {
representedType = null;
}
- return representedType;
}
+ return representedType;
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameFactory.java
index a074f9f15a..b57e5b2549 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameFactory.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameFactory.java
@@ -15,6 +15,8 @@ import net.sourceforge.pmd.lang.java.qname.ImmutableList.ListFactory;
/**
* Static factory methods for JavaQualifiedName.
+ * These are intended only for tests, even though some deprecated
+ * APIs use it. May be moved to an internal package?
*
* @author Clément Fournier
* @since 6.1.0
@@ -108,13 +110,10 @@ public final class QualifiedNameFactory {
name = '.' + name; // unnamed package, marked by a full stop. See ofString's format below
}
- JavaTypeQualifiedName qualifiedName = (JavaTypeQualifiedName) ofString(name);
- if (qualifiedName != null) {
- // Note: this assumes, that clazz has been loaded through the correct classloader,
- // specifically through the auxclasspath classloader.
- qualifiedName.withClassLoader(clazz.getClassLoader());
- }
- return qualifiedName;
+ // Note: this assumes, that clazz has been loaded through the correct classloader,
+ // specifically through the auxclasspath classloader.
+ // But this method should only be used in tests anyway
+ return (JavaTypeQualifiedName) ofStringWithClassLoader(name, clazz.getClassLoader());
}
@@ -146,6 +145,10 @@ public final class QualifiedNameFactory {
* @return A qualified name instance corresponding to the parsed string.
*/
public static JavaQualifiedName ofString(String name) {
+ return ofStringWithClassLoader(name, null);
+ }
+
+ private static JavaQualifiedName ofStringWithClassLoader(String name, ClassLoader classLoader) {
Matcher matcher = FORMAT.matcher(name);
if (!matcher.matches()) {
@@ -153,8 +156,8 @@ public final class QualifiedNameFactory {
}
ImmutableList packages = StringUtils.isBlank(matcher.group(PACKAGES_GROUP_INDEX))
- ? ListFactory.emptyList()
- : ListFactory.split(matcher.group(PACKAGES_GROUP_INDEX), "\\.");
+ ? ListFactory.emptyList()
+ : ListFactory.split(matcher.group(PACKAGES_GROUP_INDEX), "\\.");
String operation = matcher.group(OPERATION_GROUP_INDEX) == null ? null : matcher.group(OPERATION_GROUP_INDEX).substring(1);
boolean isLambda = operation != null && COMPILED_LAMBDA_PATTERN.matcher(operation).matches();
@@ -175,7 +178,7 @@ public final class QualifiedNameFactory {
}
}
- JavaTypeQualifiedName parent = new JavaTypeQualifiedName(packages, classes, localIndices);
+ JavaTypeQualifiedName parent = new JavaTypeQualifiedName(packages, classes, localIndices, classLoader);
return operation == null ? parent : new JavaOperationQualifiedName(parent, operation, isLambda);
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java
index babdd7a494..d7f7226a6a 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java
@@ -9,6 +9,7 @@ import static net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName.NOTLOCAL
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.mutable.MutableInt;
@@ -45,7 +46,7 @@ public class QualifiedNameResolver extends JavaParserVisitorReducedAdapter {
// Package names to package representation.
// Allows reusing the same list instance for the same packages.
// Package prefixes are also shared.
- private final Map> foundPackages = new HashMap<>(128);
+ private static final Map> FOUND_PACKAGES = new ConcurrentHashMap<>(128);
// The following stacks stack some counter of the
// visited classes. A new entry is pushed when
@@ -147,7 +148,7 @@ public class QualifiedNameResolver extends JavaParserVisitorReducedAdapter {
}
final String image = pack.getPackageNameImage();
- ImmutableList fullExisting = foundPackages.get(image);
+ ImmutableList fullExisting = FOUND_PACKAGES.get(image);
if (fullExisting != null) {
return fullExisting;
@@ -166,7 +167,7 @@ public class QualifiedNameResolver extends JavaParserVisitorReducedAdapter {
for (int i = longestPrefix.size(); i < allPacks.length; i++) {
longestPrefix = longestPrefix.prepend(allPacks[i]);
prefixImage.append(allPacks[i]);
- foundPackages.put(prefixImage.toString(), longestPrefix);
+ FOUND_PACKAGES.put(prefixImage.toString(), longestPrefix);
}
return longestPrefix;
@@ -184,7 +185,7 @@ public class QualifiedNameResolver extends JavaParserVisitorReducedAdapter {
* the total number of packages in the package name
*/
private ImmutableList getLongestPackagePrefix(String acc, int i) {
- ImmutableList prefix = foundPackages.get(acc);
+ ImmutableList prefix = FOUND_PACKAGES.get(acc);
if (prefix != null) {
return prefix;
}
@@ -356,8 +357,7 @@ public class QualifiedNameResolver extends JavaParserVisitorReducedAdapter {
/** Creates a new class qname from the current context (fields). */
private JavaTypeQualifiedName contextClassQName() {
- return new JavaTypeQualifiedName(packages, classNames, localIndices)
- .withClassLoader(classLoader);
+ return new JavaTypeQualifiedName(packages, classNames, localIndices, classLoader);
}
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 9400c5e1bd..3b97daa27d 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
@@ -109,25 +109,30 @@ public class MissingOverrideRule extends AbstractJavaRule {
return null;
}
- Set overridden = overriddenMethods(exploredType);
- Map>> result = new HashMap<>();
-
- for (Method m : exploredType.getDeclaredMethods()) {
- if (!result.containsKey(m.getName())) {
- result.put(m.getName(), new HashMap>());
+ try {
+ Set overridden = overriddenMethods(exploredType);
+ Map>> result = new HashMap<>();
+
+ for (Method m : exploredType.getDeclaredMethods()) {
+ if (!result.containsKey(m.getName())) {
+ result.put(m.getName(), new HashMap>());
+ }
+
+ Map> pCountToOverloads = result.get(m.getName());
+
+ int paramCount = m.getParameterTypes().length;
+ if (!pCountToOverloads.containsKey(paramCount)) {
+ pCountToOverloads.put(paramCount, new ArrayList());
+ }
+
+ pCountToOverloads.get(paramCount).add(m);
}
-
- Map> pCountToOverloads = result.get(m.getName());
-
- int paramCount = m.getParameterTypes().length;
- if (!pCountToOverloads.containsKey(paramCount)) {
- pCountToOverloads.put(paramCount, new ArrayList());
- }
-
- pCountToOverloads.get(paramCount).add(m);
+
+ return new MethodLookup(result, overridden);
+ } catch (final LinkageError e) {
+ // we may have an incomplete auxclasspath
+ return null;
}
-
- return new MethodLookup(result, overridden);
}
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 77d4dd17ad..e53f3a9c23 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
@@ -26,8 +26,8 @@ import net.sourceforge.pmd.util.StringUtil;
*/
abstract class AbstractNamingConventionRule extends AbstractJavaRule {
- static final String CAMEL_CASE = "[a-z][a-zA-Z0-9]+";
- static final String PASCAL_CASE = "[A-Z][a-zA-Z0-9]+";
+ static final String CAMEL_CASE = "[a-z][a-zA-Z0-9]*";
+ static final String PASCAL_CASE = "[A-Z][a-zA-Z0-9]*";
/** The argument is interpreted as the display name, and is converted to camel case to get the property name. */
RegexPBuilder defaultProp(String displayName) {
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
new file mode 100644
index 0000000000..f68dcb2556
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java
@@ -0,0 +1,99 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.rule.codestyle;
+
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant;
+import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.RegexProperty;
+
+
+/**
+ * Configurable naming conventions for field declarations.
+ *
+ * @author Clément Fournier
+ * @since 6.7.0
+ */
+public class FieldNamingConventionsRule extends AbstractNamingConventionRule {
+ private final RegexProperty publicConstantFieldRegex = defaultProp("public constant").defaultValue("[A-Z][A-Z_0-9]*").build();
+ private final RegexProperty constantFieldRegex = defaultProp("constant").desc("Regex which applies to non-public static final field names").defaultValue("[A-Z][A-Z_0-9]*").build();
+ private final RegexProperty enumConstantRegex = defaultProp("enum constant").defaultValue("[A-Z][A-Z_0-9]*").build();
+ private final RegexProperty finalFieldRegex = defaultProp("final field").build();
+ private final RegexProperty staticFieldRegex = defaultProp("static field").build();
+ private final RegexProperty defaultFieldRegex = defaultProp("defaultField", "field").build();
+
+
+ public FieldNamingConventionsRule() {
+ definePropertyDescriptor(publicConstantFieldRegex);
+ definePropertyDescriptor(constantFieldRegex);
+ definePropertyDescriptor(enumConstantRegex);
+ definePropertyDescriptor(finalFieldRegex);
+ definePropertyDescriptor(staticFieldRegex);
+ definePropertyDescriptor(defaultFieldRegex);
+
+ addRuleChainVisit(ASTFieldDeclaration.class);
+ addRuleChainVisit(ASTEnumConstant.class);
+ }
+
+
+ @Override
+ public Object visit(ASTFieldDeclaration node, Object data) {
+
+ for (ASTVariableDeclaratorId id : node) {
+ if (node.isFinal() && node.isStatic()) {
+ checkMatches(id, node.isPublic() ? publicConstantFieldRegex : constantFieldRegex, data);
+ } else if (node.isFinal()) {
+ checkMatches(id, finalFieldRegex, data);
+ } else if (node.isStatic()) {
+ checkMatches(id, staticFieldRegex, data);
+ } else {
+ checkMatches(id, defaultFieldRegex, data);
+ }
+ }
+ return data;
+ }
+
+
+ @Override
+ public Object visit(ASTEnumConstant node, Object data) {
+ // This inlines checkMatches because there's no variable declarator id
+
+ if (!getProperty(enumConstantRegex).matcher(node.getImage()).matches()) {
+ addViolation(data, node, new Object[]{
+ "enum constant",
+ node.getImage(),
+ getProperty(enumConstantRegex).toString(),
+ });
+ }
+
+ return data;
+ }
+
+
+ @Override
+ String defaultConvention() {
+ return CAMEL_CASE;
+ }
+
+
+ @Override
+ String kindDisplayName(ASTVariableDeclaratorId node, PropertyDescriptor descriptor) {
+ ASTFieldDeclaration field = (ASTFieldDeclaration) node.getNthParent(2);
+
+ if (field.isFinal() && field.isStatic()) {
+ return field.isPublic() ? "public constant" : "constant";
+ } else if (field.isFinal()) {
+ return "final field";
+ } else if (field.isStatic()) {
+ return "static field";
+ } else {
+ return "field";
+ }
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LinguisticNamingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LinguisticNamingRule.java
new file mode 100644
index 0000000000..7ab22598c0
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LinguisticNamingRule.java
@@ -0,0 +1,186 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.rule.codestyle;
+
+import java.util.List;
+
+import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+import net.sourceforge.pmd.lang.java.ast.ASTResultType;
+import net.sourceforge.pmd.lang.java.ast.ASTType;
+import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
+import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
+import net.sourceforge.pmd.properties.BooleanProperty;
+import net.sourceforge.pmd.properties.StringMultiProperty;
+
+public class LinguisticNamingRule extends AbstractJavaRule {
+ private static final BooleanProperty CHECK_BOOLEAN_METHODS = BooleanProperty.named("checkBooleanMethod")
+ .defaultValue(true).desc("Check method names and types for inconsistent naming").uiOrder(1.0f).build();
+ private static final BooleanProperty CHECK_GETTERS = BooleanProperty.named("checkGetters").defaultValue(true)
+ .desc("Check return type of getters").uiOrder(2.0f).build();
+ private static final BooleanProperty CHECK_SETTERS = BooleanProperty.named("checkSetters").defaultValue(true)
+ .desc("Check return type of setters").uiOrder(3.0f).build();
+ private static final BooleanProperty CHECK_PREFIXED_TRANSFORM_METHODS = BooleanProperty
+ .named("checkPrefixedTransformMethods")
+ .defaultValue(true).desc("Check return type of methods whose names start with 'to'").uiOrder(4.0f).build();
+ private static final BooleanProperty CHECK_TRANSFORM_METHODS = BooleanProperty.named("checkTransformMethods")
+ .defaultValue(false).desc("Check return type of methods which contain 'To' in their name").uiOrder(4.0f).build();
+ private static final StringMultiProperty BOOLEAN_METHOD_PREFIXES_PROPERTY = StringMultiProperty
+ .named("booleanMethodPrefixes").defaultValues("is", "has", "can", "have", "will", "should")
+ .desc("the prefixes of methods that return boolean").uiOrder(5.0f).build();
+
+ private static final BooleanProperty CHECK_FIELDS = BooleanProperty.named("checkFields").defaultValue(true)
+ .desc("Check field names and types for inconsistent naming").uiOrder(6.0f).build();
+ private static final BooleanProperty CHECK_VARIABLES = BooleanProperty.named("checkVariables").defaultValue(true)
+ .desc("Check local variable names and types for inconsistent naming").uiOrder(7.0f).build();
+ private static final StringMultiProperty BOOLEAN_FIELD_PREFIXES_PROPERTY = StringMultiProperty
+ .named("booleanFieldPrefixes").defaultValues("is", "has", "can", "have", "will", "should")
+ .desc("the prefixes of fields and variables that indicate boolean").uiOrder(8.0f).build();
+
+ public LinguisticNamingRule() {
+ definePropertyDescriptor(CHECK_BOOLEAN_METHODS);
+ definePropertyDescriptor(CHECK_GETTERS);
+ definePropertyDescriptor(CHECK_SETTERS);
+ definePropertyDescriptor(CHECK_PREFIXED_TRANSFORM_METHODS);
+ definePropertyDescriptor(CHECK_TRANSFORM_METHODS);
+ definePropertyDescriptor(BOOLEAN_METHOD_PREFIXES_PROPERTY);
+ definePropertyDescriptor(CHECK_FIELDS);
+ definePropertyDescriptor(CHECK_VARIABLES);
+ definePropertyDescriptor(BOOLEAN_FIELD_PREFIXES_PROPERTY);
+ addRuleChainVisit(ASTMethodDeclaration.class);
+ addRuleChainVisit(ASTFieldDeclaration.class);
+ addRuleChainVisit(ASTLocalVariableDeclaration.class);
+ }
+
+ @Override
+ public Object visit(ASTMethodDeclaration node, Object data) {
+ String nameOfMethod = node.getMethodName();
+
+ if (getProperty(CHECK_BOOLEAN_METHODS)) {
+ checkBooleanMethods(node, data, nameOfMethod);
+ }
+
+ if (getProperty(CHECK_SETTERS)) {
+ checkSetters(node, data, nameOfMethod);
+ }
+
+ if (getProperty(CHECK_GETTERS)) {
+ checkGetters(node, data, nameOfMethod);
+ }
+
+ if (getProperty(CHECK_PREFIXED_TRANSFORM_METHODS)) {
+ checkPrefixedTransformMethods(node, data, nameOfMethod);
+ }
+
+ if (getProperty(CHECK_TRANSFORM_METHODS)) {
+ checkTransformMethods(node, data, nameOfMethod);
+ }
+
+ return data;
+ }
+
+ private void checkPrefixedTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
+ ASTResultType resultType = node.getResultType();
+ if (resultType.isVoid() && hasPrefix(nameOfMethod, "to")) {
+ // To as prefix
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The transform method ''{0}'' should not return void linguistically",
+ new Object[] { nameOfMethod });
+ }
+ }
+
+ private void checkTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
+ ASTResultType resultType = node.getResultType();
+ if (resultType.isVoid() && containsWord(nameOfMethod, "To")) {
+ // To in the middle somewhere
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The transform method ''{0}'' should not return void linguistically",
+ new Object[] { nameOfMethod });
+ }
+ }
+
+ private void checkGetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
+ ASTResultType resultType = node.getResultType();
+ if (hasPrefix(nameOfMethod, "get") && resultType.isVoid()) {
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The getter ''{0}'' should not return void linguistically",
+ new Object[] { nameOfMethod });
+ }
+ }
+
+ private void checkSetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
+ ASTResultType resultType = node.getResultType();
+ if (hasPrefix(nameOfMethod, "set") && !resultType.isVoid()) {
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The setter ''{0}'' should not return any type except void linguistically",
+ new Object[] { nameOfMethod });
+ }
+ }
+
+ private void checkBooleanMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
+ ASTResultType resultType = node.getResultType();
+ ASTType t = node.getResultType().getFirstChildOfType(ASTType.class);
+ if (!resultType.isVoid() && t != null) {
+ for (String prefix : getProperty(BOOLEAN_METHOD_PREFIXES_PROPERTY)) {
+ if (hasPrefix(nameOfMethod, prefix) && !"boolean".equalsIgnoreCase(t.getTypeImage())) {
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The method ''{0}'' indicates linguistically it returns a boolean, but it returns ''{1}''",
+ new Object[] { nameOfMethod, t.getTypeImage() });
+ }
+ }
+ }
+ }
+
+ private void checkField(String typeImage, ASTVariableDeclarator node, Object data) {
+ for (String prefix : getProperty(BOOLEAN_FIELD_PREFIXES_PROPERTY)) {
+ if (hasPrefix(node.getName(), prefix) && !"boolean".equalsIgnoreCase(typeImage)) {
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The field ''{0}'' indicates linguistically it is a boolean, but it is ''{1}''",
+ new Object[] { node.getName(), typeImage });
+ }
+ }
+ }
+
+ private void checkVariable(String typeImage, ASTVariableDeclarator node, Object data) {
+ for (String prefix : getProperty(BOOLEAN_FIELD_PREFIXES_PROPERTY)) {
+ if (hasPrefix(node.getName(), prefix) && !"boolean".equalsIgnoreCase(typeImage)) {
+ addViolationWithMessage(data, node, "Linguistics Antipattern - The variable ''{0}'' indicates linguistically it is a boolean, but it is ''{1}''",
+ new Object[] { node.getName(), typeImage });
+ }
+ }
+ }
+
+ @Override
+ public Object visit(ASTFieldDeclaration node, Object data) {
+ ASTType type = node.getFirstChildOfType(ASTType.class);
+ if (type != null && getProperty(CHECK_FIELDS)) {
+ List fields = node.findChildrenOfType(ASTVariableDeclarator.class);
+ for (ASTVariableDeclarator field : fields) {
+ checkField(type.getTypeImage(), field, data);
+ }
+ }
+ return data;
+ }
+
+ @Override
+ public Object visit(ASTLocalVariableDeclaration node, Object data) {
+ ASTType type = node.getFirstChildOfType(ASTType.class);
+ if (type != null && getProperty(CHECK_VARIABLES)) {
+ List variables = node.findChildrenOfType(ASTVariableDeclarator.class);
+ for (ASTVariableDeclarator variable : variables) {
+ checkVariable(type.getTypeImage(), variable, data);
+ }
+ }
+ return data;
+ }
+
+ private static boolean hasPrefix(String name, String prefix) {
+ return name.startsWith(prefix) && name.length() > prefix.length()
+ && Character.isUpperCase(name.charAt(prefix.length()));
+ }
+
+ private static boolean containsWord(String name, String word) {
+ int index = name.indexOf(word);
+ if (index >= 0 && name.length() > index + word.length()) {
+ return Character.isUpperCase(name.charAt(index + word.length()));
+ }
+ return false;
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java
index 37c2b06d9d..ad12b080e8 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryFullyQualifiedNameRule.java
@@ -17,7 +17,11 @@ import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
+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.AbstractJavaTypeNode;
+import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope;
@@ -79,6 +83,16 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
&& name.lastIndexOf('.') == decl.getImportedName().length();
}
+ private boolean couldBeMethodCall(JavaNode node) {
+ if (node.getNthParent(2) instanceof ASTPrimaryExpression && node.getNthParent(1) instanceof ASTPrimaryPrefix) {
+ int nextSibling = node.jjtGetParent().jjtGetChildIndex() + 1;
+ if (node.getNthParent(2).jjtGetNumChildren() > nextSibling) {
+ return node.getNthParent(2).jjtGetChild(nextSibling) instanceof ASTPrimarySuffix;
+ }
+ }
+ return false;
+ }
+
private void checkImports(AbstractJavaTypeNode node, Object data) {
String name = node.getImage();
List matches = new ArrayList<>();
@@ -127,7 +141,7 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
matches.add(importDeclaration);
}
} else {
- // Last 2 parts match?
+ // Last 2 parts match? Class + Method name
if (nameParts[nameParts.length - 1].equals(importParts[importParts.length - 1])
&& nameParts[nameParts.length - 2].equals(importParts[importParts.length - 2])) {
matches.add(importDeclaration);
@@ -137,6 +151,10 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
// last part matches?
if (nameParts[nameParts.length - 1].equals(importParts[importParts.length - 1])) {
matches.add(importDeclaration);
+ } else if (couldBeMethodCall(node)
+ && nameParts.length > 1 && nameParts[nameParts.length - 2].equals(importParts[importParts.length - 1])) {
+ // maybe the Name is part of a method call, then the second two last part needs to match
+ matches.add(importDeclaration);
}
}
}
@@ -147,7 +165,7 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
}
if (!matches.isEmpty()) {
- ASTImportDeclaration firstMatch = matches.get(0);
+ ASTImportDeclaration firstMatch = findFirstMatch(matches);
// Could this done to avoid a conflict?
if (!isAvoidingConflict(node, name, firstMatch)) {
@@ -159,13 +177,37 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
}
}
+ private ASTImportDeclaration findFirstMatch(List imports) {
+ // first search only static imports
+ ASTImportDeclaration result = null;
+ for (ASTImportDeclaration importDeclaration : imports) {
+ if (importDeclaration.isStatic()) {
+ result = importDeclaration;
+ break;
+ }
+ }
+
+ // then search all non-static, if needed
+ if (result == null) {
+ for (ASTImportDeclaration importDeclaration : imports) {
+ if (!importDeclaration.isStatic()) {
+ result = importDeclaration;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
private boolean isJavaLangImplicit(AbstractJavaTypeNode node) {
String name = node.getImage();
boolean isJavaLang = name != null && name.startsWith("java.lang.");
- if (isJavaLang && node.getType() != null) {
+ if (isJavaLang && node.getType() != null && node.getType().getPackage() != null) {
// valid would be ProcessBuilder.Redirect.PIPE but not java.lang.ProcessBuilder.Redirect.PIPE
- String packageName = node.getType().getPackage().getName();
+ String packageName = node.getType().getPackage() // package might be null, if type is an array type...
+ .getName();
return "java.lang".equals(packageName);
} else if (isJavaLang) {
// only java.lang.* is implicitly imported, but not e.g. java.lang.reflection.*
@@ -231,15 +273,29 @@ public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
}
// There could be a conflict between an import of a class with the same name as the FQN
+ String importName = firstMatch.getImportedName();
+ String importUnqualified = importName.substring(importName.lastIndexOf('.') + 1);
if (!firstMatch.isImportOnDemand() && !firstMatch.isStatic()) {
- String importName = firstMatch.getImportedName();
- String importUnqualified = importName.substring(importName.lastIndexOf('.') + 1);
// the package is different, but the unqualified name is same
if (!firstMatch.getImportedName().equals(name) && importUnqualified.equals(unqualifiedName)) {
return true;
}
}
+ // There could be a conflict between an import of a class with the same name as the FQN, which
+ // could be a method call:
+ // import x.y.Thread;
+ // valid qualification (node): java.util.Thread.currentThread()
+ if (couldBeMethodCall(node)) {
+ String[] nameParts = name.split("\\.");
+ String fqnName = name.substring(0, name.lastIndexOf('.'));
+ // seems to be a static method call on a different FQN
+ if (!fqnName.equals(importName) && !firstMatch.isStatic() && !firstMatch.isImportOnDemand()
+ && nameParts.length > 1 && nameParts[nameParts.length - 2].equals(importUnqualified)) {
+ return true;
+ }
+ }
+
// Is it a conflict with a class in the same file?
final Set qualifiedTypes = node.getScope().getEnclosingScope(SourceFileScope.class)
.getQualifiedTypeNames().keySet();
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java
index f395787294..59891ef461 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java
@@ -9,6 +9,7 @@ import java.util.logging.Logger;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
+import net.sourceforge.pmd.lang.java.ast.MethodLikeNode;
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
@@ -68,7 +69,7 @@ public class NPathComplexityRule extends AbstractJavaMetricsRule {
@Override
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
- int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, node);
+ int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, (MethodLikeNode) node);
if (npath >= reportLevel) {
addViolation(data, node, new String[]{node instanceof ASTMethodDeclaration ? "method" : "constructor",
node.getQualifiedName().getOperation(), "" + npath, });
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/visitors/PMDASMVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/visitors/PMDASMVisitor.java
index fe29dd3d50..dce2ae802c 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/visitors/PMDASMVisitor.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/visitors/PMDASMVisitor.java
@@ -36,7 +36,7 @@ public class PMDASMVisitor extends ClassVisitor {
public List innerClasses;
public PMDASMVisitor(String outerName) {
- super(Opcodes.ASM6);
+ super(Opcodes.ASM7_EXPERIMENTAL);
this.outerName = outerName;
}
diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml
index 012f8798c0..4e7d4fa7eb 100644
--- a/pmd-java/src/main/resources/category/java/bestpractices.xml
+++ b/pmd-java/src/main/resources/category/java/bestpractices.xml
@@ -49,6 +49,7 @@ public abstract class Foo {
@@ -77,6 +78,7 @@ public class Outer {
@@ -408,6 +410,7 @@ otherwise skip the associate String creation and manipulation.
since="4.0"
message="JUnit 4 indicates test suites via annotations, not the suite method."
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#junit4suitesshouldusesuiteannotation">
In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicated
@@ -419,8 +422,8 @@ through the @RunWith(Suite.class) annotation.
@@ -445,21 +448,27 @@ public class GoodTest {
In JUnit 3, the tearDown method was used to clean up all data entities required in running tests.
-JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test
+JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test.
+JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after each test or after all tests in the class, respectively.
3
@@ -483,21 +492,27 @@ public class MyTest2 {
In JUnit 3, the setUp method was used to set up all data entities required in running tests.
-JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests
+JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests.
+JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods before each test or before all tests in the class, respectively.
3
@@ -521,13 +536,14 @@ public class MyTest2 {
In JUnit 3, the framework executed all methods which started with the word test as a unit test.
In JUnit 4, only methods annotated with the @Test annotation are executed.
+In JUnit 5, one of the following annotations should be used for tests: @Test, @RepeatedTest, @TestFactory, @TestTemplate or @ParameterizedTest.
3
@@ -539,7 +555,12 @@ In JUnit 4, only methods annotated with the @Test annotation are executed.
or ExtendsList/ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]]
/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration[MethodDeclaration[@Public=true()]/MethodDeclarator[starts-with(@Image, 'test')]]
- [not(Annotation//Name[pmd-java:typeIs('org.junit.Test')])]
+ [not(Annotation//Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ])]
]]>
@@ -589,13 +610,16 @@ public class Foo extends TestCase {
-JUnit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which
-it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios.
+Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which
+it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios.
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
@@ -603,7 +627,16 @@ Customize the maximum number of assertions used by this Rule to suit your needs.
$maximumAsserts]
+//MethodDeclarator[@Image[fn:matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ or pmd-java:typeIs('org.testng.annotations.Test')
+ ]]
+ [count(..//PrimaryPrefix/Name[@Image[fn:matches(.,'^assert')]]) > $maximumAsserts]
]]>
@@ -1196,6 +1229,7 @@ public class Something {
since="3.1"
message="Use assertEquals(x, y) instead of assertTrue(x.equals(y))"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#useassertequalsinsteadofasserttrue">
This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals.
@@ -1211,8 +1245,14 @@ This rule detects JUnit assertions in object equality. These assertions should b
PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name
[ends-with(@Image, '.equals')]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
@@ -1234,6 +1274,7 @@ public class FooTest extends TestCase {
since="3.5"
message="Use assertNull(x) instead of assertTrue(x==null), or assertNotNull(x) vs assertFalse(x==null)"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#useassertnullinsteadofasserttrue">
This rule detects JUnit assertions in object references equality. These assertions should be made by
@@ -1251,8 +1292,14 @@ more specific methods, like assertNull, assertNotNull.
Expression/EqualityExpression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral
]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
@@ -1276,6 +1323,7 @@ public class FooTest extends TestCase {
since="3.1"
message="Use assertSame(x, y) instead of assertTrue(x==y), or assertNotSame(x,y) vs assertFalse(x==y)"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#useassertsameinsteadofasserttrue">
This rule detects JUnit assertions in object references equality. These assertions should be made
@@ -1293,8 +1341,14 @@ by more specific methods, like assertSame, assertNotSame.
[PrimarySuffix/Arguments
/ArgumentList/Expression
/EqualityExpression[count(.//NullLiteral) = 0]]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml
index 01aae5f509..f80f81051b 100644
--- a/pmd-java/src/main/resources/category/java/codestyle.xml
+++ b/pmd-java/src/main/resources/category/java/codestyle.xml
@@ -687,6 +687,43 @@ public class HelloWorldBean {
+
+
+
+ Configurable naming conventions for field declarations. This rule reports variable declarations
+ which do not match the regex that applies to their specific kind ---e.g. constants (static final),
+ enum constant, final field. Each regex can be configured through properties.
+
+ By default this rule uses the standard Java naming convention (Camel case), and uses the ALL_UPPER
+ convention for constants and enum constants.
+
+ 1
+
+
+
+
+
+
+
+ This rule finds Linguistic Naming Antipatterns. It checks for fields, that are named, as if they should
+ be boolean but have a different type. It also checks for methods, that according to their name, should
+ return a boolean, but don't. Further, it checks, that getters return something and setters won't.
+ Finally, it checks that methods, that start with "to" - so called transform methods - actually return
+ something, since according to their name, they should convert or transform one object into another.
+ There is additionally an option, to check for methods that contain "To" in their name - which are
+ also transform methods. However, this is disabled by default, since this detection is prone to
+ false positives.
+
+ For more information, see [Linguistic Antipatterns - What They Are and How
+Developers Perceive Them](https://doi.org/10.1007/s10664-014-9350-8).
+
+ 3
+
+
+
+
+
@@ -267,13 +267,13 @@ Exception, or Error, use a subclassed exception or error instead.
@@ -503,7 +503,7 @@ Errors are system exceptions. Do not extend them.
@@ -1187,6 +1187,7 @@ public class Foo {
since="3.6"
message="assertTrue(!expr) can be replaced by assertFalse(expr)"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#simplifybooleanassertion">
Avoid negation in an assertTrue or assertFalse test.
@@ -1213,8 +1214,14 @@ PrimaryExpression/PrimarySuffix/Arguments/ArgumentList
/Expression/UnaryExpressionNotPlusMinus[@Image='!']
/PrimaryExpression/PrimaryPrefix
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml
index 6e1d7e8e61..7882ad02ca 100644
--- a/pmd-java/src/main/resources/category/java/documentation.xml
+++ b/pmd-java/src/main/resources/category/java/documentation.xml
@@ -96,7 +96,7 @@ 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 5c322a7351..e6fdcd2de2 100644
--- a/pmd-java/src/main/resources/category/java/errorprone.xml
+++ b/pmd-java/src/main/resources/category/java/errorprone.xml
@@ -707,9 +707,9 @@ public String bar(String string) {
/Block[not(
(BlockStatement[1]/Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image]))]
[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[
- typeIs('android.app.Activity') or
- typeIs('android.app.Application') or
- typeIs('android.app.Service')
+ pmd-java:typeIs('android.app.Activity') or
+ pmd-java:typeIs('android.app.Application') or
+ pmd-java:typeIs('android.app.Service')
]]]
]]>
@@ -752,9 +752,9 @@ Super should be called at the end of the method
/Block/BlockStatement[last()]
[not(Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image])]
[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[
- typeIs('android.app.Activity') or
- typeIs('android.app.Application') or
- typeIs('android.app.Service')
+ pmd-java:typeIs('android.app.Activity') or
+ pmd-java:typeIs('android.app.Application') or
+ pmd-java:typeIs('android.app.Service')
]]]
]]>
@@ -2058,8 +2058,14 @@ Some JUnit framework methods are easy to misspell.
or (not(@Image = 'tearDown')
and translate(@Image, 'TEARdOWN', 'tearDown') = 'tearDown')]
[FormalParameters[count(*) = 0]]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
@@ -2080,6 +2086,7 @@ public class Foo extends TestCase {
since="1.0"
message="You have a suite() method that is not both public and static, so JUnit won't call it to get your TestSuite. Is that what you wanted to do?"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#junitstaticsuite">
The suite() method in a JUnit test needs to be both public and static.
@@ -2092,8 +2099,14 @@ The suite() method in a JUnit test needs to be both public and static.
//MethodDeclaration[not(@Static='true') or not(@Public='true')]
[MethodDeclarator/@Image='suite']
[MethodDeclarator/FormalParameters/@ParameterCount=0]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
@@ -2277,6 +2290,7 @@ public void bar(int status) {
since="3.0"
message="Classes implementing Serializable should set a serialVersionUID"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#missingserialversionuid">
Serializable classes should provide a serialVersionUID field.
@@ -2287,16 +2301,10 @@ Serializable classes should provide a serialVersionUID field.
@@ -3037,6 +3045,7 @@ public class Foo {
since="3.0"
message="assertTrue(true) or similar statements are unnecessary"
class="net.sourceforge.pmd.lang.rule.XPathRule"
+ typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#unnecessarybooleanassertion">
A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the same thing.
@@ -3059,8 +3068,14 @@ or
UnaryExpressionNotPlusMinus[@Image='!']
/PrimaryExpression/PrimaryPrefix[Literal/BooleanLiteral or Name[count(../../*)=1]]]
]
-[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')] or //MarkerAnnotation/Name[pmd-java:typeIs('org.junit.Test')]]]
-]]>
+[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeIs('junit.framework.TestCase')]
+ or //MarkerAnnotation/Name[
+ pmd-java:typeIs('org.junit.Test')
+ or pmd-java:typeIs('org.junit.jupiter.api.Test') or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
+ or pmd-java:typeIs('org.junit.jupiter.api.TestFactory') or pmd-java:typeIs('org.junit.jupiter.api.TestTemplate')
+ or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
+ ]
+]]]]>
diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml
index fcede3d979..f2d629356d 100644
--- a/pmd-java/src/main/resources/category/java/multithreading.xml
+++ b/pmd-java/src/main/resources/category/java/multithreading.xml
@@ -169,8 +169,8 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co
[
./Name[ends-with(@Image, '.run') or @Image = 'run']
and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image
- [../../../Type/ReferenceType/ClassOrInterfaceType[typeIs('java.lang.Thread')]]
- or (./AllocationExpression/ClassOrInterfaceType[typeIs('java.lang.Thread')]
+ [../../../Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]]
+ or (./AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]
and ../PrimarySuffix[@Image = 'run'])
]
]
diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml
index 685537197b..66e1583bdb 100644
--- a/pmd-java/src/main/resources/category/java/performance.xml
+++ b/pmd-java/src/main/resources/category/java/performance.xml
@@ -135,10 +135,10 @@ The FileReader and FileWriter constructors instantiate FileInputStream and FileO
@@ -209,10 +209,10 @@ adverse impacts on performance.
@@ -467,7 +467,7 @@ Note that new Integer() is deprecated since JDK 9 for that reason.
@@ -500,7 +500,7 @@ Note that new Long() is deprecated since JDK 9 for that reason.
@@ -664,7 +664,7 @@ Note that new Short() is deprecated since JDK 9 for that reason.
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java
index f8c30b8f57..f2972b1407 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionDiscovererTest.java
@@ -26,8 +26,8 @@ public class LanguageVersionDiscovererTest {
File javaFile = new File("/path/to/MyClass.java");
LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(javaFile);
- assertEquals("LanguageVersion must be Java 10 !",
- LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"), languageVersion);
+ assertEquals("LanguageVersion must be Java 11 !",
+ LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), languageVersion);
}
/**
@@ -48,7 +48,7 @@ public class LanguageVersionDiscovererTest {
public void testLanguageVersionDiscoverer() {
PMDConfiguration configuration = new PMDConfiguration();
LanguageVersionDiscoverer languageVersionDiscoverer = configuration.getLanguageVersionDiscoverer();
- assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"),
+ assertEquals("Default Java version", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"),
languageVersionDiscoverer
.getDefaultLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)));
configuration
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java
index 984a99b706..1adfc7c185 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java
@@ -34,6 +34,12 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest {
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.7"), },
{ JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "1.8",
LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.8"), },
+ { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "9",
+ LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("9"), },
+ { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "10",
+ LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("10"), },
+ { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "11",
+ LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), },
// this one won't be found: case sensitive!
{ "JAVA", "JAVA", "1.7", null, }, });
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java
new file mode 100644
index 0000000000..aeb7942d4b
--- /dev/null
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java
@@ -0,0 +1,89 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.ast;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.java.ParserTstUtil;
+
+public class Java11Test {
+ private static String loadSource(String name) {
+ try {
+ return IOUtils.toString(Java10Test.class.getResourceAsStream("jdkversiontests/java11/" + name),
+ StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testLocalVariableSyntaxForLambdaParametersWithJava10() {
+ ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("10",
+ loadSource("LocalVariableSyntaxForLambdaParameters.java"));
+
+ List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class);
+ Assert.assertEquals(4, lambdas.size());
+
+ // (var x) -> String.valueOf(x);
+ List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class);
+ Assert.assertEquals(1, formalParameters.size());
+ ASTType type = formalParameters.get(0).getFirstChildOfType(ASTType.class);
+ assertEquals("var", type.getTypeImage());
+ assertEquals(1, type.jjtGetNumChildren());
+ ASTReferenceType referenceType = type.getFirstChildOfType(ASTReferenceType.class);
+ assertNotNull(referenceType);
+ assertEquals(1, referenceType.jjtGetNumChildren());
+ ASTClassOrInterfaceType classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class);
+ assertNotNull(classType);
+ assertEquals("var", classType.getImage());
+
+ // (var x, var y) -> x + y;
+ formalParameters = lambdas.get(1).findDescendantsOfType(ASTFormalParameter.class);
+ Assert.assertEquals(2, formalParameters.size());
+ type = formalParameters.get(0).getFirstChildOfType(ASTType.class);
+ assertEquals("var", type.getTypeImage());
+ assertEquals(1, type.jjtGetNumChildren());
+ referenceType = type.getFirstChildOfType(ASTReferenceType.class);
+ assertNotNull(referenceType);
+ assertEquals(1, referenceType.jjtGetNumChildren());
+ classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class);
+ assertNotNull(classType);
+ assertEquals("var", classType.getImage());
+ type = formalParameters.get(1).getFirstChildOfType(ASTType.class);
+ assertEquals("var", type.getTypeImage());
+ assertEquals(1, type.jjtGetNumChildren());
+
+ // (@Nonnull var x) -> String.valueOf(x);
+ formalParameters = lambdas.get(2).findDescendantsOfType(ASTFormalParameter.class);
+ Assert.assertEquals(1, formalParameters.size());
+ Node firstChild = formalParameters.get(0).jjtGetChild(0);
+ Assert.assertTrue(firstChild instanceof ASTAnnotation);
+ }
+
+ @Test
+ public void testLocalVariableSyntaxForLambdaParametersWithJava11() {
+ ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("11",
+ loadSource("LocalVariableSyntaxForLambdaParameters.java"));
+
+ List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class);
+ Assert.assertEquals(4, lambdas.size());
+
+ // (var x) -> String.valueOf(x);
+ List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class);
+ Assert.assertEquals(1, formalParameters.size());
+ Assert.assertNull(formalParameters.get(0).getTypeNode());
+ Assert.assertTrue(formalParameters.get(0).isTypeInferred());
+ }
+}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java
index 7cdaa12da2..5a64ad0efe 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java
@@ -53,6 +53,7 @@ public class XPathMetricFunctionTest {
Report report = new Report();
ctx.setReport(report);
ctx.setSourceCodeFilename("n/a");
+ ctx.setIgnoreExceptions(false); // for test, we want immediate exceptions thrown and not collect them
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx);
return report.iterator();
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CodeStyleRulesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CodeStyleRulesTest.java
index 98b22b1ecf..e2fb35e5ed 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CodeStyleRulesTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CodeStyleRulesTest.java
@@ -42,6 +42,7 @@ public class CodeStyleRulesTest extends SimpleAggregatorTst {
addRule(RULESET, "IdenticalCatchBranches");
addRule(RULESET, "IfElseStmtsMustUseBraces");
addRule(RULESET, "IfStmtsMustUseBraces");
+ addRule(RULESET, "LinguisticNaming");
addRule(RULESET, "LocalHomeNamingConvention");
addRule(RULESET, "LocalInterfaceSessionNamingConvention");
addRule(RULESET, "LocalVariableCouldBeFinal");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDBase.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDBase.java
new file mode 100644
index 0000000000..f402d9a793
--- /dev/null
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDBase.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;
+
+import java.io.Serializable;
+
+/**
+ * This class is used for a test case for the rule MissingSerialVersionUID.
+ */
+public class MissingSerialVersionUIDBase implements Serializable {
+ private static final long serialVersionUID = 1234567L;
+}
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java
new file mode 100644
index 0000000000..7daa4efd82
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java11/LocalVariableSyntaxForLambdaParameters.java
@@ -0,0 +1,28 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class LocalVariableSyntaxForLambdaParameters {
+
+ @Target({ElementType.PARAMETER})
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Nonnull { }
+
+ public void createLambdas() {
+ //var lambda = (var x, var y) -> x.process(y);
+
+ Function lambda1 = (var x) -> String.valueOf(x);
+ BiFunction lambda2 = (var x, var y) -> x + y;
+ }
+
+ public void createAnnotatedLambdaParameters() {
+ //@Nonnull var x = new Foo();
+ //(@Nonnull var x, @Nullable var y) -> x.process(y)
+
+ Function lambda1 = (@Nonnull var x) -> String.valueOf(x);
+ BiFunction lambda2 = (@Nonnull var x, @Nonnull var y) -> x + y;
+ }
+}
\ No newline at end of file
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NPathTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NPathTest.xml
index 0180ebfb9e..8a305fa9f5 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NPathTest.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NPathTest.xml
@@ -459,4 +459,51 @@ class Bar {
}
]]>
+
+
+ #1226 [java] NPath complexity false negative
+ 0
+ 1
+
+ 'NPathComplexityOverflow#complexMethod()' has value 2147483647.
+
+
+
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 091b027d96..9bd3fd0345 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
@@ -4,9 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
-
+ inner class has private constructor1
+ java 10
+
-
+ inner class has public constructor0
+ java 10
+
-
+ outer class has public constructor1
+ java 10
+
-
+ final inner class0
+ java 10
+
-
+ interface inner class has private constructor1
+ java 10
+
-
+ there's a check for int declaration - not sure right now why1
+ java 10
+
#1452 ArrayIndexOutOfBoundsException with Annotations for AccessorClassGenerationRule0
+ java 10
+
-
+ #291 - Private constructor called from anonymous class1
+ java 10
+
-
+ Array initializer is not a class body0
+ 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 17b12632f4..bf1b8fd638 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
@@ -4,9 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
-
+ inner class accesses private field from outer class18
+ java 10
+
-
+ inner class accesses private field from outer class unqualified18
+ java 10
+
-
+ outer class accesses private field from inner class19
+ java 10
-
-
+
+
+ non private fields are ok0
+ java 10
+
-
+ inner class accesses private method of outer class, unqualified14
+ java 10
+
-
+ inner class accesses private method of outer class, qualified14
+ java 10
+
-
+ outer class accesses private method of inner class18
+ java 10
-
+ inner class accesses non-private methods of outer class0
+ java 10
+
-
+ #274 - Method inside static inner class incorrectly reported as generating accessor methods0
+ java 10
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseAfterAnnotation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseAfterAnnotation.xml
index a71967faf6..e0854a0c66 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseAfterAnnotation.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseAfterAnnotation.xml
@@ -61,6 +61,34 @@ public class Foo {
public void tearDown(Method m) {
//...
}
+}
+ ]]>
+
+
+ #940 False positive with JUnit4TestShouldUseAfterAnnotation when JUnit5's 'AfterEach' is used
+ 0
+
+
+
+ #940 False positive with JUnit4TestShouldUseAfterAnnotation when JUnit5's 'AfterAll' is used
+ 0
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseBeforeAnnotation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseBeforeAnnotation.xml
index 272663322d..b9243c2bbe 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseBeforeAnnotation.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseBeforeAnnotation.xml
@@ -73,6 +73,34 @@ public class Foo {
public void setUp(Method m) {
//...
}
+}
+ ]]>
+
+
+ #940 False positive with JUnit4TestShouldUseBeforeAnnotation when JUnit5's 'BeforeEach' is used
+ 0
+
+
+
+ #940 False positive with JUnit4TestShouldUseBeforeAnnotation when JUnit5's 'BeforeAll' is used
+ 0
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseTestAnnotation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseTestAnnotation.xml
index 1f0d250eb2..7385b233bf 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseTestAnnotation.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnit4TestShouldUseTestAnnotation.xml
@@ -151,6 +151,36 @@ public class TestForX {
+
+
+ #940 False positives with JUnit4TestShouldUseTestAnnotation when JUnit5 is used
+ 0
+
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 839d9cb0e5..1701f8bd70 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
@@ -133,4 +133,74 @@
}
]]>
+
+
+ JUnit 5 Test contains more than one assert
+ 5
+ 10,17,24,31,39
+
+
+
+
+ TestNG Test contains more than one assert
+ 1
+ 5
+
+
\ No newline at end of file
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 da8e405407..82d90cfc3c 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
@@ -65,6 +65,22 @@ JUnit4 - TEST2
1
+
+
+
+ 1
+ 1
+
+
+
+ 1
+ 1
+
+
+
+ 1
+ [a-z][A-Za-z]*
1
- The class name 'foo' doesn't match '[A-Z][a-zA-Z0-9]+'
+ The class name 'foo' doesn't match '[A-Z][a-zA-Z0-9]*'
+
+
+ Test property defaults
+ 4
+
+ The field name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final field name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'Bar' doesn't match '[a-z][a-zA-Z0-9]*'
+ The constant name 'BOLGaaa_FIELD' doesn't match '[A-Z][A-Z_0-9]*'
+
+
+
+
+
+ Test default field property
+ [A-Z][A-Z0-9]+
+ 3
+
+ The field name 'Foo' doesn't match '[A-Z][A-Z0-9]+'
+ The final field name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'Bar' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ Test final field property
+ [A-Z][A-Z0-9]+
+ 3
+
+ The field name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final field name 'Hoo' doesn't match '[A-Z][A-Z0-9]+'
+ The static field name 'Bar' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ Test static field property
+ [A-Z][A-Z0-9]+
+ 3
+
+ The field name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final field name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'Bar' doesn't match '[A-Z][A-Z0-9]+'
+
+
+
+
+
+ Test constant field property
+ cons_[A-Z][A-Z0-9]+
+ 4
+
+ The field name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final field name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'Bar' doesn't match '[a-z][a-zA-Z0-9]*'
+ The constant name 'BOLG_FIELD' doesn't match 'cons_[A-Z][A-Z0-9]+'
+
+
+
+
+
+ Test enum constant property
+ cons_[A-Z][A-Z0-9]+
+ 2
+
+ The enum constant name 'NET' doesn't match 'cons_[A-Z][A-Z0-9]+'
+ The enum constant name 'ORG' doesn't match 'cons_[A-Z][A-Z0-9]+'
+
+
+
+
+
+
+ Test public constant property
+ cons_[A-Z][A-Z0-9]+
+ 2
+
+ The public constant name 'DDD' doesn't match 'cons_[A-Z][A-Z0-9]+'
+ The constant name 'cons_BOLG_FIELD' doesn't match '[A-Z][A-Z0-9]+'
+
+
+
+
+
+
+
+ Interface fields should be treated like constants
+ 3
+
+ The constant name 'Foo' doesn't match '[A-Z][A-Z_0-9]*'
+ The constant name 'Hoo' doesn't match '[A-Z][A-Z_0-9]*'
+ The constant name 'Bar' doesn't match '[A-Z][A-Z_0-9]*'
+
+
+
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FormalParameterNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FormalParameterNamingConventions.xml
index 60c2f9237b..e7af6d9c9f 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FormalParameterNamingConventions.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FormalParameterNamingConventions.xml
@@ -7,11 +7,11 @@
Default is camel case5
- The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
- The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
- The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]*'
+
+ One character lambda Parameters should be allowed by default
+ 0
+ i = (s) -> {
+
+ };
+
+ Consumer k = (String s) -> {
+
+ };
+
+ Consumer l = (final String s) -> {
+
+ };
+ }
+ }
+ ]]>
+
+
Test method param pattern[A-Z]+5The method parameter name 'Foo' doesn't match '[A-Z]+'
- The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
- The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]*'[A-Z]+
5
- The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'The final method parameter name 'Hoo' doesn't match '[A-Z]+'
- The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]*'[A-Z]+
5
- The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
- The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'The lambda parameter name 'Koo' doesn't match '[A-Z]+'
- The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]+'
- The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The explicitly-typed lambda parameter name 'Voo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The explicitly-typed lambda parameter name 'Ooo' doesn't match '[a-z][a-zA-Z0-9]*'[A-Z]+
5
- The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
- The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
- The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The method parameter name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final method parameter name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The lambda parameter name 'Koo' doesn't match '[a-z][a-zA-Z0-9]*'The explicitly-typed lambda parameter name 'Voo' doesn't match '[A-Z]+'The explicitly-typed lambda parameter name 'Ooo' doesn't match '[A-Z]+'
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LinguisticNaming.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LinguisticNaming.xml
new file mode 100644
index 0000000000..68a35fabcf
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LinguisticNaming.xml
@@ -0,0 +1,417 @@
+
+
+
+
+
+ Method Prefix is
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'isValid' indicates linguistically it returns a boolean, but it returns 'int'
+
+
+
+
+
+ Method Prefix Has
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'hasChild' indicates linguistically it returns a boolean, but it returns 'int'
+
+
+
+
+
+ Method Prefix Have
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'haveChild' indicates linguistically it returns a boolean, but it returns 'int'
+
+
+
+
+
+ Method Prefix can
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'canFly' indicates linguistically it returns a boolean, but it returns 'int'
+
+
+
+
+
+ Method Prefix will
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'willFly' indicates linguistically it returns a boolean, but it returns 'int'
+
+
+
+
+
+ Method Prefix should
+ 1
+ 6
+
+ Linguistics Antipattern - The method 'shouldFly' indicates linguistically it returns a boolean, but it returns 'long'
+
+
+
+
+
+ Method Setters
+ 1
+ 6
+
+ Linguistics Antipattern - The setter 'setName' should not return any type except void linguistically
+
+
+
+
+
+ Method Getters
+ 1
+ 6
+
+ Linguistics Antipattern - The getter 'getName' should not return void linguistically
+
+
+
+
+
+ Method Prefix to: Transform Method
+ 1
+ 6
+
+ Linguistics Antipattern - The transform method 'toDataType' should not return void linguistically
+
+
+
+
+
+ Method Contains To: Transformation methods
+ true
+ 1
+ 2
+
+ Linguistics Antipattern - The transform method 'grapeToWine' should not return void linguistically
+
+
+
+
+
+ Field/Variable Prefix is
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'isValid' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'isValidLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Field/Variable Prefix has
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'hasMoney' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'hasMoneyLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Field/Variable Prefix can
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'canFly' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'canFlyLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Field/Variable Prefix will
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'willMove' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'willMoveLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Field/Variable Prefix have
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'haveLegs' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'haveLegsLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Field/Variable Prefix should
+ 2
+ 3,8
+
+ Linguistics Antipattern - The field 'shouldClimb' indicates linguistically it is a boolean, but it is 'int'
+ Linguistics Antipattern - The variable 'shouldClimbLocal' indicates linguistically it is a boolean, but it is 'int'
+
+
+
+
+
+ Multiple fields/local variables
+ 4
+ 2,2,4,4
+
+
+
+
+ Boolean fields/methods false positive
+ 0
+
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LocalVariableNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LocalVariableNamingConventions.xml
index 279131ffe3..e54e1fed1d 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LocalVariableNamingConventions.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/LocalVariableNamingConventions.xml
@@ -7,9 +7,9 @@
Default is camel case3
- The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
- The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
- The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]+'
+ The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]*'3
The local variable name 'Foo' doesn't match '[A-Z][A-Z0-9]+'
- The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
- The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]+'
+ The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]*'[A-Z][A-Z0-9]+
3
- The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'The final local variable name 'Hoo' doesn't match '[A-Z][A-Z0-9]+'
- The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]+'
+ The exception block parameter name 'E' doesn't match '[a-z][a-zA-Z0-9]*'[A-Z][A-Z0-9]+
3
- The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]+'
- The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The local variable name 'Foo' doesn't match '[a-z][a-zA-Z0-9]*'
+ The final local variable name 'Hoo' doesn't match '[a-z][a-zA-Z0-9]*'The exception block parameter name 'Eff' doesn't match '[A-Z][A-Z0-9]+'
+
+ One character for loop variables should be ok by default
+ 0
+ data = Arrays.asList("a", "b");
+ for (String s : data) { }
+ }
+ }
+ ]]>
+
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 c54c8e4b99..c494c196af 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
@@ -16,7 +16,7 @@ public class Foo {
method names should not contain underscores1
- The instance method name 'bar_foo' doesn't match '[a-z][a-zA-Z0-9]+'
+ The instance method name 'bar_foo' doesn't match '[a-z][a-zA-Z0-9]*'1
2
- The native method name '__surfunc__' doesn't match '[a-z][a-zA-Z0-9]+'
+ The native method name '__surfunc__' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+ #1255 [java] UnnecessaryFullyQualifiedName false positive: static method on shadowed implicitly imported class
+ 0
+
+
+
+
+ Nullpointer in isJavaLangImplicit for java.lang.String[] arrays
+ 1
+ 6
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UselessParentheses.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UselessParentheses.xml
index 2a2de5804d..777c87bcca 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UselessParentheses.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UselessParentheses.xml
@@ -483,6 +483,22 @@ public class Test {
return (Character.toUpperCase(str1.charAt(0))
+ str1.substring(1)).replace('_', ' ');
}
+}
+ ]]>
+
+
+
+ [java] False positive "UselessParentheses" for parentheses that contain assignment #1285
+ 0
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyBooleanAssertion.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyBooleanAssertion.xml
index cc43b31f29..fbc4958638 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyBooleanAssertion.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyBooleanAssertion.xml
@@ -70,6 +70,23 @@ JUnit 4 - assertFalse(!)
1
+
+
+
+ 1
+
+
+
+ #1078 [java] MissingSerialVersionUID rule does not seem to catch inherited classes
+ 1
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnnecessaryBooleanAssertion.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnnecessaryBooleanAssertion.xml
index d06f84fb16..a0be95c777 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnnecessaryBooleanAssertion.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnnecessaryBooleanAssertion.xml
@@ -110,6 +110,22 @@ JUnit 4 - failure case
1
+
+
+
+ 1
+
+
+
+
+ #1298 [java] RedundantFieldInitializer - NumberFormatException with Long
+ 0
+
diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml
index 9b0d5310d2..2fa4054c77 100644
--- a/pmd-java8/pom.xml
+++ b/pmd-java8/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml
index f3590ca568..e6c8d5a17a 100644
--- a/pmd-javascript/pom.xml
+++ b/pmd-javascript/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-jsp/etc/grammar/JspParser.jjt b/pmd-jsp/etc/grammar/JspParser.jjt
index 7ea94dcf5f..44481522b2 100644
--- a/pmd-jsp/etc/grammar/JspParser.jjt
+++ b/pmd-jsp/etc/grammar/JspParser.jjt
@@ -248,7 +248,7 @@ PARSER_END(JspParser)
TOKEN :
{
- >
+ | "${" ( | )* "}") >
| " > : AfterTagState
| " | "!>") > : AfterTagState
| " > : AfterTagState
diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml
index c593722c19..614119b8e5 100644
--- a/pmd-jsp/pom.xml
+++ b/pmd-jsp/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java
index 0d29af459e..543602a60c 100644
--- a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java
+++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java
@@ -6,6 +6,6 @@ package net.sourceforge.pmd.cpd;
public class JSPLanguage extends AbstractLanguage {
public JSPLanguage() {
- super("JSP", "jsp", new JSPTokenizer(), ".jsp", ".jspx");
+ super("JSP", "jsp", new JSPTokenizer(), ".jsp", ".jspx", ".jspf", ".tag");
}
}
diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java
index 8fa12452f2..2ad43f34be 100644
--- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java
+++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java
@@ -16,7 +16,7 @@ public class JspLanguageModule extends BaseLanguageModule {
public static final String TERSE_NAME = "jsp";
public JspLanguageModule() {
- super(NAME, "JSP", TERSE_NAME, JspRuleChainVisitor.class, "jsp");
+ super(NAME, "JSP", TERSE_NAME, JspRuleChainVisitor.class, "jsp", "jspx", "jspf", "tag");
addVersion("", new JspHandler(), true);
}
}
diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java
index b05018c207..6f4cae6f8b 100644
--- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java
+++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/JspParserTest.java
@@ -4,12 +4,16 @@
package net.sourceforge.pmd.lang.jsp;
+import java.io.File;
import java.io.StringReader;
+import java.nio.file.Paths;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.lang.LanguageRegistry;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ast.Node;
@@ -29,7 +33,21 @@ public class JspParserTest {
"$129.00");
Assert.assertNotNull(node);
}
-
+
+ @Test
+ public void testParseELAttribute() {
+ Node node = parse(
+ "
");
+ Assert.assertNotNull(node);
+ }
+
/**
* Verifies bug #311 Jsp parser fails on boolean attribute
*/
@@ -40,6 +58,29 @@ public class JspParserTest {
Assert.assertNotNull(node);
}
+ @Test
+ public void testParseJsp() {
+ testInternalJspFile(Paths.get("sample.jsp").toFile());
+ testInternalJspFile(Paths.get("sample.jspx").toFile());
+ }
+
+ @Test
+ public void testParseTag() {
+ testInternalJspFile(Paths.get("sample.tag").toFile());
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testParseWrong() {
+ testInternalJspFile(Paths.get("sample.xxx").toFile());
+ }
+
+ private void testInternalJspFile(File jspFile) {
+ LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer();
+ LanguageVersion languageVersion = discoverer.getDefaultLanguageVersionForFile(jspFile);
+ Assert.assertEquals("LanguageVersion must be JSP!",
+ LanguageRegistry.getLanguage(JspLanguageModule.NAME).getDefaultVersion(), languageVersion);
+ }
+
private Node parse(String code) {
LanguageVersionHandler jspLang = LanguageRegistry.getLanguage(JspLanguageModule.NAME).getDefaultVersion()
.getLanguageVersionHandler();
diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml
index d3f6d1e404..c08b83faa1 100644
--- a/pmd-matlab/pom.xml
+++ b/pmd-matlab/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml
index 7f7b5bc551..049e8bc890 100644
--- a/pmd-objectivec/pom.xml
+++ b/pmd-objectivec/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml
index e0a9de5e30..70f6d334e7 100644
--- a/pmd-perl/pom.xml
+++ b/pmd-perl/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml
index 3d558889b9..83b4e422a5 100644
--- a/pmd-php/pom.xml
+++ b/pmd-php/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-plsql/etc/grammar/PldocAST.jjt b/pmd-plsql/etc/grammar/PldocAST.jjt
index 0d162e6bec..f265be2476 100644
--- a/pmd-plsql/etc/grammar/PldocAST.jjt
+++ b/pmd-plsql/etc/grammar/PldocAST.jjt
@@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* Added more complete support for CREATE TABLE
+ * Added ASTCursorForLoop and ASTSelectStatement
*
* Andreas Dangel 07/2018
*====================================================================
@@ -1176,6 +1177,7 @@ ASTUnlabelledStatement UnlabelledStatement() :
| LOOKAHEAD(3) ContinueStatement() ";" // CONTINUE keyword was added in 11G, so Oracle compilation supports CONTINUE as a variable name
| CaseStatement() ";"
| IfStatement() ";"
+ | LOOKAHEAD( ID() "("
+
+
+In case you have loops please name the loop variables more meaningful.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
index b9d737e89a..99531494f2 100644
--- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.plsql;
+import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
@@ -15,6 +16,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.apache.commons.io.IOUtils;
+
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
@@ -127,4 +130,12 @@ public abstract class AbstractPLSQLParserTst {
return languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions()).parse(null,
new StringReader(code));
}
+
+ public String loadTestResource(String name) {
+ try {
+ return IOUtils.toString(this.getClass().getResourceAsStream(name));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopTest.java
new file mode 100644
index 0000000000..e3d0a77fc5
--- /dev/null
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopTest.java
@@ -0,0 +1,64 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.plsql.ast;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst;
+
+public class CursorForLoopTest extends AbstractPLSQLParserTst {
+
+ @Test
+ public void parseCursorForLoopSimple() {
+ String code = loadTestResource("CursorForLoopSimple.pls");
+ ASTInput input = parsePLSQL(code);
+ ASTCursorForLoopStatement forloop = input.getFirstDescendantOfType(ASTCursorForLoopStatement.class);
+ Assert.assertNotNull(forloop);
+ ASTForIndex forindex = forloop.getFirstChildOfType(ASTForIndex.class);
+ Assert.assertNotNull(forindex);
+ Assert.assertEquals("someone", forindex.getImage());
+ }
+
+ @Test
+ public void parseCursorForLoopNested() {
+ String code = loadTestResource("CursorForLoopNested.pls");
+ ASTInput input = parsePLSQL(code);
+ ASTCursorForLoopStatement forloop = input.getFirstDescendantOfType(ASTCursorForLoopStatement.class);
+ Assert.assertNotNull(forloop);
+ ASTForIndex forindex = forloop.getFirstChildOfType(ASTForIndex.class);
+ Assert.assertNotNull(forindex);
+ Assert.assertEquals("c_cmp", forindex.getImage());
+
+ ASTCursorForLoopStatement forloop2 = forloop.getFirstDescendantOfType(ASTCursorForLoopStatement.class);
+ ASTForIndex forindex2 = forloop2.getFirstChildOfType(ASTForIndex.class);
+ Assert.assertEquals("c_con", forindex2.getImage());
+
+ ASTCursorForLoopStatement forloop3 = forloop2.getFirstDescendantOfType(ASTCursorForLoopStatement.class);
+ ASTForIndex forindex3 = forloop3.getFirstChildOfType(ASTForIndex.class);
+ Assert.assertEquals("c_pa", forindex3.getImage());
+ }
+
+ @Test
+ public void parseCursorForLoop1047a() {
+ String code = loadTestResource("CursorForLoop1047a.pls");
+ ASTInput input = parsePLSQL(code);
+ Assert.assertNotNull(input);
+ }
+
+ @Test
+ public void parseCursorForLoop1047b() {
+ String code = loadTestResource("CursorForLoop1047b.pls");
+ ASTInput input = parsePLSQL(code);
+ Assert.assertNotNull(input);
+ }
+
+ @Test
+ public void parseCursorForLoop681() {
+ String code = loadTestResource("CursorForLoop681.pls");
+ ASTInput input = parsePLSQL(code);
+ Assert.assertNotNull(input);
+ }
+}
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeStyleRulesTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeStyleRulesTest.java
index 47e294b3d7..a1cf646748 100644
--- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeStyleRulesTest.java
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeStyleRulesTest.java
@@ -13,5 +13,6 @@ public class CodeStyleRulesTest extends SimpleAggregatorTst {
@Override
public void setUp() {
addRule(RULESET, "MisplacedPragma");
+ addRule(RULESET, "ForLoopNaming");
}
}
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047a.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047a.pls
new file mode 100644
index 0000000000..04a27665e4
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047a.pls
@@ -0,0 +1,16 @@
+--
+-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+--
+
+CREATE OR REPLACE PROCEDURE test2 ( a OUT number, b OUT number )
+ AS
+ BEGIN
+ FOR registro IN ( SELECT SUM(field1),
+ MAX(field2)
+ INTO a,b
+ FROM test_tbl
+ GROUP BY fieldx)
+ LOOP
+ null;
+ END LOOP;
+END test2;
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047b.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047b.pls
new file mode 100644
index 0000000000..009e39cff9
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop1047b.pls
@@ -0,0 +1,13 @@
+--
+-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+--
+
+-- this one was processed without errors
+CREATE OR REPLACE PROCEDURE test2 ( a OUT number, b OUT number )
+ AS
+BEGIN
+ FOR registro IN ( SELECT SUM(field1),MAX(field2) INTO a,b FROM test_tbl GROUP BY fieldx )
+ LOOP
+ null;
+ END LOOP;
+END test2;
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop681.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop681.pls
new file mode 100644
index 0000000000..c98deddc3a
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoop681.pls
@@ -0,0 +1,20 @@
+--
+-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+--
+
+BEGIN
+ FOR sum_res IN (SELECT SUM(loss_reserve) loss_reserve,
+ SUM(expense_reserve) exp_reserve
+ FROM gicl_clm_reserve a, gicl_item_peril b
+ WHERE a.claim_id = b.claim_id
+ AND a.item_no = b.item_no
+ AND a.peril_cd = b.peril_cd
+ AND a.claim_id = p_claim_id
+ AND (NVL(b.close_flag, 'AP') IN ('AP','CC','CP') OR
+ NVL(b.close_flag2, 'AP') IN ('AP','CC','CP')))
+ LOOP
+ v_loss_res_amt := sum_res.loss_reserve;
+ v_exp_res_amt := sum_res.exp_reserve;
+ END LOOP;
+END;
+/
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopNested.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopNested.pls
new file mode 100644
index 0000000000..8eccf7a602
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopNested.pls
@@ -0,0 +1,14 @@
+--
+-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+--
+
+BEGIN
+FOR c_cmp IN (SELECT * FROM companies) LOOP
+ FOR c_con IN (SELECT * FROM contacts) LOOP
+ FOR c_pa IN (SELECT * FROM parties) LOOP
+ NULL;
+ END LOOP;
+ END LOOP;
+END LOOP;
+END;
+/
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopSimple.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopSimple.pls
new file mode 100644
index 0000000000..9c43ab8f7d
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/CursorForLoopSimple.pls
@@ -0,0 +1,16 @@
+--
+-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+--
+
+BEGIN
+FOR someone IN (
+SELECT * FROM employees
+WHERE employee_id < 120
+ORDER BY employee_id
+)
+LOOP
+DBMS_OUTPUT.PUT_LINE('First name = ' || someone.first_name ||
+', Last name = ' || someone.last_name);
+END LOOP;
+END;
+/
diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/rule/codestyle/xml/ForLoopNaming.xml b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/rule/codestyle/xml/ForLoopNaming.xml
new file mode 100644
index 0000000000..5d6bdf5df1
--- /dev/null
+++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/rule/codestyle/xml/ForLoopNaming.xml
@@ -0,0 +1,211 @@
+
+
+
+
+ CursorForLoop Simple case - no nesting
+ 1
+
+ plsql
+
+
+
+ CursorForLoop Simple case - no nesting - allowSimpleLoops
+ true
+ 0
+
+ plsql
+
+
+
+ CursorForLoop Nested loops - good example, default pattern
+ 0
+
+ plsql
+
+
+
+ CursorForLoop Nested loops - good example, custom pattern
+ c_[a-z]+
+ 0
+
+ plsql
+
+
+
+ CursorForLoop Nested loops - bad example, default pattern
+ 3
+ 2,3,4
+
+ plsql
+
+
+
+ CursorForLoop Nested loops - bad example, custom pattern
+ c_[a-z]+
+ 3
+ 2,3,4
+
+ plsql
+
+
+
+ ForLoop Simple case - no nesting
+ 1
+ 2
+
+
+
+
+ ForLoop Simple case - no nesting - allowSimpleLoops
+ true
+ 0
+
+
+
+
+ ForLoop Nested loops - good example, default pattern
+ 0
+
+
+
+
+ ForLoop Nested loops - good example, custom pattern
+ i_[a-z]+
+ 0
+
+
+
+
+ ForLoop Nested Loops - bad example, default pattern
+ 3
+ 2,3,4
+
+
+
+
+ ForLoop Nested Loops - bad example, custom pattern
+ i_[a-z]+
+ 3
+ 2,3,4
+
+
+
\ No newline at end of file
diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml
index 3a74b8710c..a88254c11c 100644
--- a/pmd-python/pom.xml
+++ b/pmd-python/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml
index 1bf0881357..99fb66a08e 100644
--- a/pmd-ruby/pom.xml
+++ b/pmd-ruby/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml
index c9b6abceca..b8837419a4 100644
--- a/pmd-scala/pom.xml
+++ b/pmd-scala/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml
index 7c0649bb74..e3dfbd9d6e 100644
--- a/pmd-swift/pom.xml
+++ b/pmd-swift/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml
index d048439a03..f525c34b05 100644
--- a/pmd-test/pom.xml
+++ b/pmd-test/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java
index 9b4f12f82a..0f9b6d310f 100644
--- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java
@@ -23,13 +23,13 @@ import org.junit.runners.model.InitializationError;
* A JUnit Runner, that combines the default {@link JUnit4}
* and our custom {@link RuleTestRunner}.
* It allows to selectively execute single test cases (it is {@link Filterable}).
- *
+ *
*
Note: Since we actually run two runners one after another, the static {@code BeforeClass}
* and {@Code AfterClass} methods will be executed twice and the test class will be instantiated twice, too.
*
*
In order to use it, you'll need to subclass {@link SimpleAggregatorTst} and
* annotate your test class with RunWith:
- *
+ *
*
* @RunWith(PMDTestRunner.class)
* public class MyRuleSetTest extends SimpleAggregatorTst {
@@ -78,7 +78,9 @@ public class PMDTestRunner extends Runner implements Filterable, Sortable {
public Description getDescription() {
Description description = Description.createSuiteDescription(klass);
description.addChild(createChildrenDescriptions(ruleTests, "Rule Tests"));
- description.addChild(createChildrenDescriptions(unitTests, "Unit Tests"));
+ if (ruleTests.hasUnitTests()) {
+ description.addChild(createChildrenDescriptions(unitTests, "Unit Tests"));
+ }
return description;
}
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 c4eddb7ce1..aede9072d9 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
@@ -202,12 +202,12 @@ public abstract class RuleTst {
}
List expected = test.getExpectedLineNumbers();
- if (report.getViolationTree().size() != expected.size()) {
+ if (report.size() != expected.size()) {
throw new RuntimeException("Test setup error: number of execpted line numbers doesn't match "
+ "number of violations for test case '" + test.getDescription() + "'");
}
- Iterator it = report.getViolationTree().iterator();
+ Iterator it = report.iterator();
int index = 0;
while (it.hasNext()) {
RuleViolation violation = it.next();
diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java
index 3c8a43eaaa..364823dbec 100644
--- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java
+++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java
@@ -12,27 +12,39 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import java.util.Arrays;
+
+import org.junit.Assert;
import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
+import net.sourceforge.pmd.test.lang.ast.DummyNode;
public class RuleTstTest {
+ private LanguageVersion dummyLanguage = LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion();
+
+ private Rule rule = mock(Rule.class);
+
+ private RuleTst ruleTester = new RuleTst() {
+ };
@Test
public void shouldCallStartAndEnd() {
- RuleTst ruleTester = new RuleTst() {
- };
- LanguageVersion languageVersion = LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion();
Report report = new Report();
- Rule rule = mock(Rule.class);
- when(rule.getLanguage()).thenReturn(languageVersion.getLanguage());
+ when(rule.getLanguage()).thenReturn(dummyLanguage.getLanguage());
when(rule.getName()).thenReturn("test rule");
- ruleTester.runTestFromString("the code", rule, report, languageVersion, false);
+ ruleTester.runTestFromString("the code", rule, report, dummyLanguage, false);
verify(rule).start(any(RuleContext.class));
verify(rule).end(any(RuleContext.class));
@@ -48,4 +60,40 @@ public class RuleTstTest {
verify(rule).getPropertiesByPropertyDescriptor();
verifyNoMoreInteractions(rule);
}
+
+ @Test
+ public void shouldAssertLinenumbersSorted() {
+ when(rule.getLanguage()).thenReturn(dummyLanguage.getLanguage());
+ when(rule.getName()).thenReturn("test rule");
+ Mockito.doAnswer(new Answer() {
+ private RuleViolation createViolation(RuleContext context, int beginLine, String message) {
+ DummyNode node = new DummyNode(1);
+ node.testingOnlySetBeginLine(beginLine);
+ node.testingOnlySetBeginColumn(1);
+ ParametricRuleViolation violation = new ParametricRuleViolation(rule, context, node, message);
+ return violation;
+ }
+
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ RuleContext context = invocation.getArgumentAt(1, RuleContext.class);
+ // the violations are reported out of order
+ context.getReport().addRuleViolation(createViolation(context, 15, "first reported violation"));
+ context.getReport().addRuleViolation(createViolation(context, 5, "second reported violation"));
+ return null;
+ }
+ }).when(rule).apply(Mockito.anyList(), Mockito.any(RuleContext.class));
+
+ TestDescriptor testDescriptor = new TestDescriptor("the code", "sample test", 2, rule, dummyLanguage);
+ testDescriptor.setReinitializeRule(false);
+ testDescriptor.setExpectedLineNumbers(Arrays.asList(5, 15));
+
+ try {
+ ruleTester.runTest(testDescriptor);
+ // there should be no assertion failures
+ // expected line numbers and actual line numbers match
+ } catch (AssertionError assertionError) {
+ Assert.fail(assertionError.toString());
+ }
+ }
}
diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml
index 9311063277..d572e7050e 100644
--- a/pmd-ui/pom.xml
+++ b/pmd-ui/pom.xml
@@ -7,7 +7,7 @@
net.sourceforge.pmdpmd
- 6.6.0-SNAPSHOT
+ 6.7.0-SNAPSHOT
diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/UniformStyleCollection.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/UniformStyleCollection.java
index 2f844b7d1e..7a713bf57d 100644
--- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/UniformStyleCollection.java
+++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/UniformStyleCollection.java
@@ -25,7 +25,7 @@ import net.sourceforge.pmd.util.fxdesigner.util.codearea.NodeStyleSpan.PositionS
/**
* Collection of nodes that share the same style. In case of overlap,
- * the nested ones gain css classes like depth-1, depth-2, etc. A
+ * the nested ones gain css classes like depth-0, depth-1, etc. A
* collection can be overlaid into a single span in one pass using
* {@link #toSpans()}.
*
diff --git a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml
index a8a67cda20..ac6c5aabb5 100644
--- a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml
+++ b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml
@@ -60,7 +60,7 @@