Merge branch '7.0.x' into generic-visitor2
104
antlr4-wrapper.xml
Normal file
@ -0,0 +1,104 @@
|
||||
<!--
|
||||
~ BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
-->
|
||||
|
||||
<project name="pmd" default="adapt-antlr-sources" basedir="../../../../">
|
||||
|
||||
<!-- Input properties:
|
||||
- lang-name: matches the grammar name (eg "Swift")
|
||||
- lang-terse-name: uncapitalized package name (eg "swift")
|
||||
- node-prefix: prefix for generated AST nodes (eg "Sw")
|
||||
- root-node-name: name of the root node (eg "TopLevel"), will be made to implement RootNode
|
||||
|
||||
See AntlrGeneratedParserBase
|
||||
|
||||
-->
|
||||
|
||||
<property name="target-package-dir" value="${antlr4.outputDirectory}/net/sourceforge/pmd/lang/${lang-terse-name}/ast"/>
|
||||
|
||||
|
||||
<property name="lang-ast-package" value="net.sourceforge.pmd.lang.${lang-terse-name}.ast" />
|
||||
<property name="ast-api-package" value="net.sourceforge.pmd.lang.ast" />
|
||||
<property name="ast-impl-package" value="${ast-api-package}.impl.antlr4" />
|
||||
|
||||
|
||||
<property name="parser-name" value="${lang-name}Parser"/>
|
||||
<property name="parser-file" value="${target-package-dir}/${parser-name}.java"/>
|
||||
|
||||
<property name="visitor-name" value="${lang-name}Visitor"/>
|
||||
<property name="visitor-file" value="${target-package-dir}/${visitor-name}.java"/>
|
||||
|
||||
<property name="base-visitor-name" value="${lang-name}BaseVisitor"/>
|
||||
<property name="base-visitor-file" value="${target-package-dir}/${base-visitor-name}.java"/>
|
||||
|
||||
<property name="node-itf-name" value="${lang-name}Node"/>
|
||||
<property name="base-class-name" value="Abstract${lang-name}Node"/>
|
||||
|
||||
<target name="adapt-antlr-sources" description="Adapt antlr sources to the PMD codebase">
|
||||
|
||||
<!-- Adapt parser. -->
|
||||
<replace file="${parser-file}">
|
||||
<replacefilter token="${root-node-name}Context extends ${lang-name}InnerNode"
|
||||
value="${root-node-name}Context extends ${lang-name}InnerNode implements net.sourceforge.pmd.lang.ast.RootNode"/>
|
||||
|
||||
<replacefilter token="_ctx = _localctx;"
|
||||
value="_ctx = _localctx.asAntlrNode();"/>
|
||||
|
||||
<replacefilter token="public <T> T accept(ParseTreeVisitor<? extends T> visitor)"
|
||||
value="public <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data)" />
|
||||
|
||||
<replacefilter token="((${visitor-name}<? extends T>)visitor)"
|
||||
value="((${visitor-name}<? super P, ? extends R>) visitor)" />
|
||||
|
||||
<replacefilter token="return visitor.visitChildren(this);"
|
||||
value="return visitor.visitNode(this, data);" />
|
||||
|
||||
</replace>
|
||||
|
||||
<replaceregexp flags="g" file="${parser-file}">
|
||||
<regexp pattern="\.visit(\w++)\(this\);"/>
|
||||
<substitution expression=".visit\1(this, data);"/>
|
||||
</replaceregexp>
|
||||
|
||||
<replaceregexp flags="g" file="${parser-file}">
|
||||
<regexp pattern="_sempred\(\((\w+)\)_localctx,"/>
|
||||
<substitution expression="_sempred\((\1) asPmdNode(_localctx),"/>
|
||||
</replaceregexp>
|
||||
|
||||
<!-- Transform the visitor to PMD-style. -->
|
||||
<replace file="${visitor-file}">
|
||||
|
||||
<replacefilter token="Visitor<T> extends ParseTreeVisitor<T> {"
|
||||
value="Visitor<P, R> extends net.sourceforge.pmd.lang.ast.AstVisitor<P, R> {
|
||||
|
||||
/**
|
||||
* The default visit method for ${lang-name} nodes. Unless overridden,
|
||||
* the default implementations of the methods of this interface delegate
|
||||
* to this method. The default calls {@link #visitNode(Node, Object)}.
|
||||
*
|
||||
* @param node Node to visit
|
||||
* @param data Parameter of the visit
|
||||
* @return Result of the visit
|
||||
*/
|
||||
default R visit${node-itf-name}(${node-itf-name} node, P data) { return visitNode(node, data); }
|
||||
|
||||
"/>
|
||||
|
||||
<replacefilter token="T visit" value="default R visit"/>
|
||||
<replacefilter token="ctx);" value="node, P data) { return visit${node-itf-name}(node, data); }"/>
|
||||
|
||||
</replace>
|
||||
|
||||
<!-- This is in the main sources (not much to do). -->
|
||||
<delete file="${base-visitor-file}" />
|
||||
|
||||
|
||||
<replaceregexp flags="g">
|
||||
<fileset dir="${target-package-dir}" />
|
||||
<regexp pattern="\b([A-Z]\w*)Context\b(?<!\b(Parser)?RuleContext\b)"/>
|
||||
<substitution expression="${node-prefix}\1"/>
|
||||
</replaceregexp>
|
||||
|
||||
|
||||
</target>
|
||||
</project>
|
@ -1,10 +1,10 @@
|
||||
repository: pmd/pmd
|
||||
|
||||
pmd:
|
||||
version: 6.25.0-SNAPSHOT
|
||||
version: 7.0.0-SNAPSHOT
|
||||
previous_version: 6.24.0
|
||||
date: ??-June-2020
|
||||
release_type: minor
|
||||
date: ??-2020
|
||||
release_type: major
|
||||
|
||||
# release types: major, minor, bugfix
|
||||
|
||||
|
@ -433,6 +433,9 @@ entries:
|
||||
- title: What does 'PMD' mean?
|
||||
url: /pmd_projectdocs_trivia_meaning.html
|
||||
output: web, pdf
|
||||
- title: Logo
|
||||
url: /pmd_projectdocs_logo.html
|
||||
output: web, pdf
|
||||
- title: FAQ
|
||||
url: /pmd_projectdocs_faq.html
|
||||
output: web, pdf
|
||||
|
BIN
docs/images/logo/PMD.png
Normal file
After Width: | Height: | Size: 24 KiB |
105
docs/images/logo/PMD.svg
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="50.720001"
|
||||
inkscape:export-xdpi="50.720001"
|
||||
inkscape:export-filename="/home/andreas/PMD/source/pmd7/docs/images/logo/pmd-logo-300px.png"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="PMD.svg"
|
||||
xml:space="preserve"
|
||||
enable-background="new 0 0 1600 1200"
|
||||
viewBox="0 0 1600 1200"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata79"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs77" /><sodipodi:namedview
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:current-layer="g72"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-x="26"
|
||||
inkscape:cy="600"
|
||||
inkscape:cx="800"
|
||||
inkscape:zoom="0.748125"
|
||||
showgrid="false"
|
||||
id="namedview75"
|
||||
inkscape:window-height="883"
|
||||
inkscape:window-width="1619"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff" />
|
||||
<g
|
||||
id="g72"><g
|
||||
id="g947"><path
|
||||
fill="none"
|
||||
d="M934.8,558.4h-24.9v79.3h26.1c21.4,0,37.1-16.1,37.1-39.5C973.2,574.7,956.7,558.4,934.8,558.4z"
|
||||
id="path4" /><path
|
||||
fill="#58595B"
|
||||
d="M569.6,682.1c-0.5-3.1-1.1-6.2-1.1-9.3c-0.1-17.6,0-35.2-0.2-52.8c-0.1-7.3-1.9-14.2-7.1-19.7 c-1.8-1.9-3.9-3.6-5.9-5.4c2-1.8,4.1-3.5,5.9-5.4c5.2-5.5,7-12.3,7.1-19.7c0.2-17.6,0.1-35.2,0.2-52.8c0-3.1,0.6-6.2,1.1-9.3 c0.6-4,3.1-6.3,7-6.8h20.8v-22.2h-14.3c-11.2-0.5-22,1-31.3,9c-7.6,6.6-10.1,15.5-10.3,25c-0.4,16.3-0.1,32.6-0.3,48.9 c0,3.6-0.4,7.2-1.3,10.7c-0.1,0.3-0.2,0.6-0.3,0.9c-1.2,3.2-3.2,5.3-5.6,6.8c-1.2,0.7-2.6,1.3-4.1,1.7c-1.3,0.4-2.4,0.6-3.2,0.9 c-0.8,0.1-1.5,0.2-2.2,0.3h-8.5v11.8v11.8h8.5c0.7,0.1,1.4,0.1,2.2,0.3c0.8,0.3,1.8,0.6,3.2,0.9c1.5,0.4,2.9,1,4.1,1.7 c2.3,1.5,4.4,3.6,5.6,6.8c0.1,0.3,0.2,0.6,0.3,0.9c0.9,3.5,1.3,7.1,1.3,10.7c0.2,16.3-0.1,32.6,0.3,48.9c0.2,9.5,2.8,18.4,10.3,25 c9.3,8.1,20.1,9.5,31.3,9h14.3v-22.2h-20.8C572.8,688.4,570.2,686.1,569.6,682.1z"
|
||||
id="path60" /><path
|
||||
fill="#58595B"
|
||||
d="M1075.3,583.3c-0.7-0.1-1.4-0.1-2.2-0.3c-0.8-0.3-1.8-0.6-3.2-0.9c-1.5-0.4-2.9-1-4.1-1.7 c-2.3-1.5-4.4-3.6-5.6-6.8c-0.1-0.3-0.2-0.6-0.3-0.9c-0.9-3.5-1.3-7.1-1.3-10.7c-0.2-16.3,0.1-32.6-0.3-48.9 c-0.2-9.5-2.8-18.4-10.3-25c-9.3-8.1-20.1-9.5-31.3-9h-14.3v22.2h20.8c3.9,0.5,6.4,2.8,7,6.8c0.5,3.1,1.1,6.2,1.1,9.3 c0.1,17.6,0,35.2,0.2,52.8c0.1,7.3,1.9,14.2,7.1,19.7c1.8,1.9,3.9,3.6,5.9,5.4c-2,1.8-4.1,3.5-5.9,5.4c-5.2,5.5-7,12.3-7.1,19.7 c-0.2,17.6-0.1,35.2-0.2,52.8c0,3.1-0.6,6.2-1.1,9.3c-0.6,4-3.1,6.3-7,6.8h-20.8v22.2h14.3c11.2,0.5,22-1,31.3-9 c7.6-6.6,10.1-15.5,10.3-25c0.4-16.3,0.1-32.6,0.3-48.9c0-3.6,0.4-7.2,1.3-10.7c0.1-0.3,0.2-0.6,0.3-0.9c1.2-3.2,3.2-5.3,5.6-6.8 c1.2-0.7,2.6-1.3,4.1-1.7c1.3-0.4,2.4-0.6,3.2-0.9c0.8-0.1,1.5-0.2,2.2-0.3h8.5v-11.8v-11.8H1075.3z"
|
||||
id="path62" /><g
|
||||
id="g70">
|
||||
<polygon
|
||||
fill="#1DBF73"
|
||||
points="791,612.3 753.7,534.1 721.4,534.1 721.4,662 747.5,662 747.5,575.8 781.5,649.1 800.3,649.1 834.3,575.8 834.3,662 860.2,662 860.2,534.1 828.1,534.1 "
|
||||
id="polygon64" />
|
||||
<path
|
||||
fill="#1DBF73"
|
||||
d="M935.3,534.1h-54.3V662h53.2c40.4,0,68.3-26.3,68.3-64C1002.6,560.2,975,534.1,935.3,534.1z M936.1,637.7 h-26.1v-79.3h24.9c21.9,0,38.4,16.3,38.4,39.8C973.2,621.6,957.5,637.7,936.1,637.7z"
|
||||
id="path66" />
|
||||
<path
|
||||
fill="#1DBF73"
|
||||
d="M723.3,525.6c-4,2.2-7.9,4.7-11.7,7.2c-7.4,5-14.1,10.3-19.8,15.2c-2.7,2.4-5.2,4.6-7.4,6.7 c-1.1,0.9-2.1,1.8-3.1,2.7c-3.4,3-6.6,6-9.7,8.8c-17,15.8-29.2,28.9-29.2,28.9l-16-18l-8-8.9l-5.7-6.4c0.6-3.1,2.9-5.7,5.7-7.9 c1.9-1.5,4-2.7,6.1-3.8c4.1-2.1,7.7-3.2,7.7-3.2l2.7,3.2l6.8,7.9l8,9.3c2.9-3,5.8-5.9,8.6-8.6c3.2-3.1,6.4-6,9.5-8.6 c2.2-1.9,4.4-3.8,6.6-5.5c2.1-1.6,4.2-3.1,6.5-4.6c-7.8-3.8-17.6-5.9-29-5.9h-54.3V662h28.9v-36.5h25.4c32.5,0,51.2-17.2,51.2-47 c0-9.9-2.3-18.4-6.7-25.1L723.3,525.6z"
|
||||
id="path68" />
|
||||
</g></g>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
BIN
docs/images/logo/favicon.ico
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
docs/images/logo/pmd-logo-300px.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
docs/images/logo/pmd-logo-600px.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/images/logo/pmd-logo-white-300px.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
docs/images/logo/pmd-logo-white-600px.png
Normal file
After Width: | Height: | Size: 16 KiB |
@ -52,6 +52,7 @@ in a variety of ways, which are [documented here](pmd_userdocs_cpd.html).
|
||||
|
||||
The latest release of PMD can be downloaded from our [Github releases page](https://github.com/pmd/pmd/releases/latest).
|
||||
|
||||
The Logo is available from the [Logo Project Page](pmd_projectdocs_logo.html).
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -74,3 +74,62 @@ All you need to do is follow this few steps:
|
||||
You should take a look to [Kotlin token filter implementation](https://github.com/pmd/pmd/blob/master/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinTokenizer.java)
|
||||
|
||||
- For non-Antlr grammars you can use [BaseTokenFilter](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java) directly or take a peek to [Java's token filter](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java)
|
||||
|
||||
|
||||
### Testing your implementation
|
||||
|
||||
Add a Maven dependency on `pmd-lang-test` (scope `test`) in your `pom.xml`.
|
||||
This contains utilities to test your Tokenizer.
|
||||
|
||||
For simple tests, create a test class extending from `CpdTextComparisonTest`.
|
||||
That class is written in Kotlin, but you can extend it in Java as well.
|
||||
|
||||
To add tests, you need to write regular JUnit `@Test`-annotated methods, and
|
||||
call the method `doTest` with the name of the test file.
|
||||
|
||||
For example, for the Dart language:
|
||||
|
||||
```java
|
||||
|
||||
public class DartTokenizerTest extends CpdTextComparisonTest {
|
||||
|
||||
/**********************************
|
||||
Implementation of the superclass
|
||||
***********************************/
|
||||
|
||||
|
||||
public DartTokenizerTest() {
|
||||
super(".dart"); // the file extension for the dart language
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourcePrefix() {
|
||||
// If your class is in src/test/java /some/package
|
||||
// you need to place the test files in src/test/resources/some/package/cpdData
|
||||
return "cpdData";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tokenizer newTokenizer() {
|
||||
// Override this abstract method to return the correct tokenizer
|
||||
return new DartTokenizer();
|
||||
}
|
||||
|
||||
/**************
|
||||
Test methods
|
||||
***************/
|
||||
|
||||
|
||||
@Test // don't forget the JUnit annotation
|
||||
public void testLiterals() {
|
||||
// This will look for a file named literals.dart
|
||||
// in the directory identified by getResourcePrefix,
|
||||
// tokenize it, then compare the result against a baseline
|
||||
// literals.txt file in the same directory
|
||||
|
||||
// If the baseline file does not exist, it is created automatically
|
||||
doTest("literals");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
20
docs/pages/pmd/projectdocs/logo.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Logo
|
||||
sidebar: pmd_sidebar
|
||||
permalink: pmd_projectdocs_logo.html
|
||||
folder: pmd/projectdocs
|
||||
---
|
||||
|
||||
![PMD Logo](images/logo/pmd-logo-300px.png)
|
||||
|
||||
|
||||
The following PMD Logos and Icons are licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/):
|
||||
|
||||
* [PMD.svg](images/logo/PMD.svg)
|
||||
* [PMD.png](images/logo/PMD.png)
|
||||
* [Logo (300px, transparent)](images/logo/pmd-logo-300px.png)
|
||||
* [Logo (300px, white)](images/logo/pmd-logo-white-300px.png)
|
||||
* [Logo (600px, transparent)](images/logo/pmd-logo-600px.png)
|
||||
* [Logo (600px, white)](images/logo/pmd-logo-white-600px.png)
|
||||
|
||||
* [Favicon (16x16)](images/logo/favicon.ico)
|
@ -40,7 +40,9 @@ The tool comes with a rather extensive help text, simply running with `-help`!
|
||||
{% include custom/cli_option_row.html options="-auxclasspath"
|
||||
option_arg="cp"
|
||||
description="Specifies the classpath for libraries used by the source code.
|
||||
This is used to resolve types in source files. Alternatively, a `file://` URL
|
||||
This is used to resolve types in source files. The platform specific path delimiter
|
||||
(\":\" on Linux, \";\" on Windows) is used to separate the entries.
|
||||
Alternatively, a single `file:` URL
|
||||
to a text file containing path elements on consecutive lines can be specified."
|
||||
languages="Java"
|
||||
%}
|
||||
|
@ -53,30 +53,82 @@ The command line version of PMD continues to use **scala 2.13**.
|
||||
* The new Java Rule {% rule "java/codestyle/UnnecessaryCast" %} (`java-codestyle`)
|
||||
finds casts that are unnecessary while accessing collection elements.
|
||||
|
||||
* The new Java Rule {% rule "java/performance/AvoidCalendarDateCreation" %} (`java-performance`)
|
||||
finds usages of `java.util.Calendar` whose purpose is just to get the current date. This
|
||||
can be done in a more lightweight way.
|
||||
|
||||
#### Modified rules
|
||||
|
||||
* The Java rule {% rule "java/codestyle/UseDiamondOperator" %} (`java-codestyle`) now by default
|
||||
finds unnecessary usages of type parameters, which are nested, involve wildcards and are used
|
||||
within a ternary operator. These usages are usually only unnecessary with Java8 and later, when
|
||||
the type inference in Java has been improved.
|
||||
|
||||
In order to avoid false positives when checking Java7 only code, the rule has the new property
|
||||
`java7Compatibility`, which is disabled by default. Settings this to "true" retains
|
||||
the old rule behaviour.
|
||||
|
||||
### Fixed Issues
|
||||
|
||||
* apex-bestpractices
|
||||
* [#2554](https://github.com/pmd/pmd/issues/2554): \[apex] Exception applying rule UnusedLocalVariable on trigger
|
||||
* core
|
||||
* [#2599](https://github.com/pmd/pmd/pull/2599): \[core] Fix XPath 2.0 Rule Chain Analyzer with Unions
|
||||
* [#2483](https://github.com/pmd/pmd/issues/2483): \[lang-test] Support cpd tests based on text comparison.
|
||||
For details see
|
||||
[Testing your implementation](pmd_devdocs_major_adding_new_cpd_language.html#testing-your-implementation)
|
||||
in the developer documentation.
|
||||
* c#
|
||||
* [#2551](https://github.com/pmd/pmd/issues/2551): \[c#] CPD suppression with comments doesn't work
|
||||
* cpp
|
||||
* [#1757](https://github.com/pmd/pmd/issues/1757): \[cpp] Support unicode characters
|
||||
* java
|
||||
* [#2549](https://github.com/pmd/pmd/issues/2549): \[java] Auxclasspath in PMD CLI does not support relative file path
|
||||
* java-codestyle
|
||||
* [#2545](https://github.com/pmd/pmd/issues/2545): \[java] UseDiamondOperator false negatives
|
||||
* [#2573](https://github.com/pmd/pmd/pull/2573): \[java] DefaultPackage: Allow package default JUnit 5 Test methods
|
||||
* java-design
|
||||
* [#2563](https://github.com/pmd/pmd/pull/2563): \[java] UselessOverridingMethod false negative with already public methods
|
||||
* [#2570](https://github.com/pmd/pmd/issues/2570): \[java] NPathComplexity should mention the expected NPath complexity
|
||||
* java-errorprone
|
||||
* [#2544](https://github.com/pmd/pmd/issues/2544): \[java] UseProperClassLoader can not detect the case with method call on intermediate variable
|
||||
* java-performance
|
||||
* [#2591](https://github.com/pmd/pmd/pull/2591): \[java] InefficientStringBuffering/AppendCharacterWithChar: Fix false negatives with concats in appends
|
||||
* [#2600](https://github.com/pmd/pmd/pull/2600): \[java] UseStringBufferForStringAppends: fix false negative with fields
|
||||
* scala
|
||||
* [#2547](https://github.com/pmd/pmd/pull/2547): \[scala] Add cross compilation for scala 2.12 and 2.13
|
||||
|
||||
|
||||
### API Changes
|
||||
|
||||
* The maven module `net.sourceforge.pmd:pmd-scala` is deprecated. Use `net.sourceforge.pmd:pmd-scala_2.13`
|
||||
or `net.sourceforge.pmd:pmd-scala_2.12` instead.
|
||||
|
||||
|
||||
#### Deprecated APIs
|
||||
|
||||
* {%jdoc apex::lang.apex.ast.ASTAnnotation#suppresses(core::Rule) %}
|
||||
* {% jdoc !!apex::lang.apex.ast.ASTAnnotation#suppresses(core::Rule) %} (Apex)
|
||||
* {% jdoc !!core::cpd.TokenEntry#TokenEntry(java.lang.String, java.lang.String, int) %}
|
||||
* {% jdoc test::testframework.AbstractTokenizerTest %}. Use CpdTextComparisonTest in module pmd-lang-test instead.
|
||||
For details see
|
||||
[Testing your implementation](pmd_devdocs_major_adding_new_cpd_language.html#testing-your-implementation)
|
||||
in the developer documentation.
|
||||
|
||||
|
||||
* {% jdoc !!java::lang.java.rule.performance.InefficientStringBufferingRule#isInStringBufferOperation(net.sourceforge.pmd.lang.ast.Node, int, java.lang.String) %}
|
||||
|
||||
#### Internal API
|
||||
|
||||
* {% jdoc apex::lang.apex.ApexParser %}
|
||||
* {% jdoc apex::lang.apex.ApexHandler %}
|
||||
|
||||
### External Contributions
|
||||
|
||||
* [#1932](https://github.com/pmd/pmd/pull/1932): \[java] Added 4 performance rules originating from PMD-jPinpoint-rules - [Jeroen Borgers](https://github.com/jborgers)
|
||||
* [#2349](https://github.com/pmd/pmd/pull/2349): \[java] Optimize UnusedPrivateMethodRule - [shilko2013](https://github.com/shilko2013)
|
||||
* [#2547](https://github.com/pmd/pmd/pull/2547): \[scala] Add cross compilation for scala 2.12 and 2.13 - [João Ferreira](https://github.com/jtjeferreira)
|
||||
* [#2567](https://github.com/pmd/pmd/pull/2567): \[c#] Fix CPD suppression with comments doesn't work - [Lixon Lookose](https://github.com/LixonLookose)
|
||||
* [#2573](https://github.com/pmd/pmd/pull/2573): \[java] DefaultPackage: Allow package default JUnit 5 Test methods - [Craig Andrews](https://github.com/candrews)
|
||||
* [#2593](https://github.com/pmd/pmd/pull/2593): \[java] NPathComplexity should mention the expected NPath complexity - [Artem Krosheninnikov](https://github.com/KroArtem)
|
||||
|
||||
{% endtocmaker %}
|
||||
|
||||
|
@ -55,8 +55,10 @@ public class ApexTokenizer implements Tokenizer {
|
||||
if (!caseSensitive) {
|
||||
tokenText = tokenText.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
TokenEntry tokenEntry = new TokenEntry(tokenText, sourceCode.getFileName(), token.getLine(),
|
||||
token.getCharPositionInLine(), token.getCharPositionInLine() + tokenText.length());
|
||||
TokenEntry tokenEntry = new TokenEntry(tokenText, sourceCode.getFileName(),
|
||||
token.getLine(),
|
||||
token.getCharPositionInLine() + 1,
|
||||
token.getCharPositionInLine() + tokenText.length() + 1);
|
||||
tokenEntries.add(tokenEntry);
|
||||
}
|
||||
token = lexer.nextToken();
|
||||
|
@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.apex;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
|
||||
import net.sourceforge.pmd.lang.Parser;
|
||||
import net.sourceforge.pmd.lang.ParserOptions;
|
||||
@ -20,6 +21,7 @@ import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
|
||||
import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider;
|
||||
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
|
||||
|
||||
@InternalApi
|
||||
public class ApexHandler extends AbstractPmdLanguageVersionHandler {
|
||||
|
||||
private final ApexMetricsProvider myMetricsProvider = new ApexMetricsProvider();
|
||||
|
@ -9,6 +9,7 @@ import java.io.Reader;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.AbstractParser;
|
||||
import net.sourceforge.pmd.lang.ParserOptions;
|
||||
import net.sourceforge.pmd.lang.apex.ApexJorjeLogging;
|
||||
@ -19,6 +20,7 @@ import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
|
||||
import apex.jorje.data.Locations;
|
||||
import apex.jorje.semantic.ast.compilation.Compilation;
|
||||
|
||||
@InternalApi
|
||||
public final class ApexParser extends AbstractParser {
|
||||
|
||||
public ApexParser(ParserOptions parserOptions) {
|
||||
|
@ -24,6 +24,10 @@ public class UnusedLocalVariableRule extends AbstractApexRule {
|
||||
String variableName = node.getImage();
|
||||
|
||||
ASTBlockStatement variableContext = node.getFirstParentOfType(ASTBlockStatement.class);
|
||||
if (variableContext == null) {
|
||||
// if there is no parent BlockStatement, e.g. in triggers
|
||||
return data;
|
||||
}
|
||||
|
||||
List<ApexNode<?>> potentialUsages = new ArrayList<>();
|
||||
|
||||
|
@ -4,89 +4,57 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.cpd.SourceCode.StringCodeLoader;
|
||||
import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest;
|
||||
|
||||
public class ApexTokenizerTest extends CpdTextComparisonTest {
|
||||
|
||||
public ApexTokenizerTest() {
|
||||
super(".cls");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourcePrefix() {
|
||||
return "../lang/apex/cpd/testdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tokenizer newTokenizer(Properties properties) {
|
||||
ApexTokenizer tokenizer = new ApexTokenizer();
|
||||
tokenizer.setProperties(properties);
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
public class ApexTokenizerTest {
|
||||
|
||||
@Test
|
||||
public void testTokenize() throws IOException {
|
||||
Tokens tokens = tokenize(load("Simple.cls"));
|
||||
if (tokens.size() != 28) {
|
||||
printTokens(tokens);
|
||||
}
|
||||
assertEquals(28, tokens.size());
|
||||
assertEquals("someparam", findTokensByLine(8, tokens).get(0).toString());
|
||||
public void testTokenize() {
|
||||
doTest("Simple");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenizeCaseSensitive() throws IOException {
|
||||
Tokens tokens = tokenize(load("Simple.cls"), true);
|
||||
if (tokens.size() != 28) {
|
||||
printTokens(tokens);
|
||||
}
|
||||
assertEquals(28, tokens.size());
|
||||
assertEquals("someParam", findTokensByLine(8, tokens).get(0).toString());
|
||||
public void testTokenizeCaseSensitive() {
|
||||
doTest("Simple", "_caseSensitive", caseSensitive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Comments are ignored since using ApexLexer.
|
||||
*/
|
||||
@Test
|
||||
public void testTokenizeWithComments() throws IOException {
|
||||
Tokens tokens = tokenize(load("issue427/SFDCEncoder.cls"));
|
||||
assertEquals(17, tokens.size());
|
||||
|
||||
Tokens tokens2 = tokenize(load("issue427/SFDCEncoderConstants.cls"));
|
||||
assertEquals(17, tokens2.size());
|
||||
public void testTokenizeWithComments() {
|
||||
doTest("comments");
|
||||
}
|
||||
|
||||
private List<TokenEntry> findTokensByLine(int line, Tokens tokens) {
|
||||
List<TokenEntry> result = new ArrayList<>();
|
||||
for (TokenEntry entry : tokens.getTokens()) {
|
||||
if (entry.getBeginLine() == line) {
|
||||
result.add(entry);
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
fail("Not tokens found at line " + line);
|
||||
}
|
||||
return result;
|
||||
private Properties caseSensitive() {
|
||||
return properties(true);
|
||||
}
|
||||
|
||||
private Tokens tokenize(String code) {
|
||||
return tokenize(code, false);
|
||||
}
|
||||
|
||||
private Tokens tokenize(String code, boolean caseSensitive) {
|
||||
ApexTokenizer tokenizer = new ApexTokenizer();
|
||||
private Properties properties(boolean caseSensitive) {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(ApexTokenizer.CASE_SENSITIVE, Boolean.toString(caseSensitive));
|
||||
tokenizer.setProperties(properties);
|
||||
Tokens tokens = new Tokens();
|
||||
tokenizer.tokenize(new SourceCode(new StringCodeLoader(code)), tokens);
|
||||
return tokens;
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void printTokens(Tokens tokens) {
|
||||
for (TokenEntry entry : tokens.getTokens()) {
|
||||
System.out.printf("%02d: %s%s", entry.getBeginLine(), entry.toString(), PMD.EOL);
|
||||
}
|
||||
}
|
||||
|
||||
private String load(String name) throws IOException {
|
||||
return IOUtils.toString(ApexTokenizerTest.class.getResourceAsStream(name), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
35
pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/testdata/Simple.txt
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
[Image] or [Truncated image[ Bcol Ecol
|
||||
L4
|
||||
[public] 1 7
|
||||
[with] 8 12
|
||||
[sharing] 13 20
|
||||
[class] 21 26
|
||||
[simple] 27 33
|
||||
[{] 34 35
|
||||
L5
|
||||
[public] 5 11
|
||||
[string] 12 18
|
||||
[someparam] 19 28
|
||||
[{] 29 30
|
||||
[get] 31 34
|
||||
[;] 34 35
|
||||
[set] 36 39
|
||||
[;] 39 40
|
||||
[}] 41 42
|
||||
L7
|
||||
[public] 5 11
|
||||
[void] 12 16
|
||||
[getinit] 17 24
|
||||
[(] 24 25
|
||||
[)] 25 26
|
||||
[{] 27 28
|
||||
L8
|
||||
[someparam] 9 18
|
||||
[=] 19 20
|
||||
[test] 22 26
|
||||
[;] 27 28
|
||||
L9
|
||||
[}] 5 6
|
||||
L10
|
||||
[}] 1 2
|
||||
EOF
|
35
pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/testdata/Simple_caseSensitive.txt
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
[Image] or [Truncated image[ Bcol Ecol
|
||||
L4
|
||||
[public] 1 7
|
||||
[with] 8 12
|
||||
[sharing] 13 20
|
||||
[class] 21 26
|
||||
[Simple] 27 33
|
||||
[{] 34 35
|
||||
L5
|
||||
[public] 5 11
|
||||
[String] 12 18
|
||||
[someParam] 19 28
|
||||
[{] 29 30
|
||||
[get] 31 34
|
||||
[;] 34 35
|
||||
[set] 36 39
|
||||
[;] 39 40
|
||||
[}] 41 42
|
||||
L7
|
||||
[public] 5 11
|
||||
[void] 12 16
|
||||
[getInit] 17 24
|
||||
[(] 24 25
|
||||
[)] 25 26
|
||||
[{] 27 28
|
||||
L8
|
||||
[someParam] 9 18
|
||||
[=] 19 20
|
||||
[test] 22 26
|
||||
[;] 27 28
|
||||
L9
|
||||
[}] 5 6
|
||||
L10
|
||||
[}] 1 2
|
||||
EOF
|
14
pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/testdata/comments.cls
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* OWO
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common character classes used for input validation, output encoding, verifying password strength
|
||||
*/
|
||||
public with sharing class SFDCEncoderConstants {
|
||||
// a single-line comment
|
||||
|
||||
/************ CLASS CODE HERE *************/
|
||||
public String someParam { get; set; }
|
||||
}
|
||||
|
21
pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/testdata/comments.txt
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
[Image] or [Truncated image[ Bcol Ecol
|
||||
L8
|
||||
[public] 1 7
|
||||
[with] 8 12
|
||||
[sharing] 13 20
|
||||
[class] 21 26
|
||||
[sfdcencoderconstants] 27 47
|
||||
[{] 48 49
|
||||
L12
|
||||
[public] 2 8
|
||||
[string] 9 15
|
||||
[someparam] 16 25
|
||||
[{] 26 27
|
||||
[get] 28 31
|
||||
[;] 31 32
|
||||
[set] 33 36
|
||||
[;] 36 37
|
||||
[}] 38 39
|
||||
L13
|
||||
[}] 1 2
|
||||
EOF
|
@ -83,6 +83,17 @@ public class Foo {
|
||||
public String fieldUsage() {
|
||||
return myfield;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>NPE with triggers (#2554)</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
trigger leadOwnerUpdate on Lead (after update) {
|
||||
for(Lead Id : Trigger.new) {
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
@ -95,7 +95,11 @@ public class PMDParameters {
|
||||
private String language = null;
|
||||
|
||||
@Parameter(names = "-auxclasspath",
|
||||
description = "Specifies the classpath for libraries used by the source code. This is used by the type resolution. Alternatively, a 'file://' URL to a text file containing path elements on consecutive lines can be specified.")
|
||||
description = "Specifies the classpath for libraries used by the source code. "
|
||||
+ "This is used by the type resolution. The platform specific path delimiter "
|
||||
+ "(\":\" on Linux, \";\" on Windows) is used to separate the entries. "
|
||||
+ "Alternatively, a single 'file:' URL to a text file containing path elements on consecutive lines "
|
||||
+ "can be specified.")
|
||||
private String auxclasspath;
|
||||
|
||||
@Parameter(names = { "-failOnViolation", "--failOnViolation" }, arity = 1,
|
||||
|
@ -34,6 +34,10 @@ public abstract class AbstractTokenizer implements Tokenizer {
|
||||
private int lineNumber = 0;
|
||||
private String currentLine;
|
||||
|
||||
// both zero-based
|
||||
private int tokBeginLine;
|
||||
private int tokBeginCol;
|
||||
|
||||
protected boolean spanMultipleLinesString = true; // Most languages do, so
|
||||
// default is true
|
||||
protected Character spanMultipleLinesLineContinuationCharacter = null;
|
||||
@ -49,23 +53,35 @@ public abstract class AbstractTokenizer implements Tokenizer {
|
||||
int loc = 0;
|
||||
while (loc < currentLine.length()) {
|
||||
StringBuilder token = new StringBuilder();
|
||||
loc = getTokenFromLine(token, loc);
|
||||
loc = getTokenFromLine(token, loc); // may jump several lines
|
||||
|
||||
if (token.length() > 0 && !isIgnorableString(token.toString())) {
|
||||
final String image;
|
||||
if (downcaseString) {
|
||||
token = new StringBuilder(token.toString().toLowerCase(Locale.ROOT));
|
||||
image = token.toString().toLowerCase(Locale.ROOT);
|
||||
} else {
|
||||
image = token.toString();
|
||||
}
|
||||
// need to re-think how to link this
|
||||
// if ( CPD.debugEnable ) {
|
||||
// System.out.println("Token added:" + token.toString());
|
||||
// }
|
||||
tokenEntries.add(new TokenEntry(token.toString(), tokens.getFileName(), lineNumber + 1, loc - token.length(), loc - 1));
|
||||
|
||||
tokenEntries.add(new TokenEntry(image,
|
||||
tokens.getFileName(),
|
||||
tokBeginLine + 1,
|
||||
tokBeginCol + 1,
|
||||
loc + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns (0-based) EXclusive offset of the end of the token,
|
||||
* may jump several lines (sets {@link #lineNumber} in this case).
|
||||
*/
|
||||
private int getTokenFromLine(StringBuilder token, int loc) {
|
||||
tokBeginLine = lineNumber;
|
||||
tokBeginCol = loc;
|
||||
|
||||
for (int j = loc; j < currentLine.length(); j++) {
|
||||
char tok = currentLine.charAt(j);
|
||||
if (!Character.isWhitespace(tok) && !ignoreCharacter(tok)) {
|
||||
@ -89,6 +105,9 @@ public abstract class AbstractTokenizer implements Tokenizer {
|
||||
} else {
|
||||
if (token.length() > 0) {
|
||||
return j;
|
||||
} else {
|
||||
// ignored char
|
||||
tokBeginCol++;
|
||||
}
|
||||
}
|
||||
loc = j;
|
||||
@ -125,14 +144,14 @@ public abstract class AbstractTokenizer implements Tokenizer {
|
||||
if (spanMultipleLinesLineContinuationCharacter != null
|
||||
&& token.length() > 0
|
||||
&& token.charAt(token.length() - 1) == spanMultipleLinesLineContinuationCharacter) {
|
||||
token.deleteCharAt(token.length() - 1);
|
||||
token.setLength(token.length() - 1);
|
||||
}
|
||||
// parsing new line
|
||||
currentLine = code.get(++lineNumber);
|
||||
// Warning : recursive call !
|
||||
loc = parseString(token, 0, stringDelimiter);
|
||||
}
|
||||
return loc + 1;
|
||||
return loc;
|
||||
}
|
||||
|
||||
private boolean ignoreCharacter(char tok) {
|
||||
|
@ -22,14 +22,17 @@ public class AnyTokenizer implements Tokenizer {
|
||||
StringBuilder sb = sourceCode.getCodeBuffer();
|
||||
try (BufferedReader reader = new BufferedReader(new CharArrayReader(sb.toString().toCharArray()))) {
|
||||
int lineNumber = 1;
|
||||
int colNumber = 1;
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(line, TOKENS, true);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken();
|
||||
int endCol = colNumber + token.length() - 1; // -1 because inclusive
|
||||
if (!" ".equals(token) && !"\t".equals(token)) {
|
||||
tokenEntries.add(new TokenEntry(token, sourceCode.getFileName(), lineNumber));
|
||||
tokenEntries.add(new TokenEntry(token, sourceCode.getFileName(), lineNumber, colNumber, endCol));
|
||||
}
|
||||
colNumber = endCol + 1;
|
||||
}
|
||||
// advance iteration variables
|
||||
line = reader.readLine();
|
||||
|
@ -60,6 +60,7 @@ public class Mark implements Comparable<Mark> {
|
||||
this.endToken = endToken;
|
||||
}
|
||||
|
||||
/** Newlines are normalized to \n. */
|
||||
public String getSourceCodeSlice() {
|
||||
return this.code.getSlice(getBeginLine(), getEndLine());
|
||||
}
|
||||
|