Merge branch 'pmd/7.0.x' into pr-2022

This commit is contained in:
Andreas Dangel
2020-02-18 19:04:25 +01:00
1138 changed files with 16745 additions and 15068 deletions

117
.mvn/wrapper/MavenWrapperDownloader.java vendored Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

BIN
.mvn/wrapper/maven-wrapper.jar vendored Executable file → Normal file

Binary file not shown.

3
.mvn/wrapper/maven-wrapper.properties vendored Executable file → Normal file
View File

@ -1 +1,2 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@ -10,6 +10,7 @@ env:
- secure: "B2quYqla3mqu3QHqeRGMde66IFAluUHhzY87q/giPvONm/V7xmIcxzC6JNQ7X4pHotC7MUbsMf7vdcL1tyuPP5nqyfaxk0nnGnVoJZIQhMaBQ6Lkyt2Ac5u5/QBeHujrj8gqGjT7vzgPIYMYiSLV09VcPgMaYS6WxCrFmXOEJOeVGOGjG9J2wApAmlf6m/hBogLEbdbzRheqJwX5gMxppKX5XRaiqvKGR7hHFiW5HBaAiQ6caQEw6DZL9GnjMsqd5VMVL7DmbETncNE1utLVDO7mFwDMbkeonmwjsmQsJAo5Be+WFl/a/yRQLrqhhTFwag+V7g7RjcBKAdJ5IZJkN0EC9GjY0ZGOjE2cmcrX03U1g1voO7oreH6+25VqPTTGWHv5K8wDWM5QnhBJGtH2yH76p5XYK8iNOXPTqvjubN9sG3INsnTjxBHuQw+aY2vgwBGCvW+Asqe73BV8SDrmqcCqgmm/BUy0kZRd8mhaQdFL0SAO3suV7uf+fzIsmRtndFx24J5Cm6LE6NnTmtbhHhs4mUEqfngMq0XZBXj5z4LDhzOH4g3rf/Y7jHBjAAa6sKeh1sINdHrMdB843dDfNQPp/7g3hfKi31U9ZMczndsr2QYqm2OC/3lPiRHC+8Grj5PxXmiBplzTppyhl+1pjZWy5SCoQi6ulBOyaZcwfYM="
- secure: "Kmh31kozw9NBbwtkucpf+QslinJIclB7rcktHz4RokVS5xiMD7njWvD8ptZhXqZG/tNcNiXkWskgM9EV/OvLTTavJToHE1gMhJel5ODxKM0b5QZDy6tkW/wI0Kdl0lck90EAJ7bmsOmF+oGczGsauiLPSlBOZV0tn1zdeJKwwmg46C9UyIhTDcFqUD1ehxExkDHnbOk6HmEPGCsZQCGNXY2t++dO7fRodIgjZAKcyTwgrh6WEZDfQDjvC51yQlVzL2mrcFqHi8qYAx3OHnuidRoMZ4huaoxLimw/1bGii/txTZwJ/pQdF90kGqSqLpRiO2lHLq5l8WAd4Z1XPSs1ZzbKUEhWO8VGgfR0Cp+Bkkd6P/vcuTq7bdhpvdnZn9FRUI/bfgZi827Skj/WoUh0RR7ns6RuDfQ5uGD9NiWDsbfoKLbvvYjyQycQlQTKglYQVcU/pWsuqVlx+27vM6JltR0Pct4uAJxAN+EFA4sTT/u4HiZ0NQyZWisZauu7CyMRN29fkKVVyYHHCr6QcNMUpvp1vvKYaUN7GeykGNVfoHB7CWAKcc6banh33c+QqG5DSTE++ZNa712gxEizQnO5MmLECUxNDpOGZnLsTlgbABwDxxe4PxFxXvBLm9pkJftq5cp9T9Syzz/yyT7z5weSGvRes94eUtSt0agoeX2MKsE="
- secure: "VezxV+VdmbmtrQYT8AZIyg41WBROxuxpumerkcubADF7V4wV6lwx9Rd2G6yAr0VuHCNUUhS4m+gPFIsuiQbAhyupiEkwhzUYqk1tF+LITlLLPegLypjiLmhJMwGUNuDSSsih1Icmg9FzrP4VyzgGn9pBjoG9QYj1civBZeGwg++e/XDYlHMXrpd/UEfMKVB71JwB0tle4fKJZSvblIqP62yvbBaKHx6A4+ZWzJV5Vps0DoIeNtKCNmNNloKZVHfjbsvqSjnMYUJzkOzyPkM822q41N/D+3IAufO16+jH/W0vAZeN0e4GXiN5W+CVkr2Gbh0FwkVQcI3bekaOIn45XLUMLKdf+JsWDPKz9RraHelR9YxL5GoJ7ntwvmucxw0p8EVyJ/xLk/pBCP8iHq0Jb8//js25XHgxzzAWI37MErPAAGgTKZAVdAN0mGXbe63tWmwaBlEbK8h2A8di6abW5x6YHTkTo2BRlHUSTU8dE3VqTnpSkne5n1SlEa4g1Bci3J45M0/pLmHV6yCxCM5BrVXS5ByaB61py/umSbpmdIBFV6TM1MaKK3lAucQrR+8To/vCbm8XqPyujJdOR+ENIuuDgEU/Yh5Hv5SAODekUYaCp4pjfGzFADHQWVNDxIOXrwBN4OfSiAvRc1x6HXndOmNI4QtOxheuCRFFthq8VZI="
- GITHUB_BASE_URL=https://api.github.com/repos/pmd/pmd
jobs:
fast_finish: true
@ -48,7 +49,7 @@ jobs:
env: BUILD=publish
before_install:
- bash .travis/before_install.sh "11.0.5+10"
- bash .travis/before_install.sh "11.0.6+10"
- source ${HOME}/java.env
install: true
before_script: true
@ -96,5 +97,5 @@ cache:
# add the encrypted GPG keyring file to repo (https://docs.travis-ci.com/user/encrypting-files/#Automated-Encryption), decrypt it and install it at the beginning of .travis-deploy.sh
#
# GITHUB_OAUTH_TOKEN - the token used to upload the binaries to github releases
# GITHUB_BASE_URL - the api url to use for github releases - does not need to be secure (https://api.github.com/repos/pmd/pmd)
#

View File

@ -37,7 +37,7 @@ elif travis_isPush; then
echo -e "\n\n"
# create a draft github release
gh_releases_createDraftRelease "${TRAVIS_TAG}" "$(git show-ref --hash ${TRAVIS_TAG})"
gh_releases_createDraftRelease "${TRAVIS_TAG}" "$(git rev-list -n 1 ${TRAVIS_TAG})"
GH_RELEASE="$RESULT"
# Build and deploy to ossrh / maven-central

View File

@ -1,9 +1,10 @@
#
# The functions here require the following scripts:
# .travis/logger.sh
# logger.sh
#
# The functions here require the following environment variables:
# GITHUB_OAUTH_TOKEN
# GITHUB_BASE_URL
#
#
@ -30,13 +31,13 @@ function gh_releases_createDraftRelease() {
EOF
)
log_debug "POST https://api.github.com/repos/pmd/pmd/releases"
log_info "Creating gihtub draft release"
log_debug "POST $GITHUB_BASE_URL/releases"
log_info "Creating github 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")
"$GITHUB_BASE_URL/releases")
log_debug " -> response: $RESULT"
log_success "Created draft release with id $(echo $RESULT | jq --raw-output ".url")"
@ -53,9 +54,9 @@ function gh_releases_createDraftRelease() {
#
function gh_releases_getLatestDraftRelease() {
log_debug "$FUNCNAME"
log_debug "GET https://api.github.com/repos/pmd/pmd/releases?per_page=1"
log_debug "GET $GITHUB_BASE_URL/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]")
"$GITHUB_BASE_URL/releases?per_page=1" | jq ".[0]")
log_debug " -> response: $RESULT"
local draft=$(echo $RESULT | jq ".draft")
if [ "$draft" != "true" ]; then
@ -77,12 +78,12 @@ function gh_release_deleteRelease() {
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_debug "DELETE $GITHUB_BASE_URL/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")
"$GITHUB_BASE_URL/releases/$releaseId")
log_debug " -> response: $response"
log_success "Deleted release with id $releaseId"
}
@ -98,6 +99,17 @@ function gh_release_getIdFromData() {
RESULT=$(echo $release | jq --raw-output ".id")
}
#
# Determines the tag_name from the given JSON release data.
#
# RESULT = "the tag name"
#
function gh_release_getTagNameFromData() {
local release="$1"
RESULT=$(echo $release | jq --raw-output ".tag_name")
}
#
# Uploads a asset to an existing release.
#
@ -141,7 +153,9 @@ function gh_release_updateRelease() {
gh_release_getIdFromData "$release"
local releaseId="$RESULT"
log_debug "$FUNCNAME releaseId=$releaseId name=$name"
gh_release_getTagNameFromData "$release"
local tagName="$RESULT"
log_debug "$FUNCNAME releaseId=$releaseId name=$name tag_name=$tagName"
body="${body//'\'/\\\\}"
body="${body//$'\r'/}"
@ -150,13 +164,14 @@ function gh_release_updateRelease() {
local request=$(cat <<-EOF
{
"tag_name": "${tagName}",
"name": "${name}",
"body": "${body}"
}
EOF
)
log_debug "PATCH https://api.github.com/repos/pmd/pmd/releases/${releaseId}"
log_debug "PATCH $GITHUB_BASE_URL/releases/${releaseId}"
log_debug " -> request: $request"
log_info "Updating github release $releaseId"
local response
@ -164,7 +179,7 @@ function gh_release_updateRelease() {
-H "Content-Type: application/json" \
--data "${request}" \
-X PATCH \
"https://api.github.com/repos/pmd/pmd/releases/${releaseId}")
"$GITHUB_BASE_URL/releases/${releaseId}")
log_debug " -> response: $response"
log_success "Updated release with id=$releaseId"
}
@ -184,7 +199,7 @@ function gh_release_publishRelease() {
log_debug "$FUNCNAME releaseId=$releaseId"
local request='{"draft":false}'
log_debug "PATCH https://api.github.com/repos/pmd/pmd/releases/${releaseId}"
log_debug "PATCH $GITHUB_BASE_URL/releases/${releaseId}"
log_debug " -> request: $request"
log_info "Publishing github release $releaseId"
local response
@ -192,9 +207,8 @@ function gh_release_publishRelease() {
-H "Content-Type: application/json" \
--data "${request}" \
-X PATCH \
"https://api.github.com/repos/pmd/pmd/releases/${releaseId}")
"$GITHUB_BASE_URL/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

@ -204,6 +204,16 @@ echo "$NEW_RELEASE_NOTES"
echo
echo
echo
tweet="PMD ${RELEASE_VERSION} released: https://github.com/pmd/pmd/releases/tag/pmd_releases/${RELEASE_VERSION} #PMD"
tweet="${tweet// /%20}"
tweet="${tweet//:/%3A}"
tweet="${tweet//#/%23}"
tweet="${tweet//\//%2F}"
tweet="${tweet//$'\r'//}"
tweet="${tweet//$'\n'//%0A}"
echo "* Tweet about this release on https://twitter.com/pmd_analyzer:"
echo " <https://twitter.com/intent/tweet?text=$tweet>"
echo
echo "------------------------------------------"
echo "Done."
echo "------------------------------------------"

View File

@ -1,11 +1,12 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.11.1)
i18n (~> 0.7)
activesupport (6.0.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
zeitwerk (~> 2.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
coffee-script (2.4.1)
@ -15,7 +16,7 @@ GEM
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
concurrent-ruby (1.1.5)
concurrent-ruby (1.1.6)
dnsruby (1.61.3)
addressable (~> 2.5)
em-websocket (0.5.1)
@ -25,33 +26,32 @@ GEM
ffi (>= 1.3.0)
eventmachine (1.2.7)
execjs (2.7.0)
faraday (0.16.2)
faraday (1.0.0)
multipart-post (>= 1.2, < 3)
ffi (1.11.1)
ffi (1.12.2)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (200)
activesupport (= 4.2.11.1)
github-pages (204)
github-pages-health-check (= 1.16.1)
jekyll (= 3.8.5)
jekyll-avatar (= 0.6.0)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.5)
jekyll-commonmark-ghpages (= 0.1.6)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.11.0)
jekyll-feed (= 0.13.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.12.1)
jekyll-mentions (= 1.4.1)
jekyll-optional-front-matter (= 0.3.0)
jekyll-github-metadata (= 2.13.0)
jekyll-mentions (= 1.5.1)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.2.0)
jekyll-redirect-from (= 0.14.0)
jekyll-relative-links (= 0.6.0)
jekyll-remote-theme (= 0.4.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.15.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.1)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.5.0)
jekyll-sitemap (= 1.2.0)
jekyll-swiss (= 0.4.0)
jekyll-seo-tag (= 2.6.1)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
@ -61,19 +61,18 @@ GEM
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.3)
jekyll-theme-primer (= 0.5.4)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-titles-from-headings (= 0.5.1)
jemoji (= 0.10.2)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.11.1)
kramdown (= 1.17.0)
liquid (= 4.0.0)
listen (= 3.1.5)
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.0)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
rouge (= 2.2.1)
rouge (= 3.13.0)
terminal-table (~> 1.4)
github-pages-health-check (1.16.1)
addressable (~> 2.3)
@ -81,7 +80,7 @@ GEM
octokit (~> 4.0)
public_suffix (~> 3.0)
typhoeus (~> 1.3)
html-pipeline (2.12.0)
html-pipeline (2.12.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
@ -100,50 +99,50 @@ GEM
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.6.0)
jekyll (~> 3.0)
jekyll-avatar (0.7.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.3.1)
commonmarker (~> 0.14)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark-ghpages (0.1.5)
jekyll-commonmark-ghpages (0.1.6)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1)
rouge (~> 2)
jekyll-commonmark (~> 1.2)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.11.0)
jekyll (~> 3.3)
jekyll-feed (0.13.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.12.1)
jekyll (~> 3.4)
jekyll-github-metadata (2.13.0)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.4.1)
jekyll-mentions (1.5.1)
html-pipeline (~> 2.3)
jekyll (~> 3.0)
jekyll-optional-front-matter (0.3.0)
jekyll (~> 3.0)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.2.0)
jekyll (~> 3.0)
jekyll-redirect-from (0.14.0)
jekyll (~> 3.3)
jekyll-relative-links (0.6.0)
jekyll (~> 3.3)
jekyll-remote-theme (0.4.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.15.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.1)
addressable (~> 2.0)
jekyll (~> 3.5)
rubyzip (>= 1.2.1, < 3.0)
jekyll (>= 3.5, < 5.0)
rubyzip (>= 1.3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.5.0)
jekyll (~> 3.3)
jekyll-sitemap (1.2.0)
jekyll (~> 3.3)
jekyll-swiss (0.4.0)
jekyll-seo-tag (2.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
@ -171,8 +170,8 @@ GEM
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.3)
jekyll (~> 3.5)
jekyll-theme-primer (0.5.4)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
@ -184,43 +183,42 @@ GEM
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.1)
jekyll (~> 3.3)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.10.2)
jemoji (0.11.1)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (~> 3.0)
jekyll (>= 3.0, < 5.0)
kramdown (1.17.0)
liquid (4.0.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
liquid (4.0.3)
listen (3.2.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.4.0)
minima (2.5.0)
jekyll (~> 3.5)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.12.2)
minitest (5.14.0)
multipart-post (2.1.1)
nokogiri (1.10.4)
nokogiri (1.10.8)
mini_portile2 (~> 2.4.0)
octokit (4.14.0)
octokit (4.16.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (3.1.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rouge (2.2.1)
rouge (3.13.0)
ruby-enum (0.7.2)
i18n
ruby_dep (1.5.0)
rubyzip (2.0.0)
rubyzip (2.2.0)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
@ -235,9 +233,10 @@ GEM
thread_safe (0.3.6)
typhoeus (1.3.1)
ethon (>= 0.9.0)
tzinfo (1.2.5)
tzinfo (1.2.6)
thread_safe (~> 0.1)
unicode-display_width (1.6.0)
unicode-display_width (1.6.1)
zeitwerk (2.2.2)
PLATFORMS
ruby
@ -247,4 +246,4 @@ DEPENDENCIES
jekyll
BUNDLED WITH
1.17.3
2.1.4

View File

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

View File

@ -61,12 +61,18 @@ entries:
- title: Extending PMD
output: web, pdf
subfolderitems:
- title: Writing a rule
url: /pmd_userdocs_extending_writing_pmd_rules.html
- title: Introduction to writing rules
url: /pmd_userdocs_extending_writing_rules_intro.html
output: web, pdf
- title: Writing XPath rules
- title: Your first rule
url: /pmd_userdocs_extending_your_first_rule.html
output: web, pdf
- title: XPath rules
url: /pmd_userdocs_extending_writing_xpath_rules.html
output: web, pdf
- title: Java rules
url: /pmd_userdocs_extending_writing_java_rules.html
output: web, pdf
- title: Rule designer reference
url: /pmd_userdocs_extending_designer_reference.html
output: web, pdf
@ -91,12 +97,18 @@ entries:
- title: Tools / Integrations
output: web, pdf
subfolderitems:
- title: Maven PMD plugin
- title: Maven PMD Plugin
output: web, pdf
url: /pmd_userdocs_tools_maven.html
- title: Gradle
output: web, pdf
url: /pmd_userdocs_tools_gradle.html
- title: Ant
output: web, pdf
url: /pmd_userdocs_tools_ant.html
- title: PMD Java API
output: web, pdf
url: /pmd_userdocs_tools_java_api.html
- title: CI integrations
output: web, pdf
url: /pmd_userdocs_tools_ci.html
@ -382,6 +394,12 @@ entries:
- title: Adding metrics support to a language
url: /pmd_devdocs_major_adding_new_metrics_framework.html
output: web, pdf
- title: Experimental features
output: web, pdf
subfolderitems:
- title: Creating (XML) dump of the AST
url: /pmd_devdocs_experimental_ast_dump.html
output: web, pdf
- title: Project documentation
output: web, pdf
folderitems:

55
docs/_data/xpath_funs.yml Normal file
View File

@ -0,0 +1,55 @@
# This file describes custom XPath functions per language
# This is rendered using _includes/custom/xpath_fun_doc.html
aliases:
- &qname_param
name: javaQualifiedName
type: "xs:string"
description: "The qualified name of a Java class, possibly with pairs of brackets to indicate an array type.
Can also be a primitive type name."
- &needs_typenode "The context node must be a {% jdoc jast::TypeNode %}"
langs:
- name: "Java"
ns: "pmd-java"
funs:
- name: typeIs
returnType: "xs:boolean"
shortDescription: "Tests a node's static type"
description: "Returns true if the context node's static Java type is a subtype of the given type.
This tests for the resolved type of the Java construct, not the type of the AST node.
For example, the AST node for a literal (e.g. `5d`) has type ASTLiteral, however this
function will compare the type of the literal (eg here, `double`) against the argument."
notes: *needs_typenode
parameters:
- *qname_param
examples:
- code: '//FormalParameter[pmd-java:typeIs("java.lang.String[]")]'
outcome: "Matches formal parameters of type `String[]` (including vararg parameters)"
- code: '//VariableDeclaratorId[pmd-java:typeIs("java.lang.List")]'
outcome: "Matches variable declarators of type `List` or any of its subtypes (including e.g. `ArrayList`)"
- name: typeIsExactly
returnType: "xs:boolean"
shortDescription: "Tests a node's static type, ignoring subtypes"
description: "Returns true if the context node's static type is exactly the given type.
In particular, returns false if the context node's type is
a subtype of the given type."
notes: *needs_typenode
parameters:
- *qname_param
examples:
- code: '//VariableDeclaratorId[pmd-java:typeIsExactly("java.lang.List")]'
outcome: "Matches variable declarators of type `List` (but not e.g. `ArrayList`)"
- name: metric
returnType: "xs:decimal?"
shortDescription: "Computes and returns the value of a metric"
description: "Returns the value of the metric as evaluated on the context node"
notes: "The context node must be a {% jdoc jast::ASTAnyTypeDeclaration %} or a {% jdoc jast::MethodLikeNode %}"
parameters:
- name: "metricKey"
type: "xs:string"
description: "The name of an enum constant in {% jdoc jmx::api.JavaOperationMetricKey %} or {% jdoc jmx::api.JavaClassMetricKey %}"

View File

@ -0,0 +1,101 @@
{% for lang in site.data.xpath_funs.langs %}
<!-- Generates the documentation of XPath functions. -->
### {{ lang.name }}
{{ lang.name }} functions are in the namespace `{{ lang.ns }}`.
<div class="table-responsive">
<table width="100%">
<thead>
<tr>
<th>Function name</th>
<th>Description (click for details)</th>
<th></th>
</tr>
</thead>
{% for fun in lang.funs %}
{% capture fun_id %}{{ lang.ns | append: '-' | append: fun.name }}{% endcapture %}
<tr data-toggle="collapse" id="{{ fun_id }}" data-target="{{ fun_id | prepend: '#' | append: '-expand' }}" class="accordion-toggle">
<td>{{ fun.name }}</td>
<td>{{ fun.shortDescription }}</td>
<td>
<button type="button" class="btn btn-link"><i
class="fa fa-ellipsis-h"></i></button>
</td>
</tr>
<tr>
<td colspan="4" class="hiddenRow">
<div class="accordion-body collapse" id="{{ fun_id | append: '-expand' }}">
<div style="margin-left: 15px" class="xpath-fun-doc">
<!-- Yeah the ID is duplicated but it's ok on my browser and makes -->
<!--links somehow work when details are collapsed -->
<h4 class="fun-details-header" id="{{ fun_id }}">
<span class="fun-ns">{{ lang.ns | append: ':' }}</span><span
class="fun-name">{{ fun.name }}</span><span
class="fun-ns">{{ fun | xpath_fun_type }}</span>
</h4>
<div style="margin-left: 30px">
<dl>
<dd>{{ fun.description | render_markdown }}</dd>
<dt>Remarks</dt>
<dd>{{ fun.notes | render_markdown }}</dd>
{% if fun.parameters.size > 0 %}
<dt>Parameters</dt>
<dd>
<dl>
{% for param in fun.parameters %}
<dt>
<span class="param-name">{{ param.name }}</span>
<span class="param-type"> as {{ param.type }}</span>
</dt>
<dd>{{ param.description | render_markdown }}</dd>
{% endfor %}
</dl>
</dd>
{% endif %}
{% if fun.examples.size > 0 %}
<dt>Examples</dt>
<dd>
<dl class="code-examples">
{% for example in fun.examples %}
<dt><code>{{ example.code }}</code></dt>
<dd>{{ example.outcome | render_markdown }}</dd>
{% endfor %}
</dl>
</dd>
{% endif %}
</dl>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endfor %}

View File

@ -1,3 +1,5 @@
require 'kramdown'
module CustomFilters
# set intersection
@ -44,16 +46,27 @@ module CustomFilters
end
def render_markdown(text)
Kramdown::Document.new(text).to_html
end
def xpath_fun_type(fun_yaml)
res = '('
params = fun_yaml['parameters']
res += params.map {|it| it['type']}.join(', ') if params
res + ') as ' + fun_yaml['returnType']
end
def regex_replace(str, regex, subst)
if str && regex
str.gsub(Regexp::new(regex), subst || "")
end
str.gsub(Regexp.new(regex), subst || '') if str && regex
end
def regex_split(str, regex = nil)
if str
str.split(regex && Regexp::new(regex))
end
str.split(regex && Regexp.new(regex)) if str
end
# Takes an array of strings and maps every element x to {{ x | append: suffix }}
@ -63,9 +76,7 @@ module CustomFilters
# Returns the initial argument only if the second argument is truthy
def keep_if(any, test)
if test
any
end
any if test
end
# Append the suffix only if the condition argument is truthy
@ -91,14 +102,18 @@ module CustomFilters
end
end
def random_alphabetic(length)
('a'..'z').to_a.shuffle[0, length].join
end
private
def flatten_rec(seq)
seq.map {|h|
if (subs = h["folderitems"] || h["subfolderitems"] || h["subfolders"])
if (subs = h['folderitems'] || h['subfolderitems'] || h['subfolders'])
flatten_rec(subs).flatten
elsif (page = h["url"])
elsif (page = h['url'])
page
end
}.flatten
@ -106,10 +121,10 @@ module CustomFilters
def rank_lookup_from_sidebar(sidebar)
folders = sidebar["entries"][0]["folders"]
folders = sidebar['entries'][0]['folders']
ordered = flatten_rec(folders).select {|url|
url && url.end_with?(".html")
url && url.end_with?('.html')
}
Hash[ordered.zip (0...ordered.size)]

View File

@ -161,7 +161,7 @@ class JavadocTag < Liquid::Tag
end
def markup_link(rname, link)
"[`#{rname}`](#{link})"
"<a href=\"#{link}\"><code>#{rname}</code></a>"
end

View File

@ -0,0 +1,17 @@
#
# Apply a second pass of rendering on a block
#
class RenderBlock < Liquid::Block
def initialize(tag_name, arg, tokens)
super
@body = tokens
end
def render(context)
template = @body.render(context)
Liquid::Template.parse(template).render(context)
end
end
Liquid::Template.register_tag('render', RenderBlock)

View File

@ -52,3 +52,35 @@ details[open] summary {
background-color: #347DBE;
color: white;
}
.hiddenRow {
padding: 0 !important;
}
.xpath-fun-doc .fun-name {
font-weight: bold;
}
.xpath-fun-doc .fun-details-header {
font-family: monospace;
font-size: larger;
}
.xpath-fun-doc .fun-ns {
color: darkgray;
}
.xpath-fun-doc span.param-name {
font-weight: normal;
}
.xpath-fun-doc span.param-type {
font-weight: lighter;
font-style: italic;
color: darkgray;
}
.xpath-fun-doc .code-examples dt {
font-weight: normal;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -73,6 +73,78 @@ 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.21.0
##### Deprecated APIs
###### Internal API
Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0.
You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning.
* {% jdoc 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 %}.
* {% jdoc core::rules.RuleFactory %}
* {% jdoc core::rules.RuleBuilder %}
* Constructors of {% jdoc core::RuleSetFactory %}, use factory methods from {% jdoc core::RulesetsFactoryUtils %} instead
* {% jdoc core::RulesetsFactoryUtils#getRulesetFactory(core::PMDConfiguration, core::util.ResourceLoader) %}
* {% jdoc apex::lang.apex.ast.AbstractApexNode %}
* {% jdoc apex::lang.apex.ast.AbstractApexNodeBase %}, and the related `visit`
methods on {% jdoc apex::lang.apex.ast.ApexParserVisitor %} and its implementations.
Use {% jdoc apex::lang.apex.ast.ApexNode %} instead, now considers comments too.
* {% jdoc core::lang.ast.CharStream %}, {% jdoc core::lang.ast.JavaCharStream %},
{% jdoc core::lang.ast.SimpleCharStream %}: these are APIs used by our JavaCC
implementations and that will be moved/refactored for PMD 7.0.0. They should not
be used, extended or implemented directly.
* All classes generated by JavaCC, eg {% jdoc java::lang.java.ast.JJTJavaParserState %}.
This includes token classes, which will be replaced with a single implementation, and
subclasses of {% jdoc core::lang.ast.ParseException %}, whose usages will be replaced
by just that superclass.
###### For removal
* pmd-core
* Many methods on the {% jdoc core::lang.ast.Node %} interface
and {% jdoc core::lang.ast.AbstractNode %} base class. See their javadoc for details.
* {% jdoc !!core::lang.ast.Node#isFindBoundary() %} is deprecated for XPath queries.
* pmd-java
* {% jdoc java::lang.java.AbstractJavaParser %}
* {% jdoc java::lang.java.AbstractJavaHandler %}
* [`ASTAnyTypeDeclaration.TypeKind`](https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.TypeKind.html)
* {% jdoc !!java::lang.java.ast.ASTAnyTypeDeclaration#getKind() %}
* {% jdoc java::lang.java.ast.JavaQualifiedName %}
* {% jdoc !!java::lang.java.ast.ASTCatchStatement#getBlock() %}
* {% jdoc !!java::lang.java.ast.ASTCompilationUnit#declarationsAreInDefaultPackage() %}
* {% jdoc java::lang.java.ast.JavaQualifiableNode %}
* {% jdoc !!java::lang.java.ast.ASTAnyTypeDeclaration#getQualifiedName() %}
* {% jdoc !!java::lang.java.ast.ASTMethodOrConstructorDeclaration#getQualifiedName() %}
* {% jdoc !!java::lang.java.ast.ASTLambdaExpression#getQualifiedName() %}
* {% jdoc_package java::lang.java.qname %} and its contents
* {% jdoc java::lang.java.ast.MethodLikeNode %}
* Its methods will also be removed from its implementations,
{% jdoc java::lang.java.ast.ASTMethodOrConstructorDeclaration %},
{% jdoc java::lang.java.ast.ASTLambdaExpression %}.
* {% jdoc !!java::lang.java.ast.ASTAnyTypeDeclaration#getImage() %} will be removed. Please use `getSimpleName()`
instead. This affects {% jdoc !!java::lang.java.ast.ASTAnnotationTypeDeclaration#getImage() %},
{% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#getImage() %}, and
{% jdoc !!java::lang.java.ast.ASTEnumDeclaration#getImage() %}.
* Several methods of {% jdoc java::lang.java.ast.ASTTryStatement %}, replacements with other names
have been added. This includes the XPath attribute `@Finally`, replace it with a test for `child::FinallyStatement`.
* Several methods named `getGuardExpressionNode` are replaced with `getCondition`. This affects the
following nodes: WhileStatement, DoStatement, ForStatement, IfStatement, AssertStatement, ConditionalExpression.
* {% jdoc java::lang.java.ast.ASTYieldStatement %} will not implement {% jdoc java::lang.java.ast.TypeNode %}
anymore come 7.0.0. Test the type of the expression nested within it.
#### 6.20.0
No changes.

View File

@ -0,0 +1,111 @@
---
title: Creating XML dump of the AST
tags: [devdocs, experimental]
summary: Creating a XML representation of the AST allows to analyze the AST with other tools.
last_updated: January 17, 2020 (6.21.0)
permalink: pmd_devdocs_experimental_ast_dump.html
---
## Command line usage
```shell
$ run.sh ast-dump --help
Usage: ast-dump [options]
Options:
--encoding, -e
Encoding of the source file.
Default: UTF-8
--file
The file to dump
--format, -f
The output format.
Default: xml
--help, -h
Display usage.
--language, -l
Specify the language to use.
Default: java
--read-stdin, -i
Read source from standard input
Default: false
-P
Properties for the renderer.
Syntax: -Pkey=value
Default: {}
Available languages: apex ecmascript java jsp modelica plsql pom scala text vf vm wsdl xml xsl
Available formats: xml XML format with the same structure as the one used in XPath
+ Properties
+ singleQuoteAttributes Use single quotes to delimit attribute values (default true)
+ lineSeparator Line separator to use. The default is platform-specific. (default \n)
+ renderProlog True to output a prolog (default true)
+ renderCommonAttributes True to render attributes like BeginLine, EndLine, etc. (default false)
```
## Example
```shell
$ cat Foo.java
public class Foo {
int a;
}
$ run.sh ast-dump --format xml --language java --file Foo.java > Foo.xml
-------------------------------------------------------------------------------
This command line utility is experimental. It might change at any time without
prior notice.
-------------------------------------------------------------------------------
$ cat Foo.xml
<?xml version='1.0' encoding='UTF-8' ?>
<CompilationUnit Image='' PackageName='' declarationsAreInDefaultPackage='true'>
<TypeDeclaration Image=''>
<ClassOrInterfaceDeclaration Abstract='false' BinaryName='Foo' Default='false' Final='false' Image='Foo' Interface='false' Local='false' Modifiers='1' Native='false' Nested='false' PackagePrivate='false' Private='false' Protected='false' Public='true' SimpleName='Foo' Static='false' Strictfp='false' Synchronized='false' Transient='false' TypeKind='CLASS' Volatile='false'>
<ClassOrInterfaceBody AnonymousInnerClass='false' EnumChild='false' Image=''>
<ClassOrInterfaceBodyDeclaration AnonymousInnerClass='false' EnumChild='false' Image='' Kind='FIELD'>
<FieldDeclaration Abstract='false' AnnotationMember='false' Array='false' ArrayDepth='0' Default='false' Final='false' Image='' InterfaceMember='false' Modifiers='0' Native='false' PackagePrivate='true' Private='false' Protected='false' Public='false' Static='false' Strictfp='false' Synchronized='false' SyntacticallyFinal='false' SyntacticallyPublic='false' SyntacticallyStatic='false' Transient='false' VariableName='a' Volatile='false'>
<Type Array='false' ArrayDepth='0' ArrayType='false' Image='' TypeImage='int'>
<PrimitiveType Array='false' ArrayDepth='0' Boolean='false' Image='int' />
</Type>
<VariableDeclarator Image='' Initializer='false' Name='a'>
<VariableDeclaratorId Array='false' ArrayDepth='0' ArrayType='false' ExceptionBlockParameter='false' ExplicitReceiverParameter='false' Field='true' Final='false' FormalParameter='false' Image='a' LambdaParameter='false' LocalVariable='false' ResourceDeclaration='false' TypeInferred='false' VariableName='a' />
</VariableDeclarator>
</FieldDeclaration>
</ClassOrInterfaceBodyDeclaration>
</ClassOrInterfaceBody>
</ClassOrInterfaceDeclaration>
</TypeDeclaration>
</CompilationUnit>
$ xmlstarlet select -t -c "//VariableDeclaratorId[@VariableName='a']" Foo.xml
<VariableDeclaratorId Array="false" ArrayDepth="0" ArrayType="false" ExceptionBlockParameter="false" ExplicitReceiverParameter="false" Field="true" Final="false" FormalParameter="false" Image="a" LambdaParameter="false" LocalVariable="false" ResourceDeclaration="false" TypeInferred="false" VariableName="a"/>
```
This example uses [xmlstarlet](http://xmlstar.sourceforge.net/) to query the xml document for any variables/fields
with the name "a".
## Programmatic usage
Just parse your source code to get the AST and pass it on to the `XmlTreeRenderer`:
```java
import java.io.IOException;
import java.io.StringReader;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer;
public class TreeExport {
public static void main(String[] args) throws IOException {
LanguageVersionHandler java = LanguageRegistry.getLanguage("Java").getDefaultVersion().getLanguageVersionHandler();
Parser parser = java.getParser(java.getDefaultParserOptions());
Node root = parser.parse("foo", new StringReader("class Foo {}"));
new XmlTreeRenderer().renderSubtree(root, System.out);
}
}
```

View File

@ -5,32 +5,15 @@ tags: [devdocs, extending, metrics]
summary: "PMD's Java module has an extensive framework for the calculation of metrics, which allows rule developers
to implement and use new code metrics very simply. Most of the functionality of this framework is abstracted in such
a way that any PMD supported language can implement such a framework without too much trouble. Here's how."
last_updated: December 2017
last_updated: February 2020
permalink: pmd_devdocs_major_adding_new_metrics_framework.html
author: Clément Fournier <clement.fournier76@gmail.com>
---
## Internal architecture of the metrics framework
### Overview of the Java framework
The framework has several subsystems, the two most easily identifiable being:
* A **project memoizer** (`ProjectMemoizer`). When a metric is computed, it's stored back in this structure and can be
reused later. This
reduces the overhead on the calculation of e.g. aggregate results (`ResultOption` calculations). The contents of
this data structure are indexed with fully qualified names (`JavaQualifiedName`), which must identify unambiguously
classes and methods.
* The **façade**. The static end-user façade (`JavaMetrics`) is backed by an instance of a `JavaMetricsFaçade`. This
allows us to abstract the functionality of the façade into `pmd-core` for other frameworks to use. The façade
instance contains a project memoizer for the analysed project, and a metrics computer
(`JavaMetricsComputer`). It's this last object which really computes the metric and stores back its result in the
project mirror, while the façade only handles parameters.
Metrics (`Metric<N>`) plug in to this static system and only provide behaviour that's executed by the metrics computer.
Internally, metric keys (`MetricKey<N>`) are parameterized with their version (`MetricVersion`) to index memoisation
maps (see `ParameterizedMetricKey<N>`). This allows us to memoise several versions of the same metric without conflict.
The framework is pretty simple. On a high level, a `Metric<N>` describes some numeric computation on a node of type `N`.
You should wrap it into a `MetricKey<N>`, so that it can be cached on nodes (implemented by {%jdoc core::lang.metrics.MetricsUtil %}).
At the very least, a metrics framework has those two components and is just a convenient way to compute and memoize
metrics on a single file. The expressive power of metrics can be improved by implementing *signature matching* capabilities,
@ -38,53 +21,12 @@ which allows a metric to count signatures matching a specific pattern (a mask) o
designed to work across files, given a working usage resolution. However, making that work with incremental analysis is
harder than it looks, and has been rescheduled to another project.
### Abstraction layer
As you may have seen, most of the functionality of the first two components are abstracted into `pmd-core`. This
allows us to implement new metrics frameworks quite quickly. These abstract components are parameterized by the
node types of the class and operation AST nodes. Moreover, it makes the external behaviour of the framework very
stable across languages, yet each component can easily be customized by adding methods or overriding existing ones.
The signature matching aspect is framed by generic interfaces, but it can't really be abstracted more
than that. The info given in the signatures is usually very language specific, as it includes info about e.g.
visibility modifiers. So more work is required to implement that, but it can already be used to implement
sophisticated metrics, that already give access to detection strategies.
## Implementation of a new framework
### 1. Groundwork
* Create a class implementing `QualifiedName`. This implementation must be tailored to the target language so
that it can indentify unambiguously any class and operation in the analysed project. You
must implement `equals`, `hashCode` and `toString`.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedName.java)
* Determine the AST nodes that correspond to class and method declaration in your language. These types are
referred hereafter as `T` and `O`, respectively. Both these types must implement the interface `QualifiableNode`,
which means they must expose a `getQualifiedName` method to give access to their qualified name.
### 2. Implement the façade
* Create a class extending `AbstractMetricsComputer<T, O>`. This object will be responsible for calculating metrics
given a memoizer, a node and info about the metric. Typically, this object is stateless so you might as well make it
a singleton.
* Create a class extending `BasicProjectMemoizer<T, O>`. There's no abstract functionality to implement.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaProjectMemoizer.java)
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java)
* Create a class extending `AbstractMetricsFacade<T, O>`. This class needs a reference to your `ProjectMemoizer` and
your `MetricsComputer`. It backs the real end user façade, and handles user provided parameters before delegating to
your `MetricsComputer`.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java)
* Create the static façade of your framework. This one has an instance of your `MetricsFaçade` object and delegates
static methods to that instance.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetrics.java)
* Create classes `AbstractOperationMetric` and `AbstractClassMetric`. These must implement `Metric<T>` and
`Metric<O>`, respectively. They typically provide defaults for the `supports` method of each metric.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaOperationMetric.java)
* Create enums `ClassMetricKey` and `OperationMetricKey`. These must implement `MetricKey<T>` and `MetricKey<O>`. The
enums list all available metric keys for your language.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaOperationMetricKey.java)
* Create metrics by extending your base classes, reference them in your enums, and you can start using them with your
façade!
* Implement metrics (typically in an internal package)
* Create some public enums/ utility classes to expose metric keys
* Implement a {%jdoc core::lang.metrics.LanguageMetricsProvider %}, to expose your metrics to the designer
* Use your metric keys in rules with {%jdoc core::lang.metrics.MetricsUtil %}
### Optional: Signature matching

View File

@ -88,17 +88,16 @@ author: Tom Copeland <tom@infoether.org>
* November 2003 - [JavaWorld: "Bug patrol"](http://www.javaworld.com/javaworld/jw-11-2003/jw-1121-quality.html) -
Various code inspection tools
* June 2003 - [Software Development Times [link broken]](http://www.sdtimes.com/news/080/story15.htm) - PMD
is embedded in QStudio.
* June 2003 - [Software Development Times](https://web.archive.org/web/20070914023950/http://www.sdtimes.com/article/story-20030615-15.html) - PMD is embedded in QStudio.
* May 2003 - [techrepublic.com](http://www.techrepublic.com/article/three-tools-that-make-java-code-review-painless-and-effective/5031836) -
PMD, Checkstyle, and Jalopy
* April 2003 - [O'Reilly OnJava.com](http://onjava.com/pub/a/onjava/2003/04/09/pmd_rules.html) - PMD custom rules
* April 2003 - [O'Reilly OnJava.com](https://web.archive.org/web/20180505093751/http://www.onjava.com/pub/a/onjava/2003/04/09/pmd_rules.html) - PMD custom rules
* March 2003 - [O'Reilly OnJava.com](http://onjava.com/pub/a/onjava/2003/03/12/pmd_cpd.html) - overview of CPD
* March 2003 - [O'Reilly OnJava.com](https://web.archive.org/web/20180505092514/http://www.onjava.com/pub/a/onjava/2003/03/12/pmd_cpd.html) - overview of CPD
* February 2003 - [O'Reilly OnJava.com](http://onjava.com/pub/a/onjava/2003/02/12/static_analysis.html) - overview of PMD
* February 2003 - [O'Reilly OnJava.com](https://web.archive.org/web/20180506055525/http://www.onjava.com/pub/a/onjava/2003/02/12/static_analysis.html) - overview of PMD
* January 2003 - [Sprout](http://netbeans.org/community/articles/interviews/tom_copeland_ole-martin_fr.html) -
* January 2003 - [Sprout](https://netbeans.org/community/articles/interviews/tom_copeland_ole-martin_fr.html) -
interview with Ole-Martin and Tom

View File

@ -3,18 +3,24 @@ title: Defining rule properties
short_title: Defining rule properties
tags: [extending, userdocs]
summary: "Learn how to define your own properties both for Java and XPath rules."
last_updated: December 2017 (6.0.0)
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_defining_properties.html
author: Hooper Bloob <hooperbloob@users.sourceforge.net>, Romain Pelisse <rpelisse@users.sourceforge.net>, Clément Fournier <clement.fournier76@gmail.com>
---
{% jdoc_nspace :props core::properties %}
{% jdoc_nspace :PF props::PropertyFactory %}
## Defining properties
Rule properties are a way to make your rules configurable directly from the
ruleset XML. Their usage is described on the [Configuring Rules](pmd_userdocs_configuring_rules.html#rule-properties) page.
If you're a rule developer, you may want to think about what would be useful for a user of your rule to parameterise. It could be a numeric report level, a boolean flag changing the behaviour of your rule... PMD ships with many types of properties ready to use!
If you're a rule developer, you may want to think about what would be useful for
a user of your rule to parameterise. It could be a numeric report level, a boolean
flag changing the behaviour of your rule... Chances are there *is* some detail
that can be abstracted away from your implementation, and in that case, this
page can help you squeeze that sweet flexibility out of your rule.
### Overview of properties
## Overview of properties
The basic thing you need to do as a developer is to define a **property descriptor** and declare that your rule uses it. A property descriptor defines a number of attributes for your property:
* Its *name*, with which the user will refer to your property;
@ -23,64 +29,57 @@ The basic thing you need to do as a developer is to define a **property descript
Don't worry, all of these attributes can be specified in a single Java statement (or xml element for XPath rules).
Without further ado, here is the list of available (single-value) properties:
|Class name|Value type|
|----------|----------|
|IntegerProperty | int
|DoubleProperty | double
|FloatProperty | float
|LongProperty | long
|EnumeratedProperty\<*E*\>| *E*
|StringProperty|String
|BooleanProperty|boolean
|CharacterProperty|char
|FileProperty|java.io.File
|MethodProperty|java.lang.reflect.Method
|TypeProperty|java.lang.Class\<?\>
|RegexProperty|java.util.regex.Pattern
Each of these is complemented by a multivalued variant, whose name ends with "MultiProperty", and which returns a list of values, e.g.
|Class name|Value type|
|----------|----------|
|LongMultiProperty | List\<Long\>
|EnumeratedMultiProperty\<*E*\>| List\<*E*\>
Note that RegexProperty doesn't have a multivalued variant, since the delimiters could be part of a specific value.
### For Java rules
## For Java rules
The procedure to define a property is quite straightforward:
* Create a property descriptor of the type you want, using its builder;
* Create a property descriptor of the type you want, by using a
builder from {% jdoc :PF %}
* Call {% jdoc !a!props::PropertySource#definePropertyDescriptor(props::PropertyDescriptor) %}` in the rule's noarg constructor.
You can then retrieve the value of the property at any time using {% jdoc !a!props::PropertySource#getProperty(props::PropertyDescriptor) %}.
#### Creating a descriptor
### Creating a descriptor
From version 6.0.0 on, properties can be built using specific **builders**. For example, to build a string property, you'd call
Properties can be built using type-specific **builders**, which can be obtained
from the factory methods of {% jdoc :PF %}. For example, to build a
string property, you'd call
```java
StringProperty.named("myProperty")
.desc("This is my property")
.defaultValue("foo")
.build();
PropertyFactory.stringProperty("myProperty")
.desc("This is my property")
.defaultValue("foo")
.build();
```
This is fairly more readable than a constructor call, but keep in mind the description and the default value are not optional.
{%include note.html content="The constructors may be deprecated in a future release, so please use the builders instead." %}
{%include note.html
content='As of version 6.10.0, all property concrete classes are deprecated for
removal in 7.0.0. See the <a href="pmd_next_major_development.html#properties-framework">detailed list of planned removals</a> for
information about how to migrate.' %}
For **numeric properties**, you'd add a call to `range` to define the range of acceptable values, e.g.
For **numeric properties**, you can add constraints on the range of acceptable values, e.g.
```java
IntegerProperty.named("myIntProperty")
PropertyFactory.intProperty("myIntProperty")
.desc("This is my property")
.defaultValue(3)
.require(positive())
.range(0, 100)
.build();
```
**Enumerated properties** are a bit less straightforward to define, though they are arguably more powerful. These properties don't have a specific value type, instead, you can choose any type of value, provided the values are from a closed set. To make that actionable, you give string labels to each of the acceptable values, and the user will provide one of those labels as a value in the XML. The property will give you back the associated value, not the label. Here's an example:
The {% jdoc props::constraints.NumericConstraints#positive() %} method is part of
the {% jdoc props::constraints.NumericConstraints %} class, which provides some
other constraints. The constraint mechanism will be completely unlocked with 7.0.0,
since we'll be migrating our API to Java 8.
**Enumerated properties** are a bit less straightforward to define, though they are
arguably more powerful. These properties don't have a specific value type, instead,
you can choose any type of value, provided the values are from a closed set. To make
that actionable, you give string labels to each of the acceptable values, and the user
will provide one of those labels as a value in the XML. The property will give you back
the associated value, not the label. Here's an example:
```java
static Map<String, ModeStrategy> map = new HashMap<>();
@ -89,46 +88,54 @@ static {
map.put("hardMode", new HardStrategy());
}
static EnumeratedProperty<ModeStrategy> modeProperty
= EnumeratedProperty.<ModeStrategy>named("modeProperty")
.desc("This is my property")
.defaultValue(new EasyStrategy())
.mappings(map)
.type(ModeStrategy.class)
.build();
static PropertyDescriptor<ModeStrategy> modeProperty
= PropertyFactory.enumProperty("modeProperty", map)
.desc("This is my property")
.defaultValue(new EasyStrategy())
.build();
```
Note that you're required to fill in the type of the values too, using `type()`.
### Example
#### Example
You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/ac2ff0f6af8d16f739584ba8d00b7ea1a6311ccc/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/AvoidDeeplyNestedIfStmtsRule.java#L17).
You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/d06b01785a712e61d33f366520f37c2473f5bd1a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java#L43-L52).
There are several things to notice here:
* The property descriptor is declared `static final`, which should generally be the case, as descriptors are immutable and can be shared between instances of the same rule;
* The property is declared using `definePropertyDescriptor` *in the constructor*, which ensures the property gets recognised by PMD;
* The value of the property is *not retrieved in the constructor*, but in one of the `visit` methods (typically on the highest node in the tree, since the property doesn't change).
* The property descriptors are declared `static final`, which should generally be
the case, as descriptors are immutable and can be shared between instances of the same rule;
* The property is declared using {% jdoc props::PropertySource#definePropertyDescriptor(props::PropertyDescriptor) %}` *in the constructor*,
which ensures the property gets recognised by PMD at the time the properties
are overridden (which happens before rule execution);
* The value of the property is *not retrieved in the constructor*, but in one of
the `visit` methods (typically on the highest node in the tree, since the property
doesn't change).
### For XPath rules
## For XPath rules
XPath rules can also define their own properties. To do so, you must add a `property` element in the `properties` element of your rule, which **declares the `type` attribute**. This attribute conditions what type the underlying property has, and can have the following values:
| `type` attribute | Property type|
| `type` attribute | XSD type
|----------|----------|
|Integer|IntegerProperty
|Double | DoubleProperty
|Float|FloatProperty
|Long| LongProperty
|String|StringProperty
|Character|CharacterProperty
|Boolean|BooleanProperty
|Class|TypeProperty
|Regex|RegexProperty
|Integer | xs:integer
|Long | xs:integer
|Double | xs:decimal
|Boolean | xs:boolean
|String | xs:string
|Character| xs:string
|Regex | xs:string
{% include note.html
content="In XPath 1.0 mode, all values are actually represented as
string values, which is mostly fine as there is no type
checking. This is a problem when [migrating from XPath 1.0
to 2.0](pmd_userdocs_extending_writing_xpath_rules.html#migrating-from-10-to-20) though" %}
Note that enumerated properties are not available in XPath rules (yet?).
Properties defined in XPath also *must* declare the `description` attribute. Numeric properties also expect the `min` and `max` attributes. Here are a few examples to sum it up:
Properties defined in XPath also *must* declare the `description` attribute.
Numeric properties also expect the `min` and `max` attributes for now. Here are
a few examples to sum it up:
```xml
<property name="stringProp" type="Boolean" value="true" description="A BooleanProperty."/>
@ -150,9 +157,14 @@ You can then use the property in XPath with the syntax `$propertyName`, for exam
</rule>
```
#### Multivalued properties
### Multivalued properties
Multivalued properties are also allowed and their `type` attribute has the form `List[Boolean]` or `List[Character]`, with every above type allowed. These properties **require XPath 2.0** to work properly, and make use of the **sequence datatype** provided by that language. You thus need to set the `version` property to `2.0` to use them. Properties can also declare the `delimiter` attribute.
Multivalued properties are also allowed and their `type` attribute has the form
`List[Boolean]` or `List[Character]`, with every above type allowed. These
properties **require XPath 2.0** to work properly, and make use of the
**sequence datatype** provided by that language. You thus need to set the
`version` property to `2.0` to use them. Properties can also declare the
`delimiter` attribute.
@ -171,5 +183,10 @@ Multivalued properties are also allowed and their `type` attribute has the form
</rule>
```
Notice that in the example above, `@Image = $reportedIdentifiers` doesn't test `@Image` for equality with the whole sequence `('foo', 'bar')`, it tests whether the sequence *contains* `@Image`. That is, the above rule will report all variables named `foo` or `bar`. All other XPath 2.0 [functions operating on sequences](https://www.w3.org/TR/xpath-functions/#sequence-functions) are supported.
Notice that in the example above, `@Image = $reportedIdentifiers` doesn't test
`@Image` for equality with the whole sequence `('foo', 'bar')`, it tests whether
the sequence *contains* `@Image`. That is, the above rule will report all variables
named `foo` or `bar`. All other XPath 2.0 [functions operating on sequences](https://www.w3.org/TR/xpath-functions/#sequence-functions)
are supported.
{%include tip.html content="You can also [define properties directly in the designer](pmd_userdocs_extending_designer_reference.html#rule-properties)" %}

View File

@ -1,15 +1,12 @@
---
title: Rule guidelines
tags: [extending, userdocs]
summary: Rule Guidelines
last_updated: July 3, 2016
summary: "Rule Guidelines, or the last touches to a rule"
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_rule_guidelines.html
author: Xavier Le Vourch, Ryan Gustafson, Romain Pelisse
---
# Rule Guidelines
Or - Last touches to a rules
Here is a bunch of thing to do you may consider once your rule is “up and running”.
@ -25,15 +22,6 @@ Rule priority may, of course, changes a lot depending on the context of the proj
For instance, lets take the ExplicitCallToGC rule (“Do not explicitly trigger a garbage collection.”). Calling GC is a bad idea, but it doesnt break the application. So we skip priority one. However, as explicit call to gc may really hinder application performances, we set for the priority 2.
## Code formatting
We try to keep a consistent code formatting through out PMD code base to ensure an easier maintenance and also make
the pull request as readable as possible.
In order to ensure this, we use a PMD specific Eclipse formatter configuration, which is maintained in a
separate project - "build-tools": [eclipse-code-formatter.xml](https://github.com/pmd/build-tools/blob/master/eclipse/pmd-eclipse-code-formatter.xml).
Please do not forget to use it before committing or any source code!
## Correctness
You should try to run the rule on a large code base, like the jdk source code for instance. This will help ensure that the rule does not raise exceptions when dealing with unusual constructs.
@ -47,28 +35,3 @@ When writing a new rule, using command line option “-benchmark” on a few rul
Rules which use the RuleChain to visit the AST are faster than rules which perform manual visitation of the AST. The difference is small for an individual Java rule, but when running 100s of rules, it is measurable. For XPath rules, the difference is extremely noticeable due to Jaxen overhead for AST navigation. Make sure your XPath rules using the RuleChain.
(TODO How does one know except by running in a debugger or horrendous performance?).
## Adding test cases
See [Testing your rules](pmd_userdocs_extending_testing.html) for the general documentation
### … for a rule I want to submit (in a patch)
Figure out the category to which you want to the rule. Then add your rule to the appropriate test class for
the category and add the XML test data in the correct xml subpackage.
### … for something too specific, that I wont be able to submit
See [Using the test framework externally](pmd_userdocs_extending_testing.html#using-the-test-framework-externally)
## Code quality
If you want to contribute a java rule to PMD, you should run PMD on it (Using the dogfood rulesets), to ensure that you rule follow the rules defined by the PMD community.
Also note, that if this is not a strong policy, most developers uses the berkeley braces syntax.
## Committing
Before committing changes, make sure the verify phase of a maven build succeeds without test failures. Drink a beer while you wait for it to finish.
Then read the output to make sure no fatal errors are present.

View File

@ -0,0 +1,164 @@
---
title: Writing a custom rule
tags: [extending, userdocs]
summary: "Learn how to write a custom rule for PMD"
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_writing_java_rules.html
author: Tom Copeland <tomcopeland@users.sourceforge.net>
---
{% jdoc_nspace :coremx core::lang.metrics %}
{% jdoc_nspace :coreast core::lang.ast %}
{% jdoc_nspace :jmx java::lang.java.metrics %}
{% jdoc_nspace :jast java::lang.java.ast %}
{% jdoc_nspace :jrule java::lang.java.rule %}
{% include note.html content="TODO All that should be written in the Javadocs,
not sure we even need a doc page. Would be simpler to maintain too" %}
{% include warning.html content="WIP lots of stuff missing" %}
This page covers the specifics of writing a rule in Java. The basic development
process is very similar to the process for XPath rules, which is described in
[Your First Rule](pmd_userdocs_extending_your_first_rule.html#rule-development-process).
Basically, you open the designer, look at the structure of the AST, and refine
your rule as you add test cases.
In this page we'll talk about rules for the Java language, but the process is
very similar for other languages.
## Basics
To write a rule in Java you'll have to:
1. write a Java class that implements the interface {% jdoc core::Rule %}. Each
language implementation provides a base rule class to ease your pain,
e.g. {% jdoc jrule::AbstractJavaRule %}.
2. compile this class, linking it to PMD APIs (eg using PMD as a maven dependency)
3. bundle this into a JAR and add it to the execution classpath of PMD
4. declare the rule in your ruleset XML
## Rule execution
Most base rule classes use a [Visitor pattern](https://sourcemaking.com/design_patterns/visitor)
to explore the AST.
### Tree traversal
When a rule is applied to a file, it's handed the root of the AST and told
to traverse all the tree to look for violations. Each rule defines a specific
`visit` method for each type of node for of the language, which
by default just visits the children.
So the following rule would traverse the whole tree and do nothing:
```java
public class MyRule extends AbstractJavaRule {
// all methods are default implementations!
}
```
Generally, a rule wants to check for only some node types. In our XPath example
in [Your First Rule](pmd_userdocs_extending_your_first_rule.html),
we wanted to check for some `VariableDeclaratorId` nodes. That's the XPath name,
but in Java, you'll get access to the {% jdoc jast::ASTVariableDeclaratorId %}
full API.
If you want to check for some specific node types, you can override the
corresponding `visit` method:
```java
public class MyRule extends AbstractJavaRule {
@Override
public Object visit(ASTVariableDeclaratorId node, Object data) {
// This method is called on each node of type ASTVariableDeclaratorId
// in the AST
if (node.getType() == short.class) {
// reports a violation at the position of the node
// the "data" parameter is a context object handed to by your rule
// the message for the violation is the message defined in the rule declaration XML element
addViolation(data, node);
}
// this calls back to the default implementation, which recurses further down the subtree
return super.visit(node, data);
}
}
```
The `super.visit(node, data)` call is super common in rule implementations,
because it makes the traversal continue by visiting all the descendants of the
current node.
#### Stopping the traversal
Sometimes you have checked all you needed and you're sure that the descendants
of a node may not contain violations. In that case, you can avoid calling the
`super` implementation and the traversal will not continue further down. This
means that your callbacks (`visit` implementations) won't be called on the rest
of the subtree. The siblings of the current node may be visited
recursively nevertheless.
#### Economic traversal: the rulechain
If you don't care about the order in which the nodes are traversed (e.g. your
rule doesn't maintain any state between visits), then you can monumentally
speed-up your rule by using the **rulechain**.
That mechanism doesn't recurse on all the tree, instead, your rule will only be
passed the nodes it is interested in. To use the rulechain correctly:
* Your rule must register those node types by calling {% jdoc core::Rule#addRuleChainVisit(java.lang.Class) %}
in its constructor.
* Your visit methods **must not recurse!** In effect, you should call never
call `super.visit` in the methods.
### Execution across files, thread-safety and statefulness
When starting execution, PMD will instantiate a new instance of your rule.
If PMD is executed in multiple threads, then each thread is using its own
instance of the rule. This means, that the rule implementation **does not need to care about
threading issues**, as PMD makes sure, that a single instance is not used concurrently
by multiple threads.
However, for performance reasons, the rule instances are used for multiple files.
This means, that the constructor of the rule is only executed once (per thread)
and the rule instance is reused. If you rely on a proper initialization of instance
properties, you can do the initialization e.g. in the visit-method of the {% jdoc jast::ASTCompilationUnit %}
node - which is visited first and only once per file. However, this
solution would only work for rules written for the Java language. A language
independent way is to override the method `start` of the rule.
The start method is called exactly once per file.
<!-- We don't support language-independent rules anyway... -->
## Rule lifecycle reference
### Construction
Exactly once:
1. The rule's no-arg constructor is called when loading the ruleset.
The rule's constructor must define:
* [Rulechain visits](#economic-traversal-the-rulechain)
* [Property descriptors](pmd_userdocs_extending_defining_properties.html#for-java-rules)
2. If the rule was included in the ruleset as a rule reference,
some properties [may be overridden](pmd_userdocs_configuring_rules.html#rule-properties).
If an overridden property is unknown, an error is reported.
3. Misconfigured rules are removed from the ruleset
### Execution
For each thread, a deep copy of the rule is created. Each thread is given
a different set of files to analyse. Then, for each such file, for each
rule copy:
3. {% jdoc core::Rule#start(core::RuleContext) %} is called once, before parsing
4. {% jdoc core::Rule#apply(java.util.List,core::RuleContext) %} is called with the root
of the AST. That method performs the AST traversal that ultimately calls visit methods.
It's not called for RuleChain rules.
5. {% jdoc core::Rule#end(core::RuleContext) %} is called when the rule is done processing
the file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
---
title: Introduction to writing PMD rules
tags: [extending, userdocs, getting_started]
summary: "Writing your own PMD rules"
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_writing_rules_intro.html
author: Clément Fournier <clement.fournier76@gmail.com>
---
PMD is a framework to perform code analysis. You can create your own rules to
check for patterns specific to your codebase, or the coding practices of your
team.
## How rules work: the AST
Before running rules, PMD parses the source file into a data structure called an
**abstract syntax tree (AST)**. This tree represents the syntactic structure of the
code, and encodes syntactic relations between source code elements. For instance,
in Java, method declarations belong to a class: in the AST, the nodes representing
method declarations will be descendants of a node representing the declaration of
their enclosing class. This representation is thus much richer than the original
source code (which, for a program, is just a chain of characters), or the token
chain produced by a lexer (which is e.g. what Checkstyle works on). For example:
<table>
<colgroup>
<col width="40%" />
<col width="70%" />
</colgroup>
<thead>
<tr class="header">
<th>Sample code (Java)</th>
<th>AST</th>
</tr>
</thead>
<tbody>
<tr>
<td markdown="block">
```java
class Foo extends Object {
}
```
</td>
<td markdown="block">
```java
└─ CompilationUnit
└─ TypeDeclaration
└─ ClassOrInterfaceDeclaration "Foo"
├─ ExtendsList
│ └─ ClassOrInterfaceType "Object"
└─ ClassOrInterfaceBody
```
</td>
</tr>
</tbody>
</table>
Conceptually, PMD rules work by **matching a "pattern" against the AST** of a
file.
Rules explore the AST and find nodes that satisfy some conditions that are characteristic
of the specific thing the rule is trying to flag. Rules then report a violation on these nodes.
### Discovering the AST
ASTs are represented by Java classes deriving from {% jdoc core::lang.ast.Node %}.
Each PMD language has its own set of such classes, and its own rules about how
these classes relate to one another, based on the grammar of the language. For
example, all Java AST nodes extend {% jdoc java::lang.java.ast.JavaNode %}.
The structure of the AST can be discovered through
* the [Rule Designer](pmd_userdocs_extending_designer_reference.html#ast-inspection)
* the [AST dump feature](pmd_devdocs_experimental_ast_dump.html)
## Writing new rules
PMD supports two ways to define rules: using an **XPath query**, or using a
**Java visitor**. XPath rules are much easier to set up, since they're defined
directly in your ruleset XML, and are expressive enough for nearly any task.
On the other hand, some parts of PMD's API are only accessible from Java, e.g.
accessing the usages of a declaration. And Java rules allow you to do some
complicated processing, to which an XPath rule couldn't scale.
In the end, choosing one strategy or the other depends on the difficulty of what
your rule does. I'd advise to keep to XPath unless you have no other choice.
## XML rule definition
New rules must be declared in a ruleset before they're referenced. This is the
case for both XPath and Java rules. To do this, the `rule` element is used, but
instead of mentioning the `ref` attribute, it mentions the `class` attribute,
with the implementation class of your rule.
* **For Java rules:** this is the class extending AbstractRule (transitively)
* **For XPath rules:** this is `net.sourceforge.pmd.lang.rule.XPathRule`
Example:
```xml
<rule name="MyJavaRule"
language="java"
message="Violation!"
class="com.me.MyJavaRule" >
<description>
Description
</description>
<priority>3</priority>
</rule>
```
## Resource index
To learn how to write a rule:
* [Your First Rule](pmd_userdocs_extending_your_first_rule.html)
introduces the basic development process of a rule with a running example
* [Writing XPath Rules](pmd_userdocs_extending_writing_xpath_rules.html)
explains a bit more about XPath rules and our XPath API
* [Writing Java Rules](pmd_userdocs_extending_writing_java_rules.html)
describes how to write a rule in Java
To go further:
* [Defining Properties](pmd_userdocs_extending_defining_properties.html)
describes how to make your rules more configurable with rule properties
* [Testing your Rules](pmd_userdocs_extending_testing.html) introduces
our testing framework and how you can use it to safeguard the quality of
your rule

View File

@ -1,198 +1,142 @@
---
title: Writing XPath rules
tags: [extending, userdocs]
summary: "Writing XPath rules for PMD"
last_updated: July 3, 2016
summary: "This page describes XPath rule support in more details"
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_writing_xpath_rules.html
author: Miguel Griffa <mikkey@users.sourceforge.net>
author: Miguel Griffa <mikkey@users.sourceforge.net>, Clément Fournier <clement.fournier76@gmail.com>
---
# XPath Rule tutorial
{% include note.html content="For a translation to Georgian, see [webhostinggeeks.com/science/xpath-sourceforge-ka](http://webhostinggeeks.com/science/xpath-sourceforge-ka)" %}
{% jdoc_nspace :coremx core::lang.metrics %}
{% jdoc_nspace :coreast core::lang.ast %}
{% jdoc_nspace :jmx java::lang.java.metrics %}
{% jdoc_nspace :jast java::lang.java.ast %}
Writing PMD rules with XPath can be a bit easier than writing rules with Java code. Heres an introduction on how to do that.
This page describes some points of XPath rule support in more details. See
also [the tutorial about how to write an XPath rule](pmd_userdocs_extending_your_first_rule.html).
## Introduction
<!-- Later we can document the specific subset of XPath features our wrappers support -->
PMD provides a very handy method for writing rules by writing an XPath query. When the XPath query finds a match, a violation is added to the report. This document focuses on XPath rules. You can go [here](pmd_userdocs_extending_writing_pmd_rules.html) for more information about writing a rule.
## XPath version
## What is the Abstract Syntax Tree (AST)?
PMD supports three XPath versions for now: 1.0, 2.0, and 1.0 compatibility mode.
The version can be specified with the `version` property in the rule definition, like so:
From [FOLDOC](http://foldoc.org/abstract+syntax+tree) an AST is
> A data structure representing something which has been parsed, often used as a compiler or interpreters internal representation of a program while it is being optimised and from which code generation is performed.
In our context, this means that we basically have a tree representation of the Java source file. This tree can viewed as a structured document - just like XML. And since its conceptually similar to XML, it can be queried with XPath to find a pattern.
## Using Designer
PMD comes with a handy tool that you will love if you want to write an XPath rule. Designer, runnable from a script in `bin/`, is a very simple and useful utility for writing rules.
The basic steps involved in writing XPath rules are these:
1. Write a simple Java example source snippet in Designer
2. See the AST for the class you wrote
3. Write an XPath expression that matches the violation you are searching
4. Modify the Java class and go back to previous step to refine the XPath expression
See [Designer Reference](pmd_userdocs_extending_designer_reference.html) for a more detailed explanation on how to use the designer.
## Simple XPath expressions
This section provides hands-on examples of XPath queries over the AST. You will probably find this section more useful if you follow it with Designer and copy/paste the examples.
Copy the following Java source code to Designer:
```java
public class a {
int fOne;
int fTwo;
private void run() {
int one;
int two;
}
}
```xml
<property version="2.0" /> <!-- or "1.0", or "1.0 compatibility" -->
```
Lets assume you want to match something on class variable names. You see in the ASTVviewer that VariableDeclaratorId contains the variable name - in XML terms, the name is in the `@Image` attribute. So you try an XPath expression as follows:
The default has always been version 1.0.
`//VariableDeclaratorId`
**As of PMD version 6.22.0, XPath versions 1.0 and the 1.0 compatibility mode are
deprecated**. XPath 2.0 is superior in many ways, for example for its support for
type checking, sequence values, or quantified expressions. For a detailed
but approachable review of the features of XPath 2.0 and above, see [the Saxon documentation](https://www.saxonica.com/documentation/index.html#!expressions).
If you try this expression youll see that variables declared in methods are also matched. A more precise expression for matching field declarations is, well, using the FieldDeclaration node. This expression matches only the two fields declared in the class:
It is recommended that you migrate to 2.0 before 7.0.0, but we expect
to be able to provide an automatic migration tool when releasing 7.0.0.
See [the migration guide](#migrating-from-10-to-20) below.
`//FieldDeclaration`
In a similar way, you can match only local variables with this expression
## DOM representation of ASTs
`//LocalVariableDeclaration`
XPath rules view the AST as an XML-like DOM, which is what the XPath language is
defined on. Concretely, this means:
* Every AST node is viewed as an XML element
* The element has for local name the value of {% jdoc core::lang.ast.Node#getXPathNodeName() %}
for the given node
* Some Java getters are exposed as XML attributes on those elements
* This means, that documentation for attributes can be found in our Javadocs. For
example, the attribute `@SimpleName` of the Java node `EnumDeclaration` is backed
by the Java getter {% jdoc java::lang.java.ast.ASTAnyTypeDeclaration#getSimpleName() %}.
With local variables we need to be more careful. Consider the following class:
### Value conversion
```java
public class a {
private void run() {
final int one;
int two;
To represent attributes, we must map Java values to [XPath Data Model (XDM)](https://www.w3.org/TR/xpath-datamodel/) values. The conversion
depends on the XPath version used.
{
int a;
}
}
}
```
#### XPath 1.0
Local variable declarations will match a, since it is a perfectly legal Java local variable. Now, a more interesting expression is to match variables declared in a method, and not on an internal block, nor in the class. Maybe youll start with an expression like this:
On XPath 1.0 we map every Java value to an `xs:string` value by using the `toString`
of the object. Since XPath 1.0 allows many implicit conversions this works, but it
causes some incompatibilities with XPath 2.0 (see the section about migration further
down).
`//MethodDeclaration//LocalVariableDeclaration`
#### XPath 2.0
Youll quickly see that all three local variables are matched. A possible solution for this is to request that the parent of the local variable declaration is the MethodDeclaration node:
XPath 2.0 is a strongly typed language, and so we use more precise type annotations.
In the following table we refer to the type conversion function as `conv`, a
function from Java types to XDM types.
`//LocalVariableDeclaration[name(../../..) = 'MethodDeclaration']`
| Java type `T` | XSD type `conv(T)`
|-----------------|---------------------|
|`int` | `xs:integer`
|`long` | `xs:integer`
|`double` | `xs:decimal`
|`float` | `xs:decimal`
|`boolean` | `xs:boolean`
|`String` | `xs:string`
|`Character` | `xs:string`
|`Enum<E>` | `xs:string` (uses `Object::toString`)
|`List<E>` | `conv(E)*` (a sequence type)
## Matching variables by name
Lets consider that we are writing rules for logger. Lets assume we use the Java logging API and we want to find all classes that have more than one logger. The following expression returns all variable declarations whose type is Logger.
The same `conv` function is used to translate rule property values to XDM values.
`//VariableDeclarator[../Type/ReferenceType/ClassOrInterfaceType[@Image='Logger']]`
Finding a class with more than one logger is quite easy now. This expression matches the classes we are looking for.
## Migrating from 1.0 to 2.0
```xpath
TypeDeclaration[count(//VariableDeclarator[../Type/ReferenceType/ClassOrInterfaceType[@Image='Logger']])>1
```
XPath 1.0 and 2.0 have some incompatibilities. The [XPath 2.0 specification](https://www.w3.org/TR/xpath20/#id-incompat-in-false-mode)
describes them precisely. Those are however mostly corner cases and XPath
rules usually don't feature any of them.
But lets refine this expression a little bit more. Consider the following class:
The incompatibilities that are most relevant to migrating your rules are not
caused by the specification, but by the different engines we use to run
XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities:
```java
public class a {
Logger log = null;
Logger log = null;
int b;
* The namespace prefixes `fn:` and `string:` should not be mentioned explicitly.
In XPath 2.0 mode, the engine will complain about an undeclared namespace, but
the functions are in the default namespace. Removing the namespace prefixes fixes it.
* <code><b style="color:red">fn:</b>substring("Foo", 1)</code> &rarr; `substring("Foo", 1)`
* Conversely, calls to custom PMD functions like `typeIs` *must* be prefixed
with the namespace of the declaring module (`pmd-java`).
* `typeIs("Foo")` &rarr; <code><b style="color:green">pmd-java:</b>typeIs("Foo")</code>
* Boolean attribute values on our 1.0 engine are represented as the string values
`"true"` and `"false"`. In 2.0 mode though, boolean values are truly represented
as boolean values, which in XPath may only be obtained through the functions
`true()` and `false()`.
If your XPath 1.0 rule tests an attribute like `@Private="true"`, then it just
needs to be changed to `@Private=true()` when migrating. A type error will warn
you that you must update the comparison. More is explained on [issue #1244](https://github.com/pmd/pmd/issues/1244).
* `"true"`, `'true'` &rarr; `true()`
* `"false"`, `'false'` &rarr; `false()`
void myMethod() {
Logger log = null;
int a;
}
class c {
Logger a;
Logger a;
}
}
```
* In XPath 1.0, comparing a number to a string coerces the string to a number.
In XPath 2.0, a type error occurs. Like for boolean values, numeric values are
represented by our 1.0 implementation as strings, meaning that `@BeginLine > "1"`
worked ---that's not the case in 2.0 mode.
* <code>@ArgumentCount > <b style="color:red">'</b>1<b style="color:red">'</b></code> &rarr; `@ArgumentCount > 1`
With this class we will only be matching one violation, when we probably would have wanted to produce two violations (one for each class). The following refined expression matches classes that contain more than one logger.
## Rule properties
```xpath
//ClassOrInterfaceBodyDeclaration[count(//VariableDeclarator[../Type/ReferenceType/ClassOrInterfaceType[@Image='Logger']])>1]
```
**See [Defining rule properties](pmd_userdocs_extending_defining_properties.html#for-xpath-rules)**
Lets assume we have a Factory class, that could be always declared final. Well search an xpath expression that matches all declarations of Factory and reports a violation if it is not declared final. Consider the following class:
```java
public class a {
Factory f1;
## PMD extension functions
void myMethod() {
Factory f2;
int a;
}
}
```
PMD provides some language-specific XPath functions to access semantic
information from the AST.
The following expression does the magic we need:
On XPath 2.0, the namespace of custom PMD function must be explicitly mentioned.
```xpath
//VariableDeclarator
[../Type/ReferenceType/ClassOrInterfaceType
[@Image = 'Factory'] and ..[@Final='false']]
```
{% render %}
{% include custom/xpath_fun_doc.html %}
{% endrender %}
We recommend at this point that you experiment with Designer putting the final modifier to the Factory and verifying that the results produced are those expected.
{% include note.html content='There is also a `typeOf` function which is
deprecated and whose usages should be replaced with uses of `typeIs` or
`typeIsExactly`. That one will be removed with PMD 7.0.0.' %}
## Creating a new rule definition
To actually use your new XPath rule, it needs to be in a ruleset. You can create a new custom ruleset which just
contains your new XPath rule. You can use the following template. Just make sure, to replace the `xpath` property,
the example code and give your rule a useful name and message.
``` xml
<?xml version="1.0"?>
<ruleset name="Custom Rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Custom rules
</description>
<rule name="My Rule"
language="java"
message="violation message"
class="net.sourceforge.pmd.lang.rule.XPathRule">
<description>
Rule Description
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value><![CDATA[
--- here comes your XPath expression
]]></value>
</property>
</properties>
<example>
<![CDATA[
public class ExampleCode {
public void foo() {
}
}
]]>
</example>
</rule>
</ruleset>
```

View File

@ -0,0 +1,142 @@
---
title: Your first rule XPath
tags: [extending, userdocs]
summary: "Introduction to rule writing through an example."
last_updated: February 2020 (6.22.0)
permalink: pmd_userdocs_extending_your_first_rule.html
author: Miguel Griffa <mikkey@users.sourceforge.net>, Clément Fournier <clement.fournier76@gmail.com>
---
This page is a gentle introduction to rule writing, and the Rule Designer.
Using the designer is useful both to write Java
rules and XPath rules, but it's more specifically geared towards XPath rules.
This page uses a simple XPath rule to illustrate the common workflow. We assume
here that you already know what XPath is and how to read basic XPath queries. W3C
has a good tutorial [here](https://www.w3schools.com/xml/xpath_syntax.asp) if
you don't (in the context of XML only), and [the Saxon documentation](https://www.saxonica.com/documentation/index.html#!expressions)
features a comprehensive but approachable description of the syntax of XPath
expressions.
## The Rule Designer
The rule designer is a tool that packs a lot of features to help you develop XPath
rules quickly and painlessly. Basically, it allows you to examine the AST of a code
snippet and evaluate an XPath expression against it.
Like for PMD and CPD, you can launch it using `run.sh designer` on Linux/Unix
and `designer.bat` on Windows. The interface looks like the following:
{% include image.html file="userdocs/designer-overview-with-nums.png" alt="Designer overview" %}
The zone (2) is the **main editor**. When you write a code snippet in the
code area to the left, you'll see that the tree to the right will be updated
automatically: it's the AST of the code.
Note that the code snippet must be a syntactically valid compilation unit for the
language you've chosen, e.g. for Java, a compilation unit necessarily has a top-level
type declaration.
If you select a node in the AST, its specific properties will also be displayed
in the panel (1): they're the XPath attributes of the node. More on that later.
The zone (3) is the **XPath editor**. If you enter an XPath query in that area,
it will be evaluated on the current AST and the results will be displayed in the
list to the bottom right.
### Rule development process
The basic development process is straightforward:
1. Write a code snippet in the main editor that features the offending code you're looking for
2. Examine the AST and determine what node the violation should be reported on
3. Write an XPath expression matching that node in the XPath editor
4. Refine the XPath expression iteratively using different code snippets, so that
it matches violation cases, but no other node
5. Export your XPath expression to an XML rule element, and place it in your ruleset
Each time you test your rule against a different snippet, it's a good idea to
save it to [make test cases](pmd_userdocs_extending_testing.html).
In the following sections, we walk through several examples to refine your rule.
## A simple rule
Let's say you want to prevent your coding team from naming variables of type
`short` after your boss, whose name is Bill. You try the designer on the following
offending code snippet:
```java
public class KeepingItSerious {
public void method() {
short bill; // LocalVariableDeclaration
}
}
```
Examining the AST, you find out that the LocalVariableDeclaration has a VariableDeclaratorId
descendant, whose `Image` XPath attribute is exactly `bill`. You thus write your first attempt
in the XPath editor:
```xpath
//VariableDeclaratorId[@Image = "bill"]
```
You can see the XPath result list is updated with the variable declarator.
If you try the query against the following updated snippet though, you can
see that the field declaration id is matched even though it's not of type `short`.
```java
public class KeepingItSerious {
Delegator bill; // FieldDeclaration
public void method() {
short bill; // LocalVariableDeclaration
}
}
```
You thus refine your XPath expression with an additional predicate,
based on your examination of the Type node of the field and local variable
declaration nodes.
```xpath
//VariableDeclaratorId[@Image = "bill" and ../../Type[@TypeImage = "short"]]
```
### Exporting to XML
You estimate that your rule is now production ready, and you'd like to use it in your ruleset.
The `File > Export XPath to rule...` allows you to do that in a few clicks: just enter some
additional metadata for your rule, and the popup will generate an XML element that you can
copy-paste into your ruleset XML. The resulting element looks like so:
```xml
<rule name="DontCallBossShort"
language="java"
message="Boss wants to talk to you."
class="net.sourceforge.pmd.lang.rule.XPathRule" >
<description>
TODO
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//VariableDeclaratorId[../../Type[@TypeImage="short"] and @Image = "bill"]
]]>
</value>
</property>
</properties>
</rule>
```
You can notice that your XPath expression ends up inside a [property](pmd_userdocs_configuring_rules.html#rule-properties)
of a rule of type XPathRule, which is how XPath rules are implemented.

View File

@ -32,7 +32,7 @@ for you:
changing the Rule, but you do not need to submit a patch back to the
PMD project.
If you need to modify the Rule, see [How to write a rule](pmd_userdocs_extending_writing_pmd_rules.html).
If you need to modify the Rule, see [How to write a rule](pmd_userdocs_extending_writing_rules_intro.html).
Otherwise, the other suppression methods are explained in the following sections.
## Annotations

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