Merge branch 'master' into pr-5038

This commit is contained in:
Andreas Dangel
2024-06-27 11:41:13 +02:00
51 changed files with 1381 additions and 242 deletions

View File

@ -7581,6 +7581,15 @@
"bug",
"code"
]
},
{
"login": "anuragagarwal561994",
"name": "Anurag Agarwal",
"avatar_url": "https://avatars.githubusercontent.com/u/6075379?v=4",
"profile": "https://github.com/anuragagarwal561994",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,

View File

@ -62,7 +62,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Daether.connector.http.connectionMaxTtl=180 -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/25/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/26/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -24,7 +24,7 @@ jobs:
shell: bash
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/25/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/26/scripts" >> $GITHUB_ENV
- name: Sync
run: .ci/git-repo-sync.sh
shell: bash

View File

@ -36,7 +36,7 @@ jobs:
run: |
echo "LANG=en_US.UTF-8" >> $GITHUB_ENV
echo "MAVEN_OPTS=-Daether.connector.http.connectionMaxTtl=180 -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/25/scripts" >> $GITHUB_ENV
echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/26/scripts" >> $GITHUB_ENV
- name: Check Environment
shell: bash
run: |

View File

@ -254,7 +254,7 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
## {{ site.pmd.date }} - {{ site.pmd.version }}
## {{ site.pmd.date | date: "%d-%B-%Y" }} - {{ site.pmd.version }}
The PMD team is pleased to announce PMD {{ site.pmd.version }}.

View File

@ -3,14 +3,15 @@ repository: pmd/pmd
pmd:
version: 7.3.0-SNAPSHOT
previous_version: 7.2.0
date: 28-June-2024
date: 2024-06-28
# release types: major, minor, bugfix
release_type: minor
# release types: major, minor, bugfix
output: web
# this property is useful for conditional filtering of content that is separate from the PDF.
sidebar_title: PMD
topnav_title: PMD Source Code Analyzer Project
# this appears on the top navigation bar next to the home button

View File

@ -1,7 +1,5 @@
entries:
- title: sidebar
product: PMD
version: '!PMD_VERSION!'
folders:
- title: null
output: pdf
@ -31,6 +29,12 @@ entries:
- title: Getting help
url: /pmd_about_help.html
output: web, pdf
- title: Release policies
url: /pmd_about_release_policies.html
output: web, pdf
- title: Support lifecycle
url: /pmd_about_support_lifecycle.html
output: web, pdf
- title: User Documentation
output: web, pdf
folderitems:

View File

@ -1,8 +1,8 @@
{% include custom/sidebarconfigs.html %}
<ul id="mysidebar" class="nav">
<li class="sidebarTitle">{{sidebar[0].product}} {{sidebar[0].version | replace: '!PMD_VERSION!', site.pmd.version}}</li>
<div class="sidebarTitleDate">Release date: {{site.pmd.date}}</div>
<li class="sidebarTitle">{{site.sidebar_title}} {{site.pmd.version}}</li>
<div class="sidebarTitleDate">Release date: {{site.pmd.date | date: "%d-%B-%Y" }}</div>
{% for entry in sidebar %}
{% for folder in entry.folders %}
{% if folder.output contains "web" %}

View File

@ -0,0 +1,55 @@
---
title: Release schedule and version policies
permalink: pmd_about_release_policies.html
author: Andreas Dangel <andreas.dangel@pmd-code.org>
last_updated: June 2024 (PMD 7.3.0)
---
## Release schedule
PMD uses a time-based release schedule.
We release a new minor version **every month**, usually on the last Friday of the month.
A patch release will only be done if necessary (e.g. blocker bugs).
## Version policy
PMD aims to follow [SemVer](https://semver.org/), that means, versions are numbered in the form MAJOR.MINOR.PATCH.
A **major** release can break any compatibility, and it means more effort to upgrade to the next major version (like
it was from 6.x to 7.x, see [Migration Guide for PMD 7](pmd_userdocs_migrating_to_pmd7.html).
A **minor** release tries to be compatible so that an effortless (aka "drop-in replacement") upgrade is possible
with some exceptions.
Such releases might contain:
* fixed false-positive (FP) issues for rules
* fixed false-negative (FN) issues for rules: These fixes might break your builds, as new violations might be found.
* new rules: these new rules are not used by default _if_ you use custom rulesets, so they shouldn't affect your builds.
* deprecations of existing functionality
In summary: we only guarantee stability on how you integrate / use the tool, but builds may start failing because
we fixed FNs, or introduced a new rule (ie: for people referencing whole categories).
We stick to our current approach when moving / renaming rules of deprecating them, and referencing
the new one until the next major.
A **patch** release absolutely is a drop-in replacement. So only bugs (ie: crashes or obviously broken stuff,
like rules not being applied at all), or security issues (dependency updates, hardening, etc.) are part of
a patch release.
See also
* [ADR 3 - API evolution principles](pmd_projectdocs_decisions_adr_3.html)
* [Rule deprecation policy](pmd_devdocs_rule_deprecation_policy.html)
## Git branches/tags policy
* Main development happens on the main branch (currently called `master`).
* PR and enhancements are done on the main branch.
* Release are usually done directly from the main branch, we don't create release branches.
* Each release has its own tag named `pmd_releases/MAJOR.MINOR.PATCH`.
* In case of a patch release, we either do it from the main branch (if there was no development ongoing)
or create a separate branch off the last release tag.
* See also [Release process](pmd_projectdocs_committers_releasing.html).

View File

@ -0,0 +1,25 @@
---
title: Support lifecycle
permalink: pmd_about_support_lifecycle.html
author: Andreas Dangel <andreas.dangel@pmd-code.org>
last_updated: June 2024 (PMD 7.3.0)
---
{% capture latest_release %}{{site.pmd.version}} ({{site.pmd.date | date: "%Y-%m-%d" }}){% endcapture %}
| Major version | Initial release | Latest Release | Required Java Version | In development / still supported? |
|---------------|--------------------|----------------------|-----------------------|-----------------------------------|
| 7.x | 7.0.0 (2024-03-22) | {{ latest_release }} | 8 | ✔ yes |
| 6.x | 6.0.0 (2017-12-15) | 6.55.0 (2023-02-25) | 7 | ❌ no |
| 5.x | 5.0.0 (2012-05-01) | 5.8.1 (2017-07-01) | 7 | ❌ no |
| 4.x | 4.0 (2007-07-20) | 4.3 (2011-11-04) | 5 | ❌ no |
| 3.x | 3.0 (2005-03-23) | 3.9 (2006-12-19) | 4 | ❌ no |
| 2.x | 2.0 (2004-10-19) | 2.3 (2005-02-01) | | ❌ no |
| 1.x | 1.0 (2002-11-04) | 1.9 (2004-07-14) | | ❌ no |
In general, only the latest major version is in active development and regularly will receive new features
and bug fixes etc.
Once a new version is released, the previous version becomes unsupported.
We recommend to always update to the latest version to benefit from new features and bug fixes.
See also [Release process and version policies](pmd_about_release_policies.html).

View File

@ -74,7 +74,7 @@ in order to release version "6.34.0", the configuration should look like this:
pmd:
version: 7.2.0
previous_version: 7.1.0
date: 31-May-2024
date: 2024-05-31
release_type: minor
```
@ -307,7 +307,7 @@ There are a couple of manual steps needed to prepare the current main branch for
pmd:
version: 7.3.0-SNAPSHOT
previous_version: 7.2.0
date: ??-??-2024
date: 2024-??-??
release_type: minor
```
@ -324,7 +324,7 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
## {{ site.pmd.date }} - {{ site.pmd.version }}
## {{ site.pmd.date | date: "%d-%B-%Y" }} - {{ site.pmd.version }}
The PMD team is pleased to announce PMD {{ site.pmd.version }}.

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ keywords: [formats, renderers]
summary: "Overview of the built-in report formats for CPD"
permalink: pmd_userdocs_cpd_report_formats.html
author: Andreas Dangel <andreas.dangel@pmd-code.org>
last_updated: June 2024 (7.3.0)
---
## Overview
@ -97,11 +98,19 @@ Starting at line 110 of /home/pmd/source/pmd-core/src/test/java/net/sourceforge/
This format uses XML to output the duplications in a more structured format.
The XML format can then further be processed using XSLT transformations. See [section xslt](#xslt) for examples.
Since PMD 7.3.0 any recoverable errors are also reported as additional elements `error` to help investigate
any errors occurred during analysis.
Example:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<pmd-cpd>
<pmd-cpd xmlns="https://pmd-code.org/schema/cpd-report"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
pmdVersion="7.3.0"
timestamp="2024-06-23T09:00:00+02:00"
version="1.0.0"
xsi:schemaLocation="https://pmd-code.org/schema/cpd-report https://pmd.github.io/schema/cpd-report_1_0_0.xsd">
<file path="/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/RuleReferenceTest.java" totalNumberOfTokens="523"/>
<file path="/home/pmd/source/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/JaxenXPathRuleQueryTest.java" totalNumberOfTokens="120"/>
<duplication lines="33" tokens="239">
@ -167,6 +176,32 @@ Example:
Assert.assertEquals(2, query.nodeNameToXPaths.size());
Assert.assertEquals("self::node()[(attribute::Test1 = \"false\")][(attribute::Test2 = \"true\")]", query.nodeNameToXPaths.get("dummyNode").get(0).toString());]]></codefragment>
</duplication>
<error filename="/home/pmd/source/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/cpd/badandgood/BadFile.java"
msg="LexException: Lexical error in file '/home/pmd/source/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/cpd/badandgood/BadFile.java' at line 4, column 14: &#34;\ufffd&#34; (65533), after : &#34;&#34; (in lexical state DEFAULT)">net.sourceforge.pmd.lang.ast.LexException: Lexical error in file '/home/pmd/source/pmd-cli/src/test/resources/net/sourceforge/pmd/cli/cpd/badandgood/BadFile.java' at line 4, column 14: "\ufffd" (65533), after : "" (in lexical state DEFAULT)
at net.sourceforge.pmd.lang.ast.InternalApiBridge.newLexException(InternalApiBridge.java:25)
at net.sourceforge.pmd.lang.java.ast.JavaParserImplTokenManager.getNextToken(JavaParserImplTokenManager.java:2698)
at net.sourceforge.pmd.lang.java.ast.JavaParserImplTokenManager.getNextToken(JavaParserImplTokenManager.java:18)
at net.sourceforge.pmd.cpd.impl.BaseTokenFilter.getNextToken(BaseTokenFilter.java:44)
at net.sourceforge.pmd.cpd.impl.CpdLexerBase.tokenize(CpdLexerBase.java:40)
at net.sourceforge.pmd.cpd.CpdLexer.tokenize(CpdLexer.java:29)
at net.sourceforge.pmd.cpd.CpdAnalysis.doTokenize(CpdAnalysis.java:146)
at net.sourceforge.pmd.cpd.CpdAnalysis.performAnalysis(CpdAnalysis.java:173)
at net.sourceforge.pmd.cli.commands.internal.CpdCommand.doExecute(CpdCommand.java:134)
at net.sourceforge.pmd.cli.commands.internal.CpdCommand.doExecute(CpdCommand.java:29)
at net.sourceforge.pmd.cli.internal.PmdRootLogger.executeInLoggingContext(PmdRootLogger.java:55)
at net.sourceforge.pmd.cli.commands.internal.AbstractAnalysisPmdSubcommand.execute(AbstractAnalysisPmdSubcommand.java:111)
at net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand.call(AbstractPmdSubcommand.java:30)
at net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand.call(AbstractPmdSubcommand.java:16)
at picocli.CommandLine.executeUserObject(CommandLine.java:2041)
at picocli.CommandLine.access$1500(CommandLine.java:148)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
at picocli.CommandLine.execute(CommandLine.java:2170)
at net.sourceforge.pmd.cli.PmdCli.main(PmdCli.java:24)
</error>
</pmd-cpd>
```

View File

@ -282,7 +282,7 @@ Example:
<?xml version="1.0" encoding="UTF-8"?>
<pmd xmlns="http://pmd.sourceforge.net/report/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/report/2.0.0 https://pmd.sourceforge.io/report_2_0_0.xsd"
xsi:schemaLocation="http://pmd.sourceforge.net/report/2.0.0 https://pmd.github.io/schema/report_2_0_0.xsd"
version="6.22.0" timestamp="2020-04-11T19:17:03.207">
<file name="/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java">
<violation beginline="124" endline="125" begincolumn="9" endcolumn="111" rule="GuardLogStatement" ruleset="Best Practices" package="net.sourceforge.pmd" class="RuleContext" method="setSourceCodeFilename" externalInfoUrl="https://pmd.github.io/pmd-6.22.0/pmd_rules_java_bestpractices.html#guardlogstatement" priority="2">

View File

@ -2,6 +2,7 @@
title: Gradle
tags: [userdocs, tools]
permalink: pmd_userdocs_tools_gradle.html
last_updated: June 2024 (7.3.0)
---
The [Gradle Build Tool](https://gradle.org/) provides a [PMD Plugin](https://docs.gradle.org/current/userguide/pmd_plugin.html)
@ -49,16 +50,18 @@ with the property `toolVersion`:
```
pmd {
toolVersion = "6.21.0"
toolVersion = "{{ site.pmd.version }}"
}
```
Note: For PMD 7, at least gradle 8.6 is needed. See [Support for PMD 7.0](https://github.com/gradle/gradle/issues/24502).
## References
Source code for Gradles PMD Plugin is available here:
Source code for Gradle's PMD Plugin is available here:
* [gradle/gradle code-quality](https://github.com/gradle/gradle/tree/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality)
* [Pmd.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java)
* [PmdExtension.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java)
* [PmdPlugin.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java)
* The default PMD version used by gradle: [DEFAULT_PMD_VERSION](https://github.com/gradle/gradle/blob/d6daeeb446e6a966c33efea5f3f5f1a2d96f6b8f/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L66)
* The default PMD version used by gradle: [DEFAULT_PMD_VERSION](https://github.com/gradle/gradle/blob/v8.8.0/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L66)

View File

@ -4,7 +4,7 @@ permalink: pmd_release_notes.html
keywords: changelog, release notes
---
## {{ site.pmd.date }} - {{ site.pmd.version }}
## {{ site.pmd.date | date: "%d-%B-%Y" }} - {{ site.pmd.version }}
The PMD team is pleased to announce PMD {{ site.pmd.version }}.
@ -19,11 +19,42 @@ This is a {{ site.pmd.release_type }} release.
when the keys are of an enum type. The specialized enum collections are more space- and time-efficient.
### 🐛 Fixed Issues
* java
* core
* [#4992](https://github.com/pmd/pmd/pull/4992): \[core] CPD: Include processing errors in XML report
* apex
* [#5053](https://github.com/pmd/pmd/issues/5053): \[apex] CPD fails to parse string literals with escaped characters
* java-bestpractices
* [#577](https://github.com/pmd/pmd/issues/577): \[java] New Rule: Check that Map<K,V> is an EnumMap if K is an enum value
* [#5047](https://github.com/pmd/pmd/issues/5047): \[java] UnusedPrivateMethod FP for Generics & Overloads
* plsql
* [#1934](https://github.com/pmd/pmd/issues/1934): \[plsql] ParseException with MERGE statement in anonymous block
* [#2779](https://github.com/pmd/pmd/issues/2779): \[plsql] Error while parsing statement with (Oracle) DML Error Logging
### 🚨 API Changes
#### CPD Report Format XML
There are some important changes:
1. The XML format will now use an XSD schema, that is available at <https://pmd.github.io/schema/cpd-report_1_0_0.xsd>.
This schema defines the valid elements and attributes that one can expect from a CPD report.
2. The root element `pmd-cpd` contains the new attributes `pmdVersion`, `timestamp` and `version`. The latter is
the schema version and is currently "1.0.0".
3. The CPD XML report will now also contain recoverable errors as additional `<error>` elements.
See [Report formats for CPD](pmd_userdocs_cpd_report_formats.html#xml) for an example.
The XML format should be compatible as only attributes and elements have been added. However, if you parse
the document with a namespace aware parser, you might encounter some issues like no elements being found.
In case the new format doesn't work for you (e.g. namespaces, unexpected error elements), you can
go back using the old format with the renderer "xmlold" ({%jdoc core::cpd.XMLOldRenderer %}). Note, that
this old renderer is deprecated and only there for compatibility reasons. Whatever tooling is used to
read the XML format should be updated.
#### Deprecated for removal
* {%jdoc !!core::cpd.XMLOldRenderer %} (the CPD format "xmlold").
### ✨ External Contributions
{% endtocmaker %}

View File

@ -29,6 +29,7 @@ import net.sourceforge.pmd.cpd.CPDReportRenderer;
import net.sourceforge.pmd.cpd.CSVRenderer;
import net.sourceforge.pmd.cpd.CpdAnalysis;
import net.sourceforge.pmd.cpd.SimpleRenderer;
import net.sourceforge.pmd.cpd.XMLOldRenderer;
import net.sourceforge.pmd.cpd.XMLRenderer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
@ -66,6 +67,8 @@ public class CPDTask extends Task {
private static final String TEXT_FORMAT = "text";
private static final String XML_FORMAT = "xml";
@Deprecated
private static final String XMLOLD_FORMAT = "xmlold";
private static final String CSV_FORMAT = "csv";
private String format = TEXT_FORMAT;
@ -177,6 +180,8 @@ public class CPDTask extends Task {
return new SimpleRenderer();
} else if (CSV_FORMAT.equals(format)) {
return new CSVRenderer();
} else if (XMLOLD_FORMAT.equals(format)) {
return new XMLOldRenderer();
}
return new XMLRenderer();
}
@ -253,7 +258,7 @@ public class CPDTask extends Task {
}
public static class FormatAttribute extends EnumeratedAttribute {
private static final String[] FORMATS = new String[] { XML_FORMAT, TEXT_FORMAT, CSV_FORMAT };
private static final String[] FORMATS = new String[] { XML_FORMAT, TEXT_FORMAT, CSV_FORMAT, XMLOLD_FORMAT };
@Override
public String[] getValues() {

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<pmd xmlns="http://pmd.sourceforge.net/report/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/report/2.0.0 http://pmd.sourceforge.net/report_2_0_0.xsd" version="" timestamp="">
<pmd xmlns="http://pmd.sourceforge.net/report/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/report/2.0.0 https://pmd.github.io/schema/report_2_0_0.xsd" version="" timestamp="">
<file name="sample.dummy">
<violation beginline="1" endline="1" begincolumn="1" endcolumn="110" rule="SampleXPathRule" ruleset="Test Ruleset" package="foo" externalInfoUrl="${pmd.website.baseurl}/rules/dummy/basic.xml#SampleXPathRule" priority="3">
Test Rule 2

View File

@ -9,37 +9,35 @@ import java.util.Locale;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
import net.sourceforge.pmd.cpd.CpdLexer;
import net.sourceforge.pmd.cpd.TokenFactory;
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken;
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager;
import net.sourceforge.pmd.lang.document.TextDocument;
import com.nawforce.apexparser.ApexLexer;
import com.nawforce.apexparser.CaseInsensitiveInputStream;
public class ApexCpdLexer implements CpdLexer {
@Override
public void tokenize(TextDocument document, TokenFactory tokenEntries) throws IOException {
CharStream charStream = CharStreams.fromReader(document.newReader());
ApexLexer lexer = new ApexLexer(charStream);
CaseInsensitiveInputStream caseInsensitiveInputStream = new CaseInsensitiveInputStream(charStream);
ApexLexer lexer = new ApexLexer(caseInsensitiveInputStream);
AntlrTokenManager tokenManager = new AntlrTokenManager(lexer, document);
Token token = lexer.nextToken();
AntlrToken token = tokenManager.getNextToken();
while (token.getType() != Token.EOF) {
if (token.getChannel() == ApexLexer.DEFAULT_TOKEN_CHANNEL) { // exclude WHITESPACE_CHANNEL and COMMENT_CHANNEL
String tokenText = token.getText();
while (!token.isEof()) {
if (token.isDefault()) { // excludes WHITESPACE_CHANNEL and COMMENT_CHANNEL
String tokenText = token.getImage();
// be case-insensitive
tokenText = tokenText.toLowerCase(Locale.ROOT);
tokenEntries.recordToken(
tokenText,
token.getLine(),
token.getCharPositionInLine() + 1,
token.getLine(),
token.getCharPositionInLine() + tokenText.length() + 1
);
tokenEntries.recordToken(tokenText, token.getReportLocation());
}
token = lexer.nextToken();
token = tokenManager.getNextToken();
}
}
}

View File

@ -32,4 +32,14 @@ class ApexCpdLexerTest extends CpdTextComparisonTest {
void testTabWidth() {
doTest("tabWidth");
}
@Test
void lexExceptionExpected() {
expectLexException("class Foo { String s = \"not a string literal\"; }");
}
@Test
void caseInsensitiveStringLiterals() {
doTest("StringLiterals5053");
}
}

View File

@ -0,0 +1,12 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
// See https://github.com/pmd/pmd/issues/5053
public with sharing class PMD7CPD {
public static void example(){
String str = 'alice';
str = str.replace('alice', '<bob></charlie>dan<!--JohannHeinrichvonThünen-->' + '\u00A0' + '"100%"');
}
}

View File

@ -0,0 +1,43 @@
[Image] or [Truncated image[ Bcol Ecol
L7
[public] 1 7
[with] 8 12
[sharing] 13 20
[class] 21 26
[pmd7cpd] 27 34
[{] 35 36
L8
[public] 5 11
[static] 12 18
[void] 19 23
[example] 24 31
[(] 31 32
[)] 32 33
[{] 33 34
L9
[string] 7 13
[str] 14 17
[=] 18 19
['alice'] 20 27
[;] 27 28
L10
[str] 7 10
[=] 11 12
[str] 13 16
[.] 16 17
[replace] 17 24
[(] 24 25
['alice'] 25 32
[,] 32 33
['<bob></charlie>dan<!--johannheinr[ 34 84
[+] 85 86
['\\u00a0'] 87 95
[+] 96 97
['"100%"'] 98 106
[)] 106 107
[;] 107 108
L11
[}] 5 6
L12
[}] 1 2
EOF

View File

@ -10,7 +10,6 @@ import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import java.nio.charset.StandardCharsets;
@ -38,6 +37,14 @@ class CpdCliTest extends BaseCliTest {
private static final String SRC_DIR = BASE_RES_PATH + "files/";
private static final Path SRC_PATH = Paths.get(SRC_DIR).toAbsolutePath();
private static final String CPD_REPORT_HEADER_PATTERN = "<\\?xml version=\"1.0\" encoding=\"UTF-8\"\\?>\n"
+ "<pmd-cpd xmlns=\"https://pmd-code.org/schema/cpd-report\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+ " pmdVersion=\".+?\"\n"
+ " timestamp=\".+?\"\n"
+ " version=\"1.0.0\"\n"
+ " xsi:schemaLocation=\"https://pmd-code.org/schema/cpd-report https://pmd.github.io/schema/cpd-report_1_0_0.xsd\">\n";
private static final Map<String, Integer> NUMBER_OF_TOKENS;
static {
@ -68,8 +75,11 @@ class CpdCliTest extends BaseCliTest {
void testEmptyResultRendering() throws Exception {
final String expectedFilesXml = getExpectedFileEntriesXml(NUMBER_OF_TOKENS.keySet());
runCliSuccessfully("--minimum-tokens", "340", "--language", "java", "--dir", SRC_DIR, "--format", "xml")
.verify(result -> result.checkStdOut(equalTo(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd>\n" + expectedFilesXml + "</pmd-cpd>\n"
.verify(result -> result.checkStdOut(containsPattern(CPD_REPORT_HEADER_PATTERN
+ "\\Q" // quote start
+ expectedFilesXml
+ "</pmd-cpd>\n"
+ "\\E" // quote end
)));
}
@ -176,8 +186,8 @@ class CpdCliTest extends BaseCliTest {
@Test
void testNoDuplicatesResultRendering() throws Exception {
String expectedReport = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<pmd-cpd>\n"
String expectedReportPattern = CPD_REPORT_HEADER_PATTERN
+ "\\Q" // quote start
+ " <file path=\"" + SRC_PATH.resolve("dup1.java") + "\"\n"
+ " totalNumberOfTokens=\"89\"/>\n"
+ " <file path=\"" + SRC_PATH.resolve("dup2.java") + "\"\n"
@ -186,10 +196,11 @@ class CpdCliTest extends BaseCliTest {
+ " totalNumberOfTokens=\"5\"/>\n"
+ " <file path=\"" + SRC_PATH.resolve("fileWith_UTF_8_BOM_Encoding.java") + "\"\n"
+ " totalNumberOfTokens=\"5\"/>\n"
+ "</pmd-cpd>\n";
+ "</pmd-cpd>\n"
+ "\\E"; // quote end
runCliSuccessfully("--minimum-tokens", "340", "--language", "java", "--dir", SRC_DIR, "--format", "xml")
.verify(result -> result.checkStdOut(equalTo(expectedReport)));
.verify(result -> result.checkStdOut(containsPattern(expectedReportPattern)));
}
/**
@ -251,9 +262,7 @@ class CpdCliTest extends BaseCliTest {
runCli(OK, "--minimum-tokens", "5", "--language", "ecmascript",
"-f", "xml",
"-d", BASE_RES_PATH + "tsFiles/")
.checkStdOut(equalTo(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<pmd-cpd/>\n"
));
.checkStdOut(containsPattern(CPD_REPORT_HEADER_PATTERN.substring(0, CPD_REPORT_HEADER_PATTERN.length() - 2) + "/>"));
}
@Test

View File

@ -39,7 +39,7 @@ final class RendererHelper {
}
try (SourceManager sourceManager = new SourceManager(textFiles)) {
CPDReport report = new CPDReport(sourceManager, matchesList, Collections.emptyMap());
CPDReport report = new CPDReport(sourceManager, matchesList, Collections.emptyMap(), Collections.emptyList());
renderer.render(report, writer);
} catch (Exception e) {
throw new RuntimeException(e);

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cpd="https://pmd-code.org/schema/cpd-report"
exclude-result-prefixes="cpd">
<!--
PMD CPD (Copy and Paste Detector) XML to HTML transformer
-->
@ -68,10 +70,10 @@
<th>Approximate number of bytes</th>
</tr>
<tr>
<td class="SummaryNumber"><xsl:value-of select="count(//duplication)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication/@lines)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication/@tokens)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication/@tokens) * 4"/></td>
<td class="SummaryNumber"><xsl:value-of select="count(//cpd:duplication)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication/@lines)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication/@tokens)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication/@tokens) * 4"/></td>
</tr>
</table>
</div>
@ -91,7 +93,7 @@
</tr>
</thead>
<tbody>
<xsl:apply-templates select="pmd-cpd/duplication" />
<xsl:apply-templates select="cpd:pmd-cpd/cpd:duplication" />
</tbody>
</table>
</div>
@ -133,7 +135,7 @@
</xsl:template>
<!-- templates -->
<xsl:template match="pmd-cpd/duplication">
<xsl:template match="cpd:pmd-cpd/cpd:duplication">
<xsl:for-each select=".">
<tr>
<td><xsl:value-of select="@lines"/></td>
@ -141,7 +143,7 @@
<td>
<table class="table table-light table-bordered table-striped table-hover">
<tr><th>column</th><th>endcolumn</th><th>line</th><th>endline</th><th>path</th></tr>
<xsl:for-each select="file">
<xsl:for-each select="cpd:file">
<tr>
<td><xsl:value-of select="@column"/></td>
<td><xsl:value-of select="@endcolumn"/></td>
@ -152,7 +154,7 @@
</xsl:for-each>
</table>
</td>
<td><pre><xsl:value-of select="codefragment"/></pre></td>
<td><pre><xsl:value-of select="cpd:codefragment"/></pre></td>
</tr>
</xsl:for-each>
</xsl:template>

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Stylesheet to turn the XML output of CPD into a nice-looking HTML page -->
<!-- $Id$ -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cpd="https://pmd-code.org/schema/cpd-report"
exclude-result-prefixes="cpd" version="2.0">
<xsl:output method="html" encoding="utf-8" doctype-system="about:legacy-compat"/>
<xsl:param name="lines" required="yes">30</xsl:param>
<xsl:template match="pmd-cpd">
<xsl:template match="cpd:pmd-cpd">
<html>
<head>
<meta charset="utf-8"/>
@ -46,10 +48,10 @@
<th>Approx # bytes</th>
</tr>
<tr>
<td class="SummaryNumber"><xsl:value-of select="count(//duplication[@lines>$lines])"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication[@lines>$lines]/@lines)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication[@lines>$lines]/@tokens)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//duplication[@lines>$lines]/@tokens) * 4"/></td>
<td class="SummaryNumber"><xsl:value-of select="count(//cpd:duplication[@lines>$lines])"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication[@lines>$lines]/@lines)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication[@lines>$lines]/@tokens)"/></td>
<td class="SummaryNumber"><xsl:value-of select="sum(//cpd:duplication[@lines>$lines]/@tokens) * 4"/></td>
</tr>
</table>
<p/>
@ -58,13 +60,13 @@
<p/>
<table>
<tr style="background-color: #444444; color: #DDDDDD;"><td>ID</td><td>Files</td><td>Lines</td></tr>
<xsl:for-each select="//duplication[@lines>$lines]">
<xsl:for-each select="//cpd:duplication[@lines>$lines]">
<xsl:sort data-type="number" order="descending" select="@lines"/>
<tr>
<td class="ItemNumber"><xsl:value-of select="position()"/></td>
<td>
<table>
<xsl:for-each select="file">
<xsl:for-each select="cpd:file">
<tr><td><a><xsl:attribute name="href">../src/<xsl:value-of select="@path"/>.html#<xsl:value-of select="@line"/></xsl:attribute><xsl:value-of select="@path"/></a></td><td> line <xsl:value-of select="@line"/></td></tr>
</xsl:for-each>
</table>
@ -82,7 +84,7 @@
<textarea cols="100" wrap="off" class='CodeFragment' style='display:none;'>
<xsl:attribute name="rows"><xsl:value-of select="$lines"/></xsl:attribute>
<xsl:attribute name="id">frag_<xsl:value-of select="position()"/></xsl:attribute>
<xsl:value-of select="codefragment"/>
<xsl:value-of select="cpd:codefragment"/>
</textarea>
</td>
</tr></table>

View File

@ -39,6 +39,7 @@ public class CPDConfiguration extends AbstractConfiguration {
static {
RENDERERS.put(DEFAULT_RENDERER, SimpleRenderer.class);
RENDERERS.put("xml", XMLRenderer.class);
RENDERERS.put("xmlold", XMLOldRenderer.class);
RENDERERS.put("csv", CSVRenderer.class);
RENDERERS.put("csv_with_linecount_per_file", CSVWithLinecountPerFileRenderer.class);
RENDERERS.put("vs", VSRenderer.class);

View File

@ -13,6 +13,7 @@ import java.util.stream.Collectors;
import net.sourceforge.pmd.lang.document.Chars;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.reporting.Report;
/**
* The result of a CPD analysis. This is rendered by a {@link CPDReportRenderer}.
@ -24,13 +25,16 @@ public class CPDReport {
private final SourceManager sourceManager;
private final List<Match> matches;
private final Map<FileId, Integer> numberOfTokensPerFile;
private final List<Report.ProcessingError> processingErrors;
CPDReport(SourceManager sourceManager,
List<Match> matches,
Map<FileId, Integer> numberOfTokensPerFile) {
Map<FileId, Integer> numberOfTokensPerFile,
List<Report.ProcessingError> processingErrors) {
this.sourceManager = sourceManager;
this.matches = Collections.unmodifiableList(matches);
this.numberOfTokensPerFile = Collections.unmodifiableMap(new TreeMap<>(numberOfTokensPerFile));
this.processingErrors = Collections.unmodifiableList(processingErrors);
}
/** Return the list of duplication matches found by the CPD analysis. */
@ -39,11 +43,15 @@ public class CPDReport {
}
/** Return a map containing the number of tokens by processed file. */
public Map<FileId, Integer> getNumberOfTokensPerFile() {
return numberOfTokensPerFile;
}
/** Returns the list of occurred processing errors. */
public List<Report.ProcessingError> getProcessingErrors() {
return processingErrors;
}
/**
* Return the slice of source code where the mark was found. This
* returns the entire lines from the start to the end line of the
@ -66,7 +74,7 @@ public class CPDReport {
public CPDReport filterMatches(Predicate<Match> filter) {
List<Match> filtered = this.matches.stream().filter(filter).collect(Collectors.toList());
return new CPDReport(sourceManager, filtered, this.getNumberOfTokensPerFile());
return new CPDReport(sourceManager, filtered, this.getNumberOfTokensPerFile(), this.getProcessingErrors());
}
/**

View File

@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -30,6 +31,7 @@ import net.sourceforge.pmd.lang.document.InternalApiBridge;
import net.sourceforge.pmd.lang.document.TextDocument;
import net.sourceforge.pmd.lang.document.TextFile;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.reporting.Report;
import net.sourceforge.pmd.util.log.PmdReporter;
/**
@ -162,7 +164,7 @@ public final class CpdAnalysis implements AutoCloseable {
Map<FileId, Integer> numberOfTokensPerFile = new HashMap<>();
boolean hasErrors = false;
List<Report.ProcessingError> processingErrors = new ArrayList<>();
Tokens tokens = new Tokens();
for (TextFile textFile : sourceManager.getTextFiles()) {
TextDocument textDocument = sourceManager.get(textFile);
@ -177,11 +179,11 @@ public final class CpdAnalysis implements AutoCloseable {
}
String message = configuration.isSkipLexicalErrors() ? "Skipping file" : "Error while tokenizing";
reporter.errorEx(message, e);
hasErrors = true;
processingErrors.add(new Report.ProcessingError(e, textFile.getFileId()));
savedState.restore(tokens);
}
}
if (hasErrors && !configuration.isSkipLexicalErrors()) {
if (!processingErrors.isEmpty() && !configuration.isSkipLexicalErrors()) {
// will be caught by CPD command
throw new IllegalStateException("Errors were detected while lexing source, exiting because --skip-lexical-errors is unset.");
}
@ -192,7 +194,7 @@ public final class CpdAnalysis implements AutoCloseable {
tokens = null; // NOPMD null it out before rendering
LOGGER.debug("Finished: {} duplicates found", matches.size());
CPDReport cpdReport = new CPDReport(sourceManager, matches, numberOfTokensPerFile);
CPDReport cpdReport = new CPDReport(sourceManager, matches, numberOfTokensPerFile, processingErrors);
if (renderer != null) {
try (Writer writer = IOUtil.createWriter(Charset.defaultCharset(), null)) {

View File

@ -267,7 +267,7 @@ public class GUI implements CPDListener {
}
if (!f.canWrite()) {
final CPDReport report = new CPDReport(sourceManager, matches, numberOfTokensPerFile);
final CPDReport report = new CPDReport(sourceManager, matches, numberOfTokensPerFile, Collections.emptyList());
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(f.toPath()))) {
renderer.render(report, pw);
pw.flush();
@ -549,7 +549,7 @@ public class GUI implements CPDListener {
for (int selectionIndex : selectionIndices) {
selections.add((Match) model.getValueAt(selectionIndex, 99));
}
CPDReport toRender = new CPDReport(sourceManager, selections, Collections.emptyMap());
CPDReport toRender = new CPDReport(sourceManager, selections, Collections.emptyMap(), Collections.emptyList());
String report = new SimpleRenderer(trimLeadingWhitespace).renderToString(toRender);
resultsTextArea.setText(report);
resultsTextArea.setCaretPosition(0); // move to the top

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