Merge branch '7.0.x' into generic-visitor2

This commit is contained in:
Clément Fournier 2020-06-25 14:29:54 +02:00
commit 847c4de68b
325 changed files with 32784 additions and 2383 deletions

104
antlr4-wrapper.xml Normal file
View 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 &lt;T> T accept(ParseTreeVisitor&lt;? extends T> visitor)"
value="public &lt;P, R> R acceptVisitor(AstVisitor&lt;? super P, ? extends R> visitor, P data)" />
<replacefilter token="((${visitor-name}&lt;? extends T>)visitor)"
value="((${visitor-name}&lt;? 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&lt;T> extends ParseTreeVisitor&lt;T> {"
value="Visitor&lt;P, R> extends net.sourceforge.pmd.lang.ast.AstVisitor&lt;P, R> {&#10;
&#10;
/**&#10;
* The default visit method for ${lang-name} nodes. Unless overridden,&#10;
* the default implementations of the methods of this interface delegate&#10;
* to this method. The default calls {@link #visitNode(Node, Object)}.&#10;
* &#10;
* @param node Node to visit&#10;
* @param data Parameter of the visit&#10;
* @return Result of the visit&#10;
*/&#10;
default R visit${node-itf-name}(${node-itf-name} node, P data) { return visitNode(node, data); }&#10;&#10;
&#10;
"/>
<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(?&lt;!\b(Parser)?RuleContext\b)"/>
<substitution expression="${node-prefix}\1"/>
</replaceregexp>
</target>
</project>

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

105
docs/images/logo/PMD.svg Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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

View File

@ -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");
}
}
```

View 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)

View File

@ -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"
%}

View File

@ -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 %}

View File

@ -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();

View File

@ -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();

View File

@ -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) {

View File

@ -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<>();

View File

@ -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);
}
}

View 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

View 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

View 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; }
}

View 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

View File

@ -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>

View File

@ -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,

View File

@ -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) {

View File

@ -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();

View File

@ -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());
}

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