Merge branch 'java-grammar' into grammar-prefix-postfix

This commit is contained in:
Clément Fournier
2019-12-15 01:25:01 +01:00
194 changed files with 7121 additions and 2298 deletions

View File

@ -13,10 +13,9 @@ env:
matrix:
fast_finish: true
allow_failures:
- os: windows
include:
- name: "linux - mvn deploy"
- stage: build
name: "linux - mvn deploy"
os: linux
dist: bionic
env: BUILD=deploy
@ -28,10 +27,12 @@ matrix:
os: linux
dist: bionic
env: BUILD=sonar
if: "type = push && repo = pmd/pmd && fork = false"
- name: "linux - run coveralls"
os: linux
dist: bionic
env: BUILD=coveralls
if: "type = push && repo = pmd/pmd && fork = false"
- name: "macosx - mvn verify"
os: osx
env: BUILD=deploy
@ -39,6 +40,12 @@ matrix:
os: windows
env: BUILD=deploy
language: shell
- stage: publish
name: "Release Build - Publish"
os: linux
dist: bionic
if: "type = push && repo = pmd/pmd && fork = false && tag is present"
env: BUILD=publish
before_install:
- bash .travis/before_install.sh "11.0.5+10"
@ -49,22 +56,6 @@ script: source .travis/build-$BUILD.sh
after_success: true
before_deploy: true
deploy:
provider: releases
api_key: ${GITHUB_OAUTH_TOKEN}
file_glob: true
file:
- pmd-dist/target/pmd-*.zip
- docs/pmd-doc-*.zip
skip_cleanup: true
draft: true
on:
tags: true
repo: pmd/pmd
condition: "${TRAVIS_SECURE_ENV_VARS} = true && ${BUILD} != sonar && ${BUILD} != coveralls && ${TRAVIS_OS_NAME} = linux"
after_deploy: bash .travis/release.sh
notifications:
webhooks:
urls:

View File

@ -3,24 +3,9 @@ set -e
source .travis/logger.sh
source .travis/common-functions.sh
function upload_baseline() {
log_info "Generating and uploading baseline for pmdtester..."
cd ..
bundle config --local gemfile pmd/Gemfile
pmd/.travis/travis_wait "bundle exec 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}/
../../pmd/.travis/travis_wait "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/"
exit 1
else
log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to sourceforge"
fi
}
source .travis/github-releases-api.sh
source .travis/sourceforge-api.sh
source .travis/regression-tester.sh
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}"
@ -41,16 +26,8 @@ elif travis_isPullRequest; then
log_info "This is a pull-request build"
./mvnw verify $MVN_BUILD_FLAGS
(
set +e
# Create a corresponding remote branch locally
if ! git show-ref --verify --quiet refs/heads/${TRAVIS_BRANCH}; then
git fetch --no-tags origin +refs/heads/${TRAVIS_BRANCH}:refs/remotes/origin/${TRAVIS_BRANCH}
git branch ${TRAVIS_BRANCH} origin/${TRAVIS_BRANCH}
fi
log_info "Running danger"
bundle exec danger --verbose
)
regression-tester_executeDanger
elif travis_isPush; then
@ -58,10 +35,35 @@ elif travis_isPush; then
echo -e "\n\n"
log_info "This is a release build for tag ${TRAVIS_TAG}"
echo -e "\n\n"
# create a draft github release
gh_releases_createDraftRelease "${TRAVIS_TAG}" "$(git show-ref --hash ${TRAVIS_TAG})"
GH_RELEASE="$RESULT"
# Build and deploy to ossrh / maven-central
./mvnw deploy -Possrh,sign,pmd-release $MVN_BUILD_FLAGS
echo -e "\n\n"
# Deploy to github releases
gh_release_uploadAsset "$GH_RELEASE" "pmd-dist/target/pmd-bin-${VERSION}.zip"
gh_release_uploadAsset "$GH_RELEASE" "pmd-dist/target/pmd-src-${VERSION}.zip"
# Deploy to sourceforge files
sourceforge_uploadFile "${VERSION}" "pmd-dist/target/pmd-bin-${VERSION}.zip"
sourceforge_uploadFile "${VERSION}" "pmd-dist/target/pmd-src-${VERSION}.zip"
regression-tester_uploadBaseline
elif [[ "${VERSION}" == *-SNAPSHOT ]]; then
log_info "This is a snapshot build"
./mvnw deploy -Possrh,sign $MVN_BUILD_FLAGS
# Deploy to sourceforge files
sourceforge_uploadFile "${VERSION}" "pmd-dist/target/pmd-bin-${VERSION}.zip"
sourceforge_uploadFile "${VERSION}" "pmd-dist/target/pmd-src-${VERSION}.zip"
regression-tester_uploadBaseline
else
# other build. Can happen during release: the commit with a non snapshot version is built, but not from the tag.
log_info "This is some other build, probably during release: commit with a non-snapshot version on branch master..."
@ -70,53 +72,6 @@ elif travis_isPush; then
exit 0
fi
(
# disable fast fail, exit immediately, in this subshell
set +e
echo -e "\n\n"
log_info "Uploading pmd distribution to sourceforge..."
.travis/travis_wait "rsync -avh pmd-dist/target/pmd-*-${VERSION}.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd/${VERSION}/"
if [ $? -ne 0 ]; then
log_error "Error while uploading pmd-*-${VERSION}.zip to sourceforge!"
log_error "Please upload manually: https://sourceforge.net/projects/pmd/files/pmd/"
exit 1
else
log_success "Successfully uploaded pmd-*-${VERSION}.zip to sourceforge"
fi
)
( # UPLOAD RELEASE NOTES TO SOURCEFORGE
# This handler is called if any command fails
function release_notes_fail() {
log_error "Error while uploading release_notes.md as ReadMe.md to sourceforge!"
log_error "Please upload manually: https://sourceforge.net/projects/pmd/files/pmd/"
}
# exit subshell after trap
set -e
trap release_notes_fail ERR
RELEASE_NOTES_TMP=$(mktemp -t)
bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6 > "$RELEASE_NOTES_TMP"
rsync -avh "$RELEASE_NOTES_TMP" ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd/${VERSION}/ReadMe.md
log_success "Successfully uploaded release_notes.md as ReadMe.md to sourceforge"
)
(
# disable fast fail, exit immediately, in this subshell
set +e
upload_baseline
)
else
log_info "This is neither a pull request nor a push. Not executing any build."
exit 1

View File

@ -3,92 +3,155 @@ set -e
source .travis/logger.sh
source .travis/common-functions.sh
source .travis/github-releases-api.sh
source .travis/sourceforge-api.sh
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 Documentation ${VERSION} on branch ${TRAVIS_BRANCH}"
function main() {
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 Documentation ${VERSION} on branch ${TRAVIS_BRANCH}"
#
# First step: build pmd with profile "generate-rule-docs"
# The docs should appear under "docs/pages/rules/..." for each language
#
./mvnw clean verify -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -P generate-rule-docs
#
# First step: build pmd with profile "generate-rule-docs"
# The docs should appear under "docs/pages/rules/..." for each language
# With this profile, also the checks are executed (e.g. DeadLinksChecker).
#
./mvnw clean verify -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -P generate-rule-docs
# in order to prevent that this documentation job is deploying the binary distributions to github releases
# we delete it here
rm -f pmd-dist/target/pmd-*.zip
if ! travis_isPush; then
log_info "Not publishing site, since this is not a push!"
exit 0
fi
pushd docs
# run jekyll
echo -e "\n\n"
log_info "Building documentation using jekyll..."
bundle install
bundle exec jekyll build
# create pmd-doc archive
echo -e "\n\n"
log_info "Creating pmd-doc archive..."
mv _site pmd-doc-${VERSION}
zip -qr pmd-doc-${VERSION}.zip pmd-doc-${VERSION}/
log_success "Successfully created pmd-doc-${VERSION}.zip:"
ls -lh pmd-doc-${VERSION}.zip
(
# disable fast fail, exit immediately, in this subshell
set +e
if [[ "${TRAVIS_TAG}" != "" || "${VERSION}" == *-SNAPSHOT ]]; then
echo -e "\n\n"
log_info "Uploading pmd doc distribution to sourceforge..."
../.travis/travis_wait "rsync -avh pmd-doc-${VERSION}.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd/${VERSION}/"
if [ $? -ne 0 ]; then
log_error "Couldn't upload pmd-doc-${VERSION}.zip!"
log_error "Please upload manually: https://sourceforge.net/projects/pmd/files/pmd/"
else
log_success "Successfully uploaded pmd-doc-${VERSION}.zip to sourceforge"
fi
if ! travis_isPush; then
log_info "Not publishing site, since this is not a push!"
exit 0
fi
# rsync site to pmd.sourceforge.net/snapshot
if [[ "${VERSION}" == *-SNAPSHOT && "${TRAVIS_BRANCH}" == "master" ]] && has_docs_change; then
echo -e "\n\n"
log_info "Uploading snapshot site to pmd.sourceforge.net/snapshot..."
../.travis/travis_wait "rsync -ah --stats --delete pmd-doc-${VERSION}/ ${PMD_SF_USER}@web.sourceforge.net:/home/project-web/pmd/htdocs/snapshot/"
if [ $? -ne 0 ]; then
log_error "Couldn't upload the snapshot documentation. It won't be current on http://pmd.sourceforge.net/snapshot/"
else
log_success "Successfully uploaded snapshot documentation: http://pmd.sourceforge.net/snapshot/"
fi
#
# For pushes, we'll update the online documentation
#
generate_jekyll_doc
create_pmd-doc_archive
# Deploy to sourceforge files
sourceforge_uploadFile "${VERSION}" "docs/pmd-doc-${VERSION}.zip"
if [[ "${VERSION}" == *-SNAPSHOT && "${TRAVIS_BRANCH}" == "master" ]]; then
# only for snapshot builds from branch master
# update github pages https://pmd.github.io/pmd/
publish_to_github_pages
# rsync site to https://pmd.sourceforge.io/snapshot
sourceforge_rsyncSnapshotDocumentation "${VERSION}" "snapshot"
fi
true
)
if [[ "${VERSION}" != *-SNAPSHOT && "${TRAVIS_TAG}" != "" ]]; then
log_info "This is a release documentation build for pmd ${VERSION}"
# Deploy to github releases
gh_releases_getLatestDraftRelease
GH_RELEASE="$RESULT"
gh_release_uploadAsset "$GH_RELEASE" "docs/pmd-doc-${VERSION}.zip"
# updating github release text
# 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)
RELEASE_NAME="PMD ${VERSION} ($(date -u +%d-%B-%Y))"
gh_release_updateRelease "$GH_RELEASE" "$RELEASE_NAME" "$RENDERED_RELEASE_NOTES"
sourceforge_uploadReleaseNotes "${VERSION}" "${RENDERED_RELEASE_NOTES}"
publish_release_documentation_github
sourceforge_rsyncSnapshotDocumentation "${VERSION}" "pmd-${VERSION}"
fi
}
#
# Push the generated site to gh-pages branch
# only for snapshot builds from branch master
# Executes jekyll and generates the documentation
# The documentation will be generated in the directory "docs/_site".
#
if [[ "${VERSION}" == *-SNAPSHOT && "${TRAVIS_BRANCH}" == "master" ]] && has_docs_change; then
function generate_jekyll_doc() {
pushd docs
echo -e "\n\n"
log_info "Building documentation using jekyll..."
bundle install
bundle exec jekyll build
popd
}
#
# Creates the pmd-doc.zip archive. It will be placed in "docs/".
#
function create_pmd-doc_archive() {
pushd docs
echo -e "\n\n"
log_info "Creating pmd-doc archive..."
mv _site pmd-doc-${VERSION}
zip -qr pmd-doc-${VERSION}.zip pmd-doc-${VERSION}/
log_success "Successfully created pmd-doc-${VERSION}.zip"
popd
}
#
# Publishes the site to https://pmd.github.io/pmd-${VERSION} and
# https://pmd.github.io/latest/
#
function publish_release_documentation_github() {
echo -e "\n\n"
log_info "Adding the new doc to pmd.github.io..."
# clone pmd.github.io. Note: This uses the ssh key setup earlier
# In order to speed things up, we use a sparse checkout - no need to checkout all directories here
mkdir pmd.github.io
(
cd pmd.github.io
git init
git config user.name "Travis CI (pmd-bot)"
git config user.email "andreas.dangel+pmd-bot@adangel.org"
git config core.sparsecheckout true
git remote add origin git@github.com:pmd/pmd.github.io.git
echo "/latest/" > .git/info/sparse-checkout
echo "/sitemap.xml" >> .git/info/sparse-checkout
git pull --depth=1 origin master
log_info "Copying documentation from ../docs/pmd-doc-${VERSION}/ to pmd-${VERSION}/ ..."
rsync -ah --stats ../docs/pmd-doc-${VERSION}/ pmd-${VERSION}/
git status
echo "Executing: git add pmd-${VERSION}"
git add pmd-${VERSION}
echo "Executing: git commit..."
git commit -q -m "Added pmd-${VERSION}"
log_info "Copying pmd-${VERSION} to latest ..."
git rm -qr latest
cp -a pmd-${VERSION} latest
echo "Executing: git add latest"
git add latest
echo "Executing: git commit..."
git commit -q -m "Copying pmd-${VERSION} to latest"
log_info "Generating sitemap.xml"
../.travis/sitemap_generator.sh > sitemap.xml
echo "Executing: git add sitemap.xml"
git add sitemap.xml
echo "Executing: git commit..."
git commit -q -m "Generated sitemap.xml"
echo "Executing: git push origin master"
git push origin master
)
}
#
# Updates github pages of the main repository,
# so that https://pmd.github.io/pmd/ has the latest (snapshot) content
#
function publish_to_github_pages() {
echo -e "\n\n"
log_info "Pushing the new site to github pages..."
git clone --branch gh-pages --depth 1 git@github.com:pmd/pmd.git pmd-gh-pages
# clear the files first
rm -rf pmd-gh-pages/*
# copy the new site
cp -a pmd-doc-${VERSION}/* pmd-gh-pages/
cp -a docs/pmd-doc-${VERSION}/* pmd-gh-pages/
(
cd pmd-gh-pages
git config user.name "Travis CI (pmd-bot)"
@ -102,6 +165,6 @@ TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE}"
git push git@github.com:pmd/pmd.git HEAD:gh-pages
log_success "Successfully pushed site to https://pmd.github.io/pmd/"
)
fi
}
popd
main

28
.travis/build-publish.sh Normal file
View File

@ -0,0 +1,28 @@
#!/bin/bash
set -e
source .travis/logger.sh
source .travis/common-functions.sh
source .travis/github-releases-api.sh
source .travis/sourceforge-api.sh
VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec)
log_info "PMD Release ${VERSION}"
if ! travis_isPush; then
log_info "Not publishing release, since this is not a push!"
exit 0
fi
if [[ "${VERSION}" == *-SNAPSHOT || "${TRAVIS_TAG}" == "" ]]; then
log_info "Not publishing release, since this is not a release!"
exit 0
fi
# Publish github release
gh_releases_getLatestDraftRelease
GH_RELEASE="$RESULT"
gh_release_publishRelease "$GH_RELEASE"
sourceforge_selectDefault "${VERSION}"

View File

@ -53,14 +53,3 @@ function travis_isWindows() {
return 1
fi
}
function has_docs_change() {
if [[ $(git diff --name-only ${TRAVIS_COMMIT_RANGE}) = *"docs/"* ]]; then
log_info "Checking for changes in docs/ (TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE}): changes found"
return 0
else
log_info "Checking for changes in docs/ (TRAVIS_COMMIT_RANGE=${TRAVIS_COMMIT_RANGE}): no changes"
return 1
fi
}

View File

@ -0,0 +1,200 @@
#
# The functions here require the following scripts:
# .travis/logger.sh
#
# The functions here require the following environment variables:
# GITHUB_OAUTH_TOKEN
#
#
# Creates a new release on github with the given tag and target_commit.
# The release is draft and not published.
#
# $RESULT = release json string
#
# See: https://developer.github.com/v3/repos/releases/#create-a-release
#
function gh_releases_createDraftRelease() {
local tagName="$1"
local targetCommitish="$2"
log_debug "$FUNCNAME: Creating new draft release for tag=$tagName and commit=$targetCommitish"
local request=$(cat <<-EOF
{
"tag_name": "${tagName}",
"target_commitish": "${targetCommitish}",
"name": "${tagName}",
"draft": true
}
EOF
)
log_debug "POST https://api.github.com/repos/pmd/pmd/releases"
log_info "Creating gihtub draft release"
RESULT=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
-H "Content-Type: application/json" \
-X POST \
--data "${request}" \
"https://api.github.com/repos/pmd/pmd/releases")
log_debug " -> response: $RESULT"
log_success "Created draft release with id $(echo $RESULT | jq --raw-output ".url")"
}
#
# Gets the latest release, if it is a draft and returns with 0.
# Returns with 1, if the latest release is not a draft - meaning, there is no
# draft release (yet?).
#
# RESULT = release json string
#
# See: https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository
#
function gh_releases_getLatestDraftRelease() {
log_debug "$FUNCNAME"
log_debug "GET https://api.github.com/repos/pmd/pmd/releases?per_page=1"
RESULT=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
"https://api.github.com/repos/pmd/pmd/releases?per_page=1" | jq ".[0]")
log_debug " -> response: $RESULT"
local draft=$(echo $RESULT | jq ".draft")
if [ "$draft" != "true" ]; then
RESULT=""
log_error "Could not find draft release!"
return 1
fi
log_info "Found draft release: $(echo $RESULT | jq --raw-output ".url")"
}
#
# Deletes a release.
#
# See: https://developer.github.com/v3/repos/releases/#delete-a-release
#
function gh_release_deleteRelease() {
local release="$1"
gh_release_getIdFromData "$release"
local releaseId="$RESULT"
log_debug "$FUNCNAME id=$releaseId"
log_debug "DELETE https://api.github.com/repos/pmd/pmd/releases/$releaseId"
log_info "Deleting github release $releaseId"
local response
response=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
-X DELETE \
"https://api.github.com/repos/pmd/pmd/releases/$releaseId")
log_debug " -> response: $response"
log_success "Deleted release with id $releaseId"
}
#
# Determines the release id from the given JSON release data.
#
# RESULT = "the release id"
#
function gh_release_getIdFromData() {
local release="$1"
RESULT=$(echo $release | jq --raw-output ".id")
}
#
# Uploads a asset to an existing release.
#
# See: https://developer.github.com/v3/repos/releases/#upload-a-release-asset
#
function gh_release_uploadAsset() {
local release="$1"
local filename="$2"
local name=$(basename $filename)
gh_release_getIdFromData "$release"
local releaseId="$RESULT"
log_debug "$FUNCNAME: releaseId=$releaseId file=$filename name=$name"
local uploadUrl=$(echo "$release" | jq --raw-output ".upload_url")
uploadUrl="${uploadUrl%%\{\?name,label\}}"
uploadUrl="${uploadUrl}?name=${name}"
log_debug "POST $uploadUrl"
log_info "Uploading $filename to github release $releaseId"
local response
response=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
-H "Content-Type: application/zip" \
--data-binary "@$filename" \
-X POST \
"${uploadUrl}")
log_debug " -> response: $response"
log_success "Uploaded release asset $filename for release $releaseId"
}
#
# Updates the release info: name and body.
# The body is escaped to fit into JSON, so it is allowed for the body to be
# a multi-line string.
#
# See: https://developer.github.com/v3/repos/releases/#edit-a-release
#
function gh_release_updateRelease() {
local release="$1"
local name="$2"
local body="$3"
gh_release_getIdFromData "$release"
local releaseId="$RESULT"
log_debug "$FUNCNAME releaseId=$releaseId name=$name"
body="${body//'\'/\\\\}"
body="${body//$'\r'/}"
body="${body//$'\n'/\\r\\n}"
body="${body//'"'/\\\"}"
local request=$(cat <<-EOF
{
"name": "${name}",
"body": "${body}"
}
EOF
)
log_debug "PATCH https://api.github.com/repos/pmd/pmd/releases/${releaseId}"
log_debug " -> request: $request"
log_info "Updating github release $releaseId"
local response
response=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
-H "Content-Type: application/json" \
--data "${request}" \
-X PATCH \
"https://api.github.com/repos/pmd/pmd/releases/${releaseId}")
log_debug " -> response: $response"
log_success "Updated release with id=$releaseId"
}
#
# Publish a release by setting draft="false".
# Note: This will send out the notification emails if somebody
# watched the releases.
#
# See: https://developer.github.com/v3/repos/releases/#edit-a-release
#
function gh_release_publishRelease() {
local release="$1"
gh_release_getIdFromData "$release"
local releaseId="$RESULT"
log_debug "$FUNCNAME releaseId=$releaseId"
local request='{"draft":false}'
log_debug "PATCH https://api.github.com/repos/pmd/pmd/releases/${releaseId}"
log_debug " -> request: $request"
log_info "Publishing github release $releaseId"
local response
response=$(curl --fail -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" \
-H "Content-Type: application/json" \
--data "${request}" \
-X PATCH \
"https://api.github.com/repos/pmd/pmd/releases/${releaseId}")
log_debug " -> response: $response"
local htmlUrl=$(echo "$response" | jq --raw-output ".html_url")
log_success "Published release with id=$releaseId at $htmlUrl"
}

View File

@ -6,13 +6,18 @@ COL_RESET="\e[0m"
COL_YELLOW="\e[33;1m"
function log_error() {
echo -e "${COL_RED}[ERROR] $*${COL_RESET}"
echo -e "${COL_RED}[ERROR ] $*${COL_RESET}"
}
function log_info() {
echo -e "${COL_YELLOW}[INFO] $*${COL_RESET}"
echo -e "${COL_YELLOW}[INFO ] $*${COL_RESET}"
}
function log_success() {
echo -e "${COL_GREEN}[SUCCESS] $*${COL_RESET}"
}
function log_debug() {
true
#echo -e "[DEBUG ] $*"
}

View File

@ -0,0 +1,79 @@
#
# The functions here require the following scripts:
# .travis/logger.sh
#
# The functions here require the following environment variables:
# PMD_SF_USER
#
# Generate a new baseline and upload it to sourceforge
#
# Note: this function always succeeds, even if the upload fails.
# In that case, just a error logging is provided.
#
function regression-tester_uploadBaseline() {
log_debug "$FUNCNAME branch=${TRAVIS_BRANCH}"
local targetUrl="https://sourceforge.net/projects/pmd/files/pmd-regression-tester/"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function upload_failed() {
log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!"
log_error "Please upload manually: ${targetUrl}"
}
# exit subshell after trap
set -e
trap upload_failed ERR
log_info "Generating and uploading baseline for pmdtester..."
cd ..
bundle config --local gemfile pmd/Gemfile
pmd/.travis/travis_wait "bundle exec 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}/
../../pmd/.travis/travis_wait "rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/"
log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${targetUrl}"
)
# restore errexit state
eval "$errexitstate"
}
#
# Execute danger, which executes pmd-regression-tester (via Dangerfile).
#
# Note: this function always succeeds, even if the danger fails.
# In that case, just a error logging is provided.
#
function regression-tester_executeDanger() {
log_debug "$FUNCNAME"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function danger_failed() {
log_error "Error while executing danger/pmd-regression-tester"
}
# exit subshell after trap
set -e
trap danger_failed ERR
# Create a corresponding remote branch locally
if ! git show-ref --verify --quiet refs/heads/${TRAVIS_BRANCH}; then
git fetch --no-tags origin +refs/heads/${TRAVIS_BRANCH}:refs/remotes/origin/${TRAVIS_BRANCH}
git branch ${TRAVIS_BRANCH} origin/${TRAVIS_BRANCH}
log_debug "Created local branch ${TRAVIS_BRANCH}"
fi
log_info "Running danger on branch ${TRAVIS_BRANCH}"
bundle exec danger --verbose
log_success "Executing danger successfully"
)
# restore errexit state
eval "$errexitstate"
}

View File

@ -1,135 +0,0 @@
#!/bin/bash
set -e
source .travis/logger.sh
echo "BUILD: $BUILD"
RELEASE_VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec | tail -1)
echo "RELEASE_VERSION: $RELEASE_VERSION"
if [ "${BUILD}" = "deploy" ]; then
# Deploy to ossrh has already been done with the usual build. See build-deploy.sh
(
# disable fast fail, exit immediately, in this subshell
set +e
# The site has been built before, the files have already been uploaded to sourceforge.
# Since this is a release, making the binary the new default file...
log_info "Selecting pmd-bin-${RELEASE_VERSION} as default on sourceforge.net..."
curl -H "Accept: application/json" -X PUT -d "default=windows&default=mac&default=linux&default=bsd&default=solaris&default=others" \
-d "api_key=${PMD_SF_APIKEY}" https://sourceforge.net/projects/pmd/files/pmd/${RELEASE_VERSION}/pmd-bin-${RELEASE_VERSION}.zip
if [ $? -ne 0 ]; then
log_error "Couldn't select latest binary as default on sourceforge.net"
else
log_info "pmd-bin-${RELEASE_VERSION} is now the default download option."
fi
true
)
fi
if [ "${BUILD}" = "doc" ]; then
echo -e "\n\n"
log_info "Adding the new doc to pmd.github.io..."
# clone pmd.github.io. Note: This uses the ssh key setup earlier
# In order to speed things up, we use a sparse checkout - no need to checkout all directories here
mkdir pmd.github.io
(
cd pmd.github.io
git init
git config user.name "Travis CI (pmd-bot)"
git config user.email "andreas.dangel+pmd-bot@adangel.org"
git config core.sparsecheckout true
git remote add origin git@github.com:pmd/pmd.github.io.git
echo "/latest/" > .git/info/sparse-checkout
echo "/sitemap.xml" >> .git/info/sparse-checkout
git pull --depth=1 origin master
log_info "Copying documentation from ../docs/pmd-doc-${RELEASE_VERSION}/ to pmd-${RELEASE_VERSION}/ ..."
rsync -ah --stats ../docs/pmd-doc-${RELEASE_VERSION}/ pmd-${RELEASE_VERSION}/
git status
echo "Executing: git add pmd-${RELEASE_VERSION}"
git add pmd-${RELEASE_VERSION}
echo "Executing: git commit..."
git commit -q -m "Added pmd-${RELEASE_VERSION}"
log_info "Copying pmd-${RELEASE_VERSION} to latest ..."
git rm -qr latest
cp -a pmd-${RELEASE_VERSION} latest
echo "Executing: git add latest"
git add latest
echo "Executing: git commit..."
git commit -q -m "Copying pmd-${RELEASE_VERSION} to latest"
log_info "Generating sitemap.xml"
../.travis/sitemap_generator.sh > sitemap.xml
echo "Executing: git add sitemap.xml"
git add sitemap.xml
echo "Executing: git commit..."
git commit -q -m "Generated sitemap.xml"
echo "Executing: git push origin master"
git push origin master
)
# 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="$RENDERED_RELEASE_NOTES"
RELEASE_BODY="${RELEASE_BODY//'\'/\\\\}"
RELEASE_BODY="${RELEASE_BODY//$'\r'/}"
RELEASE_BODY="${RELEASE_BODY//$'\n'/\\r\\n}"
RELEASE_BODY="${RELEASE_BODY//'"'/\\\"}"
cat > release-edit-request.json <<EOF
{
"name": "$RELEASE_NAME",
"body": "$RELEASE_BODY",
"draft": false
}
EOF
echo -e "\n\n"
log_info "Updating and publishing release at https://api.github.com/repos/pmd/pmd/releases/${RELEASE_ID}..."
RESPONSE=$(curl -i -s -H "Authorization: token ${GITHUB_OAUTH_TOKEN}" -H "Content-Type: application/json" --data "@release-edit-request.json" -X PATCH https://api.github.com/repos/pmd/pmd/releases/${RELEASE_ID})
if [[ "$RESPONSE" != *"HTTP/1.1 200"* ]]; then
log_error "Github Request failed!"
echo "Request:"
cat release-edit-request.json
echo
echo "Response:"
echo "$RESPONSE"
else
log_success "Update OK"
fi
(
echo -e "\n\n"
# disable fast fail, exit immediately, in this subshell
set +e
log_info "Uploading the new release to pmd.sourceforge.net which serves as an archive..."
.travis/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..."
log_error "Please upload manually (PMD Version: ${RELEASE_VERSION})"
else
log_success "The documentation is now available under http://pmd.sourceforge.net/pmd-${RELEASE_VERSION}/"
fi
true
)
fi

View File

@ -1,6 +1,9 @@
#!/bin/bash
set -e
source .travis/logger.sh
source .travis/common-functions.sh
if [ "${TRAVIS_REPO_SLUG}" != "pmd/pmd" ] || [ "${TRAVIS_PULL_REQUEST}" != "false" ] || [ "${TRAVIS_SECURE_ENV_VARS}" != "true" ] || [ "${encrypted_5630fbebf057_iv}" = "" ]; then
echo "Not setting up secrets:"
echo " TRAVIS_REPO_SLUG=${TRAVIS_REPO_SLUG}"
@ -18,8 +21,17 @@ mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
mv .travis/id_rsa "$HOME/.ssh/id_rsa"
chmod 600 "$HOME/.ssh/id_rsa"
mkdir -p "$HOME/.gpg"
gpg --batch --import .travis/release-signing-key-82DE7BE82166E84E.gpg
if travis_isLinux; then
mkdir -p "$HOME/.gpg"
gpg --batch --import .travis/release-signing-key-82DE7BE82166E84E.gpg
else
log_info "Not setting up gpg for ${TRAVIS_OS_NAME}."
# Note: importing keys into gpg will start gpg-agent. This background task then
# prevents travis-ci from terminating the build job under Windows.
# Alternatively "gpgconf --kill gpg-agent" can be executed to stop the
# gpg-agent at the end, if the gpg keys are needed.
fi
rm .travis/secrets.tar
rm .travis/release-signing-key-82DE7BE82166E84E.gpg

166
.travis/sourceforge-api.sh Normal file
View File

@ -0,0 +1,166 @@
#
# The functions here require the following scripts:
# .travis/logger.sh
#
# The functions here require the following environment variables:
# PMD_SF_USER
# PMD_SF_APIKEY
#
#
# Uploads the release notes to sourceforge files as "ReadMe.md".
#
# Note: this function always succeeds, even if the upload fails.
# In that case, just a error logging is provided.
#
function sourceforge_uploadReleaseNotes() {
local pmdVersion="$1"
local releaseNotes="$2"
log_debug "$FUNCNAME pmdVersion=$pmdVersion"
local targetUrl="https://sourceforge.net/projects/pmd/files/pmd/${pmdVersion}"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function release_notes_fail() {
log_error "Error while uploading release notes as ReadMe.md to sourceforge!"
log_error "Please upload manually: ${targetUrl}"
cleanup_temp_dir
}
function cleanup_temp_dir() {
log_debug "Cleanup tempdir $releaseNotesTempDir"
rm "${releaseNotesTempDir}/${pmdVersion}/ReadMe.md" || true
rmdir "${releaseNotesTempDir}/${pmdVersion}" || true
rmdir "${releaseNotesTempDir}" || true
}
# exit subshell after trap
set -e
trap release_notes_fail ERR
local releaseNotesTempDir=$(mktemp -d)
log_debug "Tempdir: $releaseNotesTempDir"
mkdir -p "${releaseNotesTempDir}/${pmdVersion}"
echo "$releaseNotes" > "${releaseNotesTempDir}/${pmdVersion}/ReadMe.md"
log_info "Uploading release notes to sourceforge for version $pmdVersion"
rsync -avz \
"${releaseNotesTempDir}/" \
"${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd/"
log_success "Successfully uploaded release notes as ReadMe.md to sourceforge: ${targetUrl}"
cleanup_temp_dir
)
# restore errexit state
eval "$errexitstate"
}
#
# Uploads the given file to sourceforge.
#
# Note: This function always succeeds, even if the upload fails.
# In that case, just a error logging is provided.
#
function sourceforge_uploadFile() {
local pmdVersion="$1"
local filename="$2"
log_debug "$FUNCNAME pmdVersion=$pmdVersion filename=$filename"
local targetUrl="https://sourceforge.net/projects/pmd/files/pmd/${pmdVersion}"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function upload_failed() {
log_error "Error while uploading ${filename} to sourceforge!"
log_error "Please upload manually: ${targetUrl}"
}
# exit subshell after trap
set -e
trap upload_failed ERR
log_info "Uploading $filename to sourceforge..."
.travis/travis_wait "rsync -avh ${filename} ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd/${pmdVersion}/"
log_success "Successfully uploaded ${filename} to sourceforge: ${targetUrl}"
)
# restore errexit state
eval "$errexitstate"
}
#
# Select the given version as the new default download.
#
# Note: This function always succeeds, even if the request fails.
# In that case, just a error logging is provided.
#
function sourceforge_selectDefault() {
local pmdVersion="$1"
log_debug "$FUNCNAME pmdVersion=$pmdVersion"
local targetUrl="https://sourceforge.net/projects/pmd/files/pmd/${pmdVersion}"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function request_failed() {
log_error "Error while selecting ${pmdVersion} as new default download on sourceforge!"
log_error "Please do it manually: ${targetUrl}"
}
# exit subshell after trap
set -e
trap request_failed ERR
log_info "Selecting $pmdVersion as new default on sourceforge..."
local response
response=$(curl --fail -s -H "Accept: application/json" \
-X PUT \
-d "api_key=${PMD_SF_APIKEY}" \
-d "default=windows&default=mac&default=linux&default=bsd&default=solaris&default=others" \
"https://sourceforge.net/projects/pmd/files/pmd/${pmdVersion}/pmd-bin-${pmdVersion}.zip")
log_debug " -> response: $response"
log_success "Successfully selected $pmdVersion as new default on sourceforge: ${targetUrl}"
)
# restore errexit state
eval "$errexitstate"
}
#
# Rsyncs the complete documentation to sourceforge.
#
# Note: This function always succeeds, even if the upload fails.
# In that case, just a error logging is provided.
#
function sourceforge_rsyncSnapshotDocumentation() {
local pmdVersion="$1"
local targetPath="$2"
log_debug "$FUNCNAME pmdVersion=$pmdVersion targetPath=$targetPath"
local targetUrl="https://pmd.sourceforge.io/${targetPath}/"
local errexitstate="$(shopt -po errexit)"
set +e # disable errexit
(
# This handler is called if any command fails
function upload_failed() {
log_error "Couldn't upload the documentation. It won't be current on ${targetUrl}"
}
# exit subshell after trap
set -e
trap upload_failed ERR
log_info "Uploading documentation to ${targetUrl}..."
.travis/travis_wait "rsync -ah --stats --delete docs/pmd-doc-${VERSION}/ ${PMD_SF_USER}@web.sourceforge.net:/home/project-web/pmd/htdocs/snapshot/"
log_success "Successfully uploaded documentation: ${targetUrl}"
)
# restore errexit state
eval "$errexitstate"
}

View File

@ -1,9 +1,9 @@
repository: pmd/pmd
pmd:
version: 6.20.0
previous_version: 6.19.0
date: ??-November-2019
version: 6.21.0
previous_version: 6.20.0
date: ??-December-2019
release_type: minor
# release types: major, minor, bugfix

View File

@ -352,6 +352,9 @@ entries:
- title: Pmdtester
url: /pmd_devdocs_pmdtester.html
output: web, pdf
- title: Rule Deprecation Policy
url: /pmd_devdocs_rule_deprecation_policy.html
output: web, pdf
- title: null
output: web, pdf
subfolders:

View File

@ -193,6 +193,10 @@ the breaking API changes will be performed in 7.0.0.
an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0,
we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %}
#### 6.20.0
No changes.
#### 6.19.0
##### Deprecated APIs

View File

@ -0,0 +1,106 @@
---
title: Rule deprecation policy
tags: [devdocs]
summary: Describes when and how rules are deprecated
last_updated: November 15, 2019
permalink: pmd_devdocs_rule_deprecation_policy.html
author: Andreas Dangel
---
When improving PMD over time, some rules might become obsolete. This could be because the underlying
technology a specific rule is checking (such as a specific JVM version) is not relevant anymore or a rule
has been replaced by a better implementation.
In order to remove the requirement to maintain such rules forever, these rules can be marked as **deprecated**.
This means, that such rules can entirely be removed in the future.
However, the rules must not be removed immediately, since that would break any (custom) ruleset, that
references this rule.
This policy tries to establish some ground rules about how and when rules are deprecated and removed.
The main goal is, to maintain compatibility of custom rulesets throughout the deprecation process.
## Renaming rules
If a rule is enhanced, it might make sense to also rename the rule, to reflect that enhancement. However,
simply renaming the rule would break existing (custom) ruleset. Therefore the following procedure should be used:
* Rename the rule to the new name (and also the rule tests and resources)
* Add a deprecated rule reference with the old name, that points to the new rule name:
`<rule name="OldRuleName" ref="NewRuleName" deprecated="true" />`
*Note:* When referencing the complete rulesets or categories,
these deprecated rule references are ignored, so that the rule is not used twice.
## Moving rules between categories
Every rule is in one category. It might happen, that the focus of the rule shifts and it makes more
sense, if it would be in a different, better fitting category.
* Move the rule to the new category (and also the rule tests and resources)
* Add a deprecated rule reference in the old category, that points to the rule in the new category:
`<rule name="MyRule" ref="category/java/errorprone.xml/MyRule" deprecated="true" />`
*Note:* When referencing the complete rulesets or categories,
these deprecated rule references are ignored, so that the rule is not used twice, if both categories
are used.
## Deprecating rules
Before a rule can be removed, it must have been marked as deprecated:
```
<rule name="MyRule" class="...." deprecated="true">
...
</rule>
```
This has the effect, that it is **automatically disabled** if the complete ruleset or category
is referenced. The rule can still be used, if it is referenced directly.
The reasons for the deprecation should be explained in the rule description. If there is a replacement rule
available, then this rule should be mentioned in the description as well.
## Removing rules
Removing rules completely can only be done
* if the rules have been deprecated before
* for a new **major** release.
Removing a rule from a ruleset or category will break any custom ruleset, that references
this rule directly. Therefore rules can only be removed with the next major release of PMD.
## Rule property compatibility
Renaming or removing rule properties is not backwards compatible and can only be done
with a major release of PMD.
In order to prepare for the change, properties can be deprecated as well: If the property description
starts with the magic string `deprecated!`, then this property is rendered in the rule documentation
as deprecated. However, there is no automatic check done if such a property is used and no
deprecation warning is issued with the log.
Therefore, the process for **renaming a property** looks like this:
* Create a new property with the same type and new name
* Prefix the description of the old property with `deprecated!` and also add a explanation
either in the property description or in the rule description, which property should be used
instead of the deprecated property.
* Adjust the rule implementation to first check the old property. If it has a value other than the
default value, then the old (deprecated) property has been used and a deprecation warning should
be logged. If the new property is used (it has a value other than the default), then it takes
preference, but the deprecation warning for the old property should still be issued.
* The deprecated property can be removed with the next major release of PMD.
**Changing the default value** of a property might have some results, that make the rule
behavioral incompatible: E.g. it could find many more violations with a different default
configuration and therefore lead to a sudden increase of violations after a PMD upgrade.
It should be judged per case, whether the new default can be considered compatible or not.
If it is not compatible, then the new default value should be configured only with the next
major release of PMD.

View File

@ -18,7 +18,7 @@ author: Tom Copeland <tom@infoether.org>
* <a href="http://www.vanwardtechnologies.com/products.php">Vanward Technology's Convergence [link broken]</a> -
a quality management dashboard for the Java platform. Thanks to Vanward Technologies for their support of PMD
by buying copies of <a href="http://pmdapplied.com/">PMD Applied</a>!
by buying copies of <a href="https://pmdapplied.thomasleecopeland.com/">PMD Applied</a>!
* <a href="http://javacentral.compuware.com/products/optimaladvisor/">Compuware's OptimalAdvisor [link broken]</a>
<a href="http://www.componentsource.com/beasv/products/compuware-optimaladvisor/index.html">OptimalAdvisor from ComponentSource</a> -
@ -26,8 +26,9 @@ author: Tom Copeland <tom@infoether.org>
## Books that mention PMD
* <a href="http://pmdapplied.com">PMD Applied</a> - the official manual; get an extra copy for the home!
Or office! Whatever!
* <a href="https://pmdapplied.thomasleecopeland.com/">PMD Applied</a> - the official manual by Tom Copeland.
*Note:* This book is from November 2005 and quite outdated. It discusses PMD 3.
* <a href="http://www.oreilly.com/catalog/9780596510237">Checking Java Programs</a> - by Ian Darwin;
also discusses FindBugs.

View File

@ -324,8 +324,7 @@ Ive found that my rules usually dont work the first time, and so I have to
As an acceptance test of sorts, I usually run a rule on the JDK 1.4 source code and make sure that a random sampling of the problems found are in fact legitimate rule violations. This also ensures that the rule doesnt get confused by nested inner classes or any of the other oddities that appear at various points in the JDK source.
Youre rolling now. If you think a rule would benefit the Java development community as a whole, post a message to [the forum](http://sourceforge.net/p/pmd/discussion/188192) so we can get the rule moved into one of the core rulesets.
Youre rolling now. If you think a rule would benefit the (Java) development community as a whole,
create a [issue on github](https://github.com/pmd/pmd/issues) so we can get the rule moved into one of the core rulesets.
Or, if you can improve one of the existing rules, thatd be great too! Thanks!
Finally, for many more details on writing rules, pick up [PMD Applied](http://pmdapplied.com/)!

View File

@ -196,6 +196,3 @@ public class ExampleCode {
</ruleset>
```
Finally, for many more details on writing XPath rules, pick up [PMD Applied](http://pmdapplied.com/)!

View File

@ -21,22 +21,30 @@ This is a {{ site.pmd.release_type }} release.
### Fixed Issues
* apex
* [#2092](https://github.com/pmd/pmd/issues/2092): \[apex] ApexLexer logs visible when Apex is the selected language upon starting the designer
* core
* [#2096](https://github.com/pmd/pmd/issues/2096): \[core] Referencing category errorprone.xml produces deprecation warnings for InvalidSlf4jMessageFormat
* java
* [#1861](https://github.com/pmd/pmd/issues/1861): \[java] Be more lenient with version numbers
* [#2105](https://github.com/pmd/pmd/issues/2105): \[java] Wrong name for inner classes in violations
### API Changes
#### Deprecated APIs
##### Internal API
* {% jdoc java::lang.java.JavaLanguageHandler %}
* {% jdoc java::lang.java.JavaLanguageParser %}
* {% jdoc java::lang.java.JavaDataFlowHandler %}
* Implementations of {% jdoc core::lang.rule.RuleViolationFactory %} in each
language module, eg {% jdoc java::lang.java.rule.JavaRuleViolationFactory %}.
See javadoc of {% jdoc core::lang.rule.RuleViolationFactory %}.
* Implementations of {% jdoc core::RuleViolation %} in each language module,
eg {% jdoc java::lang.java.rule.JavaRuleViolation %}. See javadoc of
{% jdoc core::RuleViolation %}.
##### For removal
* {% jdoc java::lang.java.AbstractJavaParser %}
* {% jdoc java::lang.java.AbstractJavaHandler %}
### External Contributions
* [#2088](https://github.com/pmd/pmd/pull/2088): \[java] Add more version shortcuts for older java - [Henning Schmiedehausen](https://github.com/hgschmie)
* [#2089](https://github.com/pmd/pmd/pull/2089): \[core] Minor unrelated improvements to code - [Gonzalo Exequiel Ibars Ingman](https://github.com/gibarsin)
* [#2091](https://github.com/pmd/pmd/pull/2091): \[core] Fix pmd warnings (IdenticalCatchCases) - [Gonzalo Exequiel Ibars Ingman](https://github.com/gibarsin)
* [#2106](https://github.com/pmd/pmd/pull/2106): \[java] Wrong name for inner classes - [Andi Pabst](https://github.com/andipabst)
{% endtocmaker %}

View File

@ -5,6 +5,50 @@ permalink: pmd_release_notes_old.html
Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases
## 29-November-2019 - 6.20.0
The PMD team is pleased to announce PMD 6.20.0.
This is a minor release.
### Table Of Contents
* [Fixed Issues](#fixed-issues)
* [External Contributions](#external-contributions)
### Fixed Issues
* apex
* [#2092](https://github.com/pmd/pmd/issues/2092): \[apex] ApexLexer logs visible when Apex is the selected language upon starting the designer
* [#2136](https://github.com/pmd/pmd/issues/2136): \[apex] Provide access to underlying query of SoqlExpression
* core
* [#2002](https://github.com/pmd/pmd/issues/2002): \[doc] Issue with http://pmdapplied.com/ linking to a gambling Web site
* [#2062](https://github.com/pmd/pmd/issues/2062): \[core] Shortnames parameter does not work with Ant
* [#2090](https://github.com/pmd/pmd/issues/2090): \[ci] Release notes and draft releases
* [#2096](https://github.com/pmd/pmd/issues/2096): \[core] Referencing category errorprone.xml produces deprecation warnings for InvalidSlf4jMessageFormat
* java
* [#1861](https://github.com/pmd/pmd/issues/1861): \[java] Be more lenient with version numbers
* [#2105](https://github.com/pmd/pmd/issues/2105): \[java] Wrong name for inner classes in violations
* java-bestpractices
* [#2016](https://github.com/pmd/pmd/issues/2016): \[java] UnusedImports: False positive if wildcard is used and only static methods
* java-codestyle
* [#1362](https://github.com/pmd/pmd/issues/1362): \[java] LinguisticNaming flags Predicates with boolean-style names
* [#2029](https://github.com/pmd/pmd/issues/2029): \[java] UnnecessaryFullyQualifiedName false-positive for non-static nested classes
* [#2098](https://github.com/pmd/pmd/issues/2098): \[java] UnnecessaryFullyQualifiedName: regression / false positive
* java-design
* [#2075](https://github.com/pmd/pmd/issues/2075): \[java] ImmutableField false positive with inner class
* [#2125](https://github.com/pmd/pmd/issues/2125): \[java] ImmutableField: False positive when variable is updated in conditional loop
* java-errorprone
* [#2102](https://github.com/pmd/pmd/issues/2102): \[java] False positive MissingStaticMethodInNonInstantiatableClass when inheritors are instantiable
### External Contributions
* [#2088](https://github.com/pmd/pmd/pull/2088): \[java] Add more version shortcuts for older java - [Henning Schmiedehausen](https://github.com/hgschmie)
* [#2089](https://github.com/pmd/pmd/pull/2089): \[core] Minor unrelated improvements to code - [Gonzalo Exequiel Ibars Ingman](https://github.com/gibarsin)
* [#2091](https://github.com/pmd/pmd/pull/2091): \[core] Fix pmd warnings (IdenticalCatchCases) - [Gonzalo Exequiel Ibars Ingman](https://github.com/gibarsin)
* [#2106](https://github.com/pmd/pmd/pull/2106): \[java] Wrong name for inner classes - [Andi Pabst](https://github.com/andipabst)
* [#2121](https://github.com/pmd/pmd/pull/2121): \[java] Predicates treated like booleans - [Ozan Gulle](https://github.com/ozangulle)
## 31-October-2019 - 6.19.0
The PMD team is pleased to announce PMD 6.19.0.
@ -7630,5 +7674,3 @@ The binary package still contains all languages and can be used as usual. Have a
## June 25 2002 - 0.1:
Initial release
The PMD book - $20 - http://pmdapplied.com/

View File

@ -11,7 +11,6 @@ import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ParserOptions;
import net.sourceforge.pmd.lang.VisitorStarter;
import net.sourceforge.pmd.lang.XPathHandler;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
@ -19,8 +18,7 @@ import net.sourceforge.pmd.lang.apex.metrics.ApexMetricsComputer;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey;
import net.sourceforge.pmd.lang.apex.multifile.ApexMultifileVisitorFacade;
import net.sourceforge.pmd.lang.apex.rule.ApexRuleViolationFactory;
import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler;
import net.sourceforge.pmd.lang.apex.rule.internal.ApexRuleViolationFactory;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
@ -35,12 +33,6 @@ public class ApexHandler extends AbstractPmdLanguageVersionHandler {
return rootNode -> new ApexMultifileVisitorFacade().initializeWith((ApexNode<?>) rootNode);
}
@Override
public XPathHandler getXPathHandler() {
return new DefaultASTXPathHandler();
}
@Override
public RuleViolationFactory getRuleViolationFactory() {
return ApexRuleViolationFactory.INSTANCE;

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.apex;
import java.io.Reader;
import java.util.Map;
import net.sourceforge.pmd.lang.AbstractParser;
import net.sourceforge.pmd.lang.ParserOptions;
@ -34,8 +33,4 @@ public class ApexParser extends AbstractParser {
return apexParser.parse(source);
}
@Override
public Map<Integer, String> getSuppressMap() {
return apexParser.getSuppressMap();
}
}

View File

@ -16,4 +16,8 @@ public class ASTSoqlExpression extends AbstractApexNode<SoqlExpression> {
public Object jjtAccept(ApexParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public String getQuery() {
return getNode().getRawQuery();
}
}

View File

@ -4,6 +4,9 @@
package net.sourceforge.pmd.lang.apex.ast;
import java.util.Collections;
import java.util.Map;
import net.sourceforge.pmd.lang.ast.RootNode;
import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
@ -11,6 +14,9 @@ import apex.jorje.semantic.ast.AstNode;
import apex.jorje.services.Version;
public abstract class ApexRootNode<T extends AstNode> extends AbstractApexNode<T> implements RootNode {
private Map<Integer, String> noPmdComments = Collections.emptyMap();
public ApexRootNode(T node) {
super(node);
}
@ -32,4 +38,14 @@ public abstract class ApexRootNode<T extends AstNode> extends AbstractApexNode<T
public double getApexVersion() {
return getNode().getDefiningType().getCodeUnitDetails().getVersion().getExternal();
}
@Override
public Map<Integer, String> getNoPmdComments() {
return noPmdComments;
}
void setNoPmdComments(Map<Integer, String> noPmdComments) {
this.noPmdComments = noPmdComments;
}
}

View File

@ -1,69 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.apex.ast.CanSuppressWarnings;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
/**
* This is an Apex RuleViolation. It knows how to try to extract the following
* extra information from the violation node:
* <ul>
* <li>Package name</li>
* <li>Class name</li>
* <li>Method name</li>
* <li>Variable name</li>
* <li>Suppression indicator</li>
* </ul>
* @param <T>
*/
@SuppressWarnings("PMD.UseUtilityClass") // we inherit non-static methods...
public class ApexRuleViolation<T> extends ParametricRuleViolation<Node> {
public ApexRuleViolation(Rule rule, RuleContext ctx, Node node, String message, int beginLine, int endLine) {
this(rule, ctx, node, message);
setLines(beginLine, endLine);
}
public ApexRuleViolation(Rule rule, RuleContext ctx, Node node, String message) {
super(rule, ctx, node, message);
if (node != null) {
if (!suppressed) {
suppressed = isSupressed(node, getRule());
}
}
}
/**
* Check for suppression on this node, on parents, and on contained types
* for ASTCompilationUnit
*
* @deprecated Is internal API, not useful, there's a typo. See <a href="https://github.com/pmd/pmd/pull/1927">#1927</a>
*/
@Deprecated
public static boolean isSupressed(Node node, Rule rule) {
boolean result = suppresses(node, rule);
if (!result) {
Node parent = node.jjtGetParent();
while (!result && parent != null) {
result = suppresses(parent, rule);
parent = parent.jjtGetParent();
}
}
return result;
}
private static boolean suppresses(final Node node, Rule rule) {
return node instanceof CanSuppressWarnings
&& ((CanSuppressWarnings) node).hasSuppressWarningsAnnotationFor(rule);
}
}

View File

@ -1,32 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRuleViolationFactory;
public final class ApexRuleViolationFactory extends AbstractRuleViolationFactory {
public static final ApexRuleViolationFactory INSTANCE = new ApexRuleViolationFactory();
private ApexRuleViolationFactory() {
}
@SuppressWarnings("rawtypes")
@Override
protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message) {
return new ApexRuleViolation<>(rule, ruleContext, node, message);
}
@Override
@SuppressWarnings("rawtypes")
protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message,
int beginLine, int endLine) {
return new ApexRuleViolation(rule, ruleContext, node, message, beginLine, endLine);
}
}

View File

@ -0,0 +1,71 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.internal;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.ViolationSuppressor;
import net.sourceforge.pmd.lang.apex.ast.CanSuppressWarnings;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
public final class ApexRuleViolationFactory extends DefaultRuleViolationFactory {
public static final ApexRuleViolationFactory INSTANCE = new ApexRuleViolationFactory();
private static final ViolationSuppressor APEX_ANNOT_SUPPRESSOR = new ViolationSuppressor() {
@Override
public String getId() {
return "@SuppressWarnings";
}
@Override
public Report.SuppressedViolation suppressOrNull(RuleViolation rv, @NonNull Node node) {
if (isSuppressed(node, rv.getRule())) {
return new SuppressedViolation(rv, this, null);
}
return null;
}
};
private ApexRuleViolationFactory() {
}
@Override
protected List<ViolationSuppressor> getSuppressors() {
return Collections.singletonList(APEX_ANNOT_SUPPRESSOR);
}
/**
* Check for suppression on this node, on parents, and on contained types
* for ASTCompilationUnit
*
* @param node
*/
private static boolean isSuppressed(Node node, Rule rule) {
boolean result = suppresses(node, rule);
if (!result) {
Node parent = node.jjtGetParent();
while (!result && parent != null) {
result = suppresses(parent, rule);
parent = parent.jjtGetParent();
}
}
return result;
}
private static boolean suppresses(final Node node, Rule rule) {
return node instanceof CanSuppressWarnings
&& ((CanSuppressWarnings) node).hasSuppressWarningsAnnotationFor(rule);
}
}

View File

@ -0,0 +1,22 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.ast;
import static net.sourceforge.pmd.lang.apex.ast.ApexParserTestHelpers.parse;
import org.junit.Assert;
import org.junit.Test;
import apex.jorje.semantic.ast.compilation.Compilation;
public class ASTSoqlExpressionTest {
@Test
public void testQuery() {
ApexNode<Compilation> node = parse("class Foo { void test1() { Account acc = [SELECT 1 FROM Account]; } }");
ASTSoqlExpression soqlExpression = node.getFirstDescendantOfType(ASTSoqlExpression.class);
Assert.assertEquals("SELECT 1 FROM Account", soqlExpression.getQuery());
}
}

View File

@ -42,7 +42,6 @@ public class Report implements Iterable<RuleViolation> {
private final List<ThreadSafeReportListener> listeners = new ArrayList<>();
private List<ProcessingError> errors;
private List<ConfigurationError> configErrors;
private Map<Integer, String> linesToSuppress = new HashMap<>();
private long start;
private long end;
private List<SuppressedViolation> suppressedRuleViolations = new ArrayList<>();
@ -177,72 +176,6 @@ public class Report implements Iterable<RuleViolation> {
}
}
/**
* Represents a violation, that has been suppressed.
*/
public static class SuppressedViolation {
private final RuleViolation rv;
private final boolean isNOPMD;
private final String userMessage;
/**
* Creates a suppressed violation.
*
* @param rv
* the actual violation, that has been suppressed
* @param isNOPMD
* the suppression mode: <code>true</code> if it is
* suppressed via a NOPMD comment, <code>false</code> if
* suppressed via annotations.
* @param userMessage
* contains the suppressed code line or <code>null</code>
*/
public SuppressedViolation(RuleViolation rv, boolean isNOPMD, String userMessage) {
this.isNOPMD = isNOPMD;
this.rv = rv;
this.userMessage = userMessage;
}
/**
* Returns <code>true</code> if the violation has been suppressed via a
* NOPMD comment.
*
* @return <code>true</code> if the violation has been suppressed via a
* NOPMD comment.
*/
public boolean suppressedByNOPMD() {
return this.isNOPMD;
}
/**
* Returns <code>true</code> if the violation has been suppressed via a
* annotation.
*
* @return <code>true</code> if the violation has been suppressed via a
* annotation.
*/
public boolean suppressedByAnnotation() {
return !this.isNOPMD;
}
public RuleViolation getRuleViolation() {
return this.rv;
}
public String getUserMessage() {
return userMessage;
}
}
/**
* Configure the lines, that are suppressed via a NOPMD comment.
*
* @param lines
* the suppressed lines
*/
public void suppress(Map<Integer, String> lines) {
linesToSuppress = lines;
}
private static String keyFor(RuleViolation rv) {
@ -301,6 +234,46 @@ public class Report implements Iterable<RuleViolation> {
return suppressedRuleViolations;
}
/**
* Represents a violation, that has been suppressed.
* TODO this should implement RuleViolation
*/
public static class SuppressedViolation {
private final RuleViolation rv;
private final String userMessage;
private final ViolationSuppressor suppressor;
/**
* Creates a suppressed violation.
*
* @param rv The violation, that has been suppressed
* @param suppressor The suppressor which suppressed the violation
* @param userMessage Any relevant info given by the suppressor
*/
public SuppressedViolation(RuleViolation rv, ViolationSuppressor suppressor, String userMessage) {
this.suppressor = suppressor;
this.rv = rv;
this.userMessage = userMessage;
}
public ViolationSuppressor getSuppressor() {
return suppressor;
}
public RuleViolation getRuleViolation() {
return this.rv;
}
public String getUserMessage() {
return userMessage;
}
}
public void addSuppressedViolation(SuppressedViolation sv) {
suppressedRuleViolations.add(sv);
}
/**
* Adds a new rule violation to the report and notify the listeners.
*
@ -308,19 +281,6 @@ public class Report implements Iterable<RuleViolation> {
* the violation to add
*/
public void addRuleViolation(RuleViolation violation) {
// NOPMD suppress
int line = violation.getBeginLine();
if (linesToSuppress.containsKey(line)) {
suppressedRuleViolations.add(new SuppressedViolation(violation, true, linesToSuppress.get(line)));
return;
}
if (violation.isSuppressed()) {
suppressedRuleViolations.add(new SuppressedViolation(violation, false, null));
return;
}
int index = Collections.binarySearch(violations, violation, RuleViolationComparator.INSTANCE);
violations.add(index < 0 ? -index - 1 : index, violation);
violationTree.addRuleViolation(violation);

View File

@ -6,7 +6,11 @@ package net.sourceforge.pmd;
/**
* A RuleViolation is created by a Rule when it identifies a violation of the
* Rule constraints.
* Rule constraints. RuleViolations are simple data holders that are collected
* into a {@link Report}.
*
* <p>Since PMD 6.21.0, implementations of this interface are considered internal
* API and hence deprecated. Clients should exclusively use this interface.
*
* @see Rule
*/
@ -26,13 +30,6 @@ public interface RuleViolation {
*/
String getDescription();
/**
* Indicates whether this violation has been suppressed.
*
* @return <code>true</code> if this violation is suppressed,
* <code>false</code> otherwise.
*/
boolean isSuppressed();
/**
* Get the source file name in which this violation was identified.

Some files were not shown because too many files have changed in this diff Show More