Merge branch 'master' into pmd/7.0.x

This commit is contained in:
Andreas Dangel 2022-02-25 20:23:58 +01:00
commit 8eb18114c9
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
39 changed files with 721 additions and 331 deletions

View File

@ -6538,6 +6538,25 @@
"contributions": [
"code"
]
},
{
"login": "JerritEic",
"name": "JerritEic",
"avatar_url": "https://avatars.githubusercontent.com/u/60690273?v=4",
"profile": "https://github.com/JerritEic",
"contributions": [
"code",
"doc"
]
},
{
"login": "karel1980",
"name": "Karel Vervaeke",
"avatar_url": "https://avatars.githubusercontent.com/u/153021?v=4",
"profile": "https://github.com/karel1980",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,

View File

@ -9,26 +9,37 @@
<div id="topbar-content-offset">
{% include topnav.html %}
<!-- Page Content -->
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
{% assign content_col_size = "col-md-12" %}
{% unless page.hide_sidebar %}
<!-- Sidebar Column -->
<div class="col-md-3" id="tg-sb-sidebar">
{% include sidebar.html %}
</div>
{% assign content_col_size = "col-md-9" %}
{% endunless %}
<div class="container-toc-wrapper">
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
{% assign content_col_size = "col-md-12" %}
{% unless page.hide_sidebar %}
<!-- Sidebar Column -->
<div class="col-md-3" id="tg-sb-sidebar">
{% include sidebar.html %}
</div>
{% assign content_col_size = "col-md-9" %}
{% endunless %}
<!-- Content Column -->
<div class="{{content_col_size}}" id="tg-sb-content">
{{content}}
<!-- Content Column -->
<div class="{{content_col_size}}" id="tg-sb-content">
{{content}}
</div>
<!-- /.row -->
</div>
<!-- /.row -->
<!-- /.container -->
</div>
<!-- /.container -->
{% unless page.toc == false %}
<!-- Sticky TOC column -->
<div class="toc-col">
{% include toc.html %}
</div>
{% endunless %}
<!-- /.toc-container-wrapper -->
</div>
</div>

View File

@ -12,9 +12,7 @@ layout: default
<div class="summary">{{page.summary}}</div>
{% endif %}
{% unless page.toc == false %}
{% include toc.html %}
{% endunless %}
<div id="inline-toc"><!-- empty, move TOC here when screen size too small --></div>
{% if site.github_editme_path %}

View File

@ -1,6 +1,70 @@
body {
font-size:15px;
}
@media (max-width: 1349px) {
/* Small screen, inline TOC*/
.container-toc-wrapper {
display: block;
}
div.toc-col {
display: none;
}
div#toc{
margin-top: 15px;
margin-left: 0px;
margin-right: 0px;
}
.container {
margin-left: auto;
margin-right: auto;
}
}
@media (min-width: 1350px) {
/* Medium screens, keep sticky TOC but remove justify-content*/
div#toc{
margin-top: 60px;
margin-left: -15px;
margin-right: 15px;
}
.container {
margin-left: 15px;
margin-right: 15px;
width: 75%;
}
.container-toc-wrapper {
display: flex;
flex-wrap: wrap;
margin-left: auto;
margin-right: auto;
}
}
@media (min-width: 1600px) {
/* Sticky TOC functionality */
div#toc{
margin-top: 60px;
margin-left: -15px;
margin-right: 15px;
}
.container {
margin-left: 15px;
margin-right: 15px;
width: 75%;
}
.container-toc-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-left: auto;
margin-right: auto;
}
}
.bs-callout {
padding: 20px;

View File

@ -1,13 +1,23 @@
// Detect small devices and move the TOC in line
function moveToc(){
if(window.innerWidth < 1350){
$( "#toc" ).detach().appendTo("#inline-toc").removeClass("position-fixed");
} else {
$( "#toc" ).detach().appendTo(".toc-col").addClass("position-fixed");
}
}
$( document ).ready(function() {
$('#mysidebar').height($(".nav").height());
// this script says, if the height of the viewport is greater than 800px, then insert position-fixed class,
// this script says, if the height of the viewport is greater than 600px, then insert position-fixed class,
// which makes the nav bar float in a fixed position as your scroll. If you have a lot of nav items,
// this height may not work for you.
var h = $(window).height();
//console.log (h);
if (h > 800) {
if (h > 600) {
$( "#mysidebar" ).attr("class", "nav position-fixed");
}
@ -20,6 +30,8 @@ $( document ).ready(function() {
* AnchorJS
*/
anchors.add('h2,h3,h4,h5');
// Check if TOC needs to be moved on page load
moveToc();
// This highlights the active parent class in the navgoco sidebar. This is critical so that the parent expands
// when you're viewing a page.
@ -71,3 +83,6 @@ $( document ).ready(function() {
event.preventDefault();
});
});
// Check if TOC needs to be moved on window resizing
$(window).resize(function () {moveToc();});

File diff suppressed because it is too large Load Diff

View File

@ -122,6 +122,14 @@ The examples below won't repeat this taskdef element, as this is always required
</td>
<td>No</td>
</tr>
<tr>
<td>threads</td>
<td>
Sets the number of threads used by PMD. Set threads to <code>0</code> to disable multi-threading processing.
Default: 1
</td>
<td>No</td>
</tr>
</table>

View File

@ -23,8 +23,14 @@ This is a {{ site.pmd.release_type }} release.
* core
* [#3427](https://github.com/pmd/pmd/issues/3427): \[core] Stop printing CLI usage text when exiting due to invalid parameters
* [#3768](https://github.com/pmd/pmd/issues/3768): \[core] SARIF formatter reports multiple locations when it should report multiple results
* doc
* [#2502](https://github.com/pmd/pmd/issues/2502): \[doc] Add floating table-of-contents (toc) on the right
* [#3807](https://github.com/pmd/pmd/pull/3807): \[doc] Document Ant Task parameter `threads`
* java
* [#3698](https://github.com/pmd/pmd/issues/3697): \[java] Parsing error with try-with-resources and qualified resource
* java-bestpractices
* [#3605](https://github.com/pmd/pmd/issues/3605): \[java] SwitchStmtsShouldHaveDefault triggered when default case is present
* java-codestyle
* [#278](https://github.com/pmd/pmd/issues/278): \[java] ConfusingTernary should treat `!= null` as positive condition
* java-performance
@ -74,6 +80,7 @@ will change drastically in PMD 7.
### External Contributions
* [#3767](https://github.com/pmd/pmd/pull/3767): \[core] Update GUI.java - [Vyom Yadav](https://github.com/Vyom-Yadav)
* [#3804](https://github.com/pmd/pmd/pull/3804): \[doc] Add floating table of contents (issue #2502) - [JerritEic](https://github.com/JerritEic)
{% endtocmaker %}

View File

@ -30,7 +30,7 @@ public class PMDTask extends Task {
private String rulesetFiles;
private boolean noRuleSetCompatibility;
private String encoding;
private int threads;
private int threads = 1; // same default as in PMDParameters (CLI)
private int minimumPriority;
private int maxRuleViolations = 0;
private String failuresPropertyName;

View File

@ -64,7 +64,7 @@ public class PMDParameters {
@Parameter(names = { "--threads", "-threads", "-t" }, description = "Sets the number of threads used by PMD.",
validateWith = PositiveInteger.class)
private int threads = 1;
private int threads = 1; // see also default in PMDTask (Ant)
@Parameter(names = { "--benchmark", "-benchmark", "-b" },
description = "Benchmark mode - output a benchmark report upon completion; default to System.err.")

View File

@ -4,38 +4,36 @@
package net.sourceforge.pmd.renderers.internal.sarif;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ArtifactLocation;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.AssociatedRule;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Component;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Exception;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Invocation;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Location;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Message;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.MultiformatMessage;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.PhysicalLocation;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.PropertyBag;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Region;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ReportingDescriptor;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Result;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Run;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Tool;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ToolConfigurationNotification;
import static net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ToolExecutionNotification;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ArtifactLocation;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.AssociatedRule;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Component;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Exception;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Invocation;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Location;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Message;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.MultiformatMessage;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.PhysicalLocation;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.PropertyBag;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Region;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ReportingDescriptor;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Result;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Run;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.Tool;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ToolConfigurationNotification;
import net.sourceforge.pmd.renderers.internal.sarif.SarifLog.ToolExecutionNotification;
public class SarifLogBuilder {
private final Map<ReportingDescriptor, List<Location>> locationsByRule = new HashMap<>();
private final List<ReportingDescriptor> rules = new ArrayList<>();
private final List<Result> results = new ArrayList<>();
private final List<ToolConfigurationNotification> toolConfigurationNotifications = new ArrayList<>();
private final List<ToolExecutionNotification> toolExecutionNotifications = new ArrayList<>();
@ -45,11 +43,15 @@ public class SarifLogBuilder {
public SarifLogBuilder add(RuleViolation violation) {
final ReportingDescriptor ruleDescriptor = getReportingDescriptor(violation);
final Location location = getRuleViolationLocation(violation);
int ruleIndex = rules.indexOf(ruleDescriptor);
if (ruleIndex == -1) {
rules.add(ruleDescriptor);
ruleIndex = rules.size() - 1;
}
final List<Location> ruleLocation = locationsByRule.containsKey(ruleDescriptor) ? locationsByRule.get(ruleDescriptor) : new ArrayList<>();
ruleLocation.add(location);
locationsByRule.put(ruleDescriptor, ruleLocation);
final Location location = getRuleViolationLocation(violation);
final Result result = resultFrom(ruleDescriptor, ruleIndex, location);
results.add(result);
return this;
}
@ -105,15 +107,6 @@ public class SarifLogBuilder {
}
public SarifLog build() {
final List<ReportingDescriptor> rules = new ArrayList<>(locationsByRule.keySet());
final List<Result> results = new ArrayList<>();
for (int i = 0, size = rules.size(); i < size; i++) {
ReportingDescriptor rule = rules.get(i);
List<Location> locations = locationsByRule.get(rule);
results.add(resultFrom(rule, i, locations));
}
final Component driver = getDriverComponent().toBuilder().rules(rules).build();
final Tool tool = Tool.builder().driver(driver).build();
final Invocation invocation = Invocation.builder()
@ -136,7 +129,7 @@ public class SarifLogBuilder {
return toolExecutionNotifications.isEmpty() && toolConfigurationNotifications.isEmpty();
}
private Result resultFrom(ReportingDescriptor rule, Integer ruleIndex, List<Location> locations) {
private Result resultFrom(ReportingDescriptor rule, Integer ruleIndex, Location location) {
final Result result = Result.builder()
.ruleId(rule.getId())
.ruleIndex(ruleIndex)
@ -147,7 +140,7 @@ public class SarifLogBuilder {
.build();
result.setMessage(message);
result.setLocations(locations);
result.setLocations(Collections.singletonList(location));
return result;
}

View File

@ -19,6 +19,7 @@ import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.ConfigurationError;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.ReportTest;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.RuleWithProperties;
@ -68,31 +69,49 @@ public abstract class AbstractRendererTest {
}
protected Consumer<FileAnalysisListener> reportOneViolation() {
return it -> it.onRuleViolation(newRuleViolation(1));
return it -> it.onRuleViolation(newRuleViolation(1, 1, 1, 1, createFooRule()));
}
private Consumer<FileAnalysisListener> reportTwoViolations() {
return it -> {
RuleViolation informationalRuleViolation = newRuleViolation(1);
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
RuleViolation informationalRuleViolation = newRuleViolation(1, 1, 1, 1, createFooRule());
it.onRuleViolation(informationalRuleViolation);
RuleViolation severeRuleViolation = newRuleViolation(2);
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
RuleViolation severeRuleViolation = newRuleViolation(1, 1, 1, 2, createBooRule());
it.onRuleViolation(severeRuleViolation);
};
}
protected RuleViolation newRuleViolation(int endColumn) {
return newRuleViolation(endColumn, "Foo");
protected DummyNode createNode(int beginLine, int beginColumn, int endLine, int endColumn) {
DummyNode node = new DummyRoot().withFileName(getSourceCodeFilename());
node.setCoords(beginLine, beginColumn, endLine, endColumn);
return node;
}
protected RuleViolation newRuleViolation(int endColumn, String ruleName) {
DummyNode node = createNode(endColumn);
FooRule rule = new FooRule();
rule.setName(ruleName);
protected RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, Rule rule) {
DummyNode node = createNode(beginLine, beginColumn, endLine, endColumn);
return new ParametricRuleViolation<Node>(rule, node, "blah");
}
/**
* Creates a new rule instance with name "Boo" and priority {@link RulePriority#HIGH}.
*/
protected Rule createBooRule() {
Rule booRule = new FooRule();
booRule.setName("Boo");
booRule.setPriority(RulePriority.HIGH);
return booRule;
}
/**
* Creates a new rule instance with name "Foo" and priority {@link RulePriority#LOW}.
*/
protected Rule createFooRule() {
Rule fooRule = new FooRule();
fooRule.setName("Foo");
fooRule.setPriority(RulePriority.LOW);
return fooRule;
}
/**
* Read a resource file relative to this class's location.
*/
@ -104,19 +123,14 @@ public abstract class AbstractRendererTest {
}
}
protected DummyNode createNode(int endColumn) {
DummyNode node = new DummyRoot().withFileName(getSourceCodeFilename());
node.setCoords(1, 1, 1, endColumn);
return node;
}
@Test
public void testRuleWithProperties() throws Exception {
DummyNode node = createNode(1);
DummyNode node = createNode(1, 1, 1, 1);
RuleWithProperties theRule = new RuleWithProperties();
theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR,
"the string value\nsecond line with \"quotes\"");
String rendered = ReportTest.render(getRenderer(), it -> it.onRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah")));
String rendered = ReportTest.render(getRenderer(),
it -> it.onRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah")));
assertEquals(filter(getExpectedWithProperties()), filter(rendered));
}

View File

@ -30,7 +30,7 @@ public class CSVRendererTest extends AbstractRendererTest {
public String getExpectedMultiple() {
return getHeader()
+ "\"1\",\"\",\"" + getSourceCodeFilename() + "\",\"5\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL
+ "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"1\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL;
+ "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"1\",\"1\",\"blah\",\"RuleSet\",\"Boo\"" + PMD.EOL;
}
@Override
@ -46,8 +46,4 @@ public class CSVRendererTest extends AbstractRendererTest {
private String getHeader() {
return "\"Problem\",\"Package\",\"File\",\"Priority\",\"Line\",\"Description\",\"Rule set\",\"Rule\"" + PMD.EOL;
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(CSVRendererTest.class);
}
}

View File

@ -10,9 +10,6 @@ import org.junit.Test;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.ReportTest;
import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
@ -72,8 +69,8 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
+ "violationSuppressRegex | | Suppress violations with messages matching a regular expression\\n"
+ "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n"
+ "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}"
+ "\u0000" + PMD.EOL + "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: High\\n\\n"
+ "\u0000" + PMD.EOL + "{\"type\":\"issue\",\"check_name\":\"Boo\",\"description\":\"blah\","
+ "\"content\":{\"body\":\"## Boo\\n\\nSince: PMD null\\n\\nPriority: High\\n\\n"
+ "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n"
+ "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n"
+ "desc\\n\\n"
@ -87,14 +84,13 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
@Test
public void testXPathRule() throws Exception {
DummyNode node = createNode(1);
XPathRule theRule = new XPathRule(XPathVersion.XPATH_3_1, "//dummyNode");
// Setup as FooRule
theRule.setDescription("desc");
theRule.setName("Foo");
String rendered = ReportTest.render(getRenderer(), it -> it.onRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah")));
String rendered = ReportTest.render(getRenderer(), it -> it.onRuleViolation(newRuleViolation(1, 1, 1, 2, theRule)));
// Output should be the exact same as for non xpath rules
assertEquals(filter(getExpected()), filter(rendered));

View File

@ -10,6 +10,7 @@ import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.FooRule;
import net.sourceforge.pmd.Report.ConfigurationError;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.Report.SuppressedViolation;
@ -79,7 +80,7 @@ public class JsonRendererTest extends AbstractRendererTest {
@Test
public void suppressedViolations() throws IOException {
SuppressedViolation suppressed = new SuppressedViolation(
newRuleViolation(1),
newRuleViolation(1, 1, 1, 1, new FooRule()),
ViolationSuppressor.NOPMD_COMMENT_SUPPRESSOR,
"test"
);

View File

@ -42,7 +42,7 @@ public class PapariTextRendererTest extends AbstractRendererTest {
public String getExpectedMultiple() {
return "* file: " + getSourceCodeFilename() + PMD.EOL + " src: " + getSourceCodeFilename() + ":1:1" + PMD.EOL + " rule: Foo" + PMD.EOL
+ " msg: blah" + PMD.EOL + " code: public class Foo {}" + PMD.EOL + PMD.EOL + " src: "
+ getSourceCodeFilename() + ":1:1" + PMD.EOL + " rule: Foo" + PMD.EOL + " msg: blah" + PMD.EOL
+ getSourceCodeFilename() + ":1:1" + PMD.EOL + " rule: Boo" + PMD.EOL + " msg: blah" + PMD.EOL
+ " code: public class Foo {}" + PMD.EOL + PMD.EOL + PMD.EOL + PMD.EOL + "Summary:" + PMD.EOL
+ PMD.EOL + "* warnings: 2" + PMD.EOL;
}
@ -67,8 +67,4 @@ public class PapariTextRendererTest extends AbstractRendererTest {
+ " err: a configuration error" + PMD.EOL + PMD.EOL
+ "* errors: 1" + PMD.EOL + "* warnings: 0" + PMD.EOL;
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(PapariTextRendererTest.class);
}
}

View File

@ -20,9 +20,12 @@ import org.junit.runners.Suite.SuiteClasses;
EmacsRendererTest.class,
HTMLRendererTest.class,
IDEAJRendererTest.class,
JsonRendererTest.class,
PapariTextRendererTest.class,
SarifRendererTest.class,
SummaryHTMLRendererTest.class,
TextPadRendererTest.class,
TextRendererTest.class,
VBHTMLRendererTest.class,
XMLRendererTest.class,
XSLTRendererTest.class,

View File

@ -4,17 +4,17 @@
package net.sourceforge.pmd.renderers;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import java.util.function.Consumer;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.ReportTest;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
public class SarifRendererTest extends AbstractRendererTest {
@ -65,32 +65,34 @@ public class SarifRendererTest extends AbstractRendererTest {
@Override
public String filter(String expected) {
return expected.replaceAll("\r\n", "\n"); // make the test run on Windows, too
return expected.replaceAll("\r\n", "\n") // make the test run on Windows, too
.replaceAll("\"version\": \".+\",", "\"version\": \"unknown\",");
}
@Override
/**
* Multiple occurrences of the same rule should be reported as individual results.
*
* @see <a href="https://github.com/pmd/pmd/issues/3768"> [core] SARIF formatter reports multiple locations
* when it should report multiple results #3768</a>
*/
@Test
public void testRendererMultiple() throws Exception {
// Exercise
String actual = ReportTest.render(getRenderer(), reportTwoViolations());
public void testRendererMultipleLocations() throws Exception {
String actual = ReportTest.render(getRenderer(), reportThreeViolationsTwoRules());
// Verify that both rules are and rule ids are linked in the results
// Initially was comparing whole files but order of rules rendered can't be guaranteed when the report is being rendered
// Refer to pmd-core/src/test/resources/net/sourceforge/pmd/renderers/sarif/expected-multiple.sarif.json to see an example data structure
assertThat(filter(actual), containsString("\"ruleId\": \"Foo\""));
assertThat(filter(actual), containsString("\"ruleId\": \"Boo\""));
assertThat(filter(actual), containsString("\"id\": \"Foo\""));
assertThat(filter(actual), containsString("\"id\": \"Boo\""));
JSONObject json = new JSONObject(actual);
JSONArray results = json.getJSONArray("runs").getJSONObject(0).getJSONArray("results");
assertEquals(3, results.length());
assertEquals(filter(readFile("expected-multiple-locations.sarif.json")), filter(actual));
}
private Consumer<FileAnalysisListener> reportTwoViolations() {
private Consumer<FileAnalysisListener> reportThreeViolationsTwoRules() {
Rule fooRule = createFooRule();
Rule booRule = createBooRule();
return reportBuilder -> {
RuleViolation informationalRuleViolation = newRuleViolation(1, "Foo");
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
reportBuilder.onRuleViolation(informationalRuleViolation);
RuleViolation severeRuleViolation = newRuleViolation(2, "Boo");
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
reportBuilder.onRuleViolation(severeRuleViolation);
reportBuilder.onRuleViolation(newRuleViolation(1, 1, 1, 10, fooRule));
reportBuilder.onRuleViolation(newRuleViolation(5, 1, 5, 11, fooRule));
reportBuilder.onRuleViolation(newRuleViolation(2, 2, 3, 1, booRule));
};
}

View File

@ -70,7 +70,8 @@ public class SummaryHTMLRendererTest extends AbstractRendererTest {
return "<html><head><title>PMD</title></head><body>" + PMD.EOL + "<center><h2>Summary</h2></center>" + PMD.EOL
+ "<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\">" + PMD.EOL
+ "<tr><th>Rule name</th><th>Number of violations</th></tr>" + PMD.EOL
+ "<tr><td>Foo</td><td align=center>2</td></tr>" + PMD.EOL + "</table>" + PMD.EOL
+ "<tr><td>Boo</td><td align=center>1</td></tr>" + PMD.EOL
+ "<tr><td>Foo</td><td align=center>1</td></tr>" + PMD.EOL + "</table>" + PMD.EOL
+ "<center><h2>Detail</h2></center><table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>"
+ PMD.EOL
+ "<center><h3>PMD report</h3></center><center><h3>Problems found</h3></center><table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>"

View File

@ -25,10 +25,6 @@ public class TextPadRendererTest extends AbstractRendererTest {
@Override
public String getExpectedMultiple() {
return getSourceCodeFilename() + "(1, Foo): blah" + PMD.EOL + getSourceCodeFilename() + "(1, Foo): blah" + PMD.EOL;
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(TextPadRendererTest.class);
return getSourceCodeFilename() + "(1, Foo): blah" + PMD.EOL + getSourceCodeFilename() + "(1, Boo): blah" + PMD.EOL;
}
}

View File

@ -28,7 +28,7 @@ public class TextRendererTest extends AbstractRendererTest {
@Override
public String getExpectedMultiple() {
return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL
+ getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL;
+ getSourceCodeFilename() + ":1:\tBoo:\tblah" + PMD.EOL;
}
@Override

View File

@ -93,8 +93,4 @@ public class VBHTMLRendererTest extends AbstractRendererTest {
+ "--></style><body><center><br><table border=\"0\" width=\"80%\"><tr id=TableHeader><td colspan=\"2\"><font class=title>&nbsp;Configuration problems found</font></td></tr><tr id=RowColor2><td><font class=body>"
+ error.rule().getName() + "</font></td><td><font class=body>" + error.issue() + "</font></td></tr></table></center></body></html>" + PMD.EOL;
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(VBHTMLRendererTest.class);
}
}

View File

@ -63,7 +63,7 @@ public class XMLRendererTest extends AbstractRendererTest {
return getHeader() + "<file name=\"" + getSourceCodeFilename() + "\">" + PMD.EOL
+ "<violation beginline=\"1\" endline=\"1\" begincolumn=\"1\" endcolumn=\"1\" rule=\"Foo\" ruleset=\"RuleSet\" priority=\"5\">"
+ PMD.EOL + "blah" + PMD.EOL + "</violation>" + PMD.EOL
+ "<violation beginline=\"1\" endline=\"1\" begincolumn=\"1\" endcolumn=\"2\" rule=\"Foo\" ruleset=\"RuleSet\" priority=\"1\">"
+ "<violation beginline=\"1\" endline=\"1\" begincolumn=\"1\" endcolumn=\"2\" rule=\"Boo\" ruleset=\"RuleSet\" priority=\"1\">"
+ PMD.EOL + "blah" + PMD.EOL + "</violation>" + PMD.EOL + "</file>" + PMD.EOL + "</pmd>" + PMD.EOL;
}

View File

@ -16,7 +16,6 @@ import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@ -25,6 +24,7 @@ import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ConfigurationError;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.ReportTest;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.Node;
@ -34,7 +34,7 @@ public class YAHTMLRendererTest extends AbstractRendererTest {
private File outputDir;
@Rule
@org.junit.Rule
public TemporaryFolder folder = new TemporaryFolder();
@Before
@ -42,8 +42,8 @@ public class YAHTMLRendererTest extends AbstractRendererTest {
outputDir = folder.newFolder("pmdtest");
}
private RuleViolation newRuleViolation(int endColumn, final String packageNameArg, final String classNameArg) {
DummyNode node = createNode(endColumn);
private RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, final String packageNameArg, final String classNameArg) {
DummyNode node = createNode(beginLine, beginColumn, endLine, endColumn);
return new ParametricRuleViolation<Node>(new FooRule(), node, "blah") {
{
packageName = packageNameArg;
@ -53,17 +53,17 @@ public class YAHTMLRendererTest extends AbstractRendererTest {
}
@Override
protected RuleViolation newRuleViolation(int endColumn) {
return newRuleViolation(endColumn, "net.sf.pmd.test", "YAHTMLSampleClass");
protected RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, Rule rule) {
return newRuleViolation(beginLine, beginColumn, endLine, endColumn, "net.sf.pmd.test", "YAHTMLSampleClass");
}
@Test
public void testReportMultipleViolations() throws Exception {
String actual = ReportTest.render(getRenderer(), it -> {
it.onRuleViolation(newRuleViolation(1, "net.sf.pmd.test", "YAHTMLSampleClass1"));
it.onRuleViolation(newRuleViolation(2, "net.sf.pmd.test", "YAHTMLSampleClass1"));
it.onRuleViolation(newRuleViolation(1, "net.sf.pmd.other", "YAHTMLSampleClass2"));
it.onRuleViolation(newRuleViolation(1, 1, 1, 1, "net.sf.pmd.test", "YAHTMLSampleClass1"));
it.onRuleViolation(newRuleViolation(1, 1, 1, 2, "net.sf.pmd.test", "YAHTMLSampleClass1"));
it.onRuleViolation(newRuleViolation(1, 1, 1, 1, "net.sf.pmd.other", "YAHTMLSampleClass2"));
});
assertEquals(filter(getExpected()), filter(actual));

View File

@ -22,7 +22,7 @@
"endline": 1,
"endcolumn": 2,
"description": "blah",
"rule": "Foo",
"rule": "Boo",
"ruleset": "RuleSet",
"priority": 1
}

View File

@ -0,0 +1,130 @@
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "PMD",
"version": "unknown",
"informationUri": "https://pmd.github.io/pmd/",
"rules": [
{
"id": "Foo",
"shortDescription": {
"text": "blah"
},
"fullDescription": {
"text": "desc"
},
"help": {
"text": "desc"
},
"properties": {
"ruleset": "RuleSet",
"priority": 5,
"tags": [
"RuleSet"
]
}
},
{
"id": "Boo",
"shortDescription": {
"text": "blah"
},
"fullDescription": {
"text": "desc"
},
"help": {
"text": "desc"
},
"properties": {
"ruleset": "RuleSet",
"priority": 1,
"tags": [
"RuleSet"
]
}
}
]
}
},
"results": [
{
"ruleId": "Foo",
"ruleIndex": 0,
"message": {
"text": "blah"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "notAvailable.ext"
},
"region": {
"startLine": 1,
"startColumn": 1,
"endLine": 1,
"endColumn": 10
}
}
}
]
},
{
"ruleId": "Boo",
"ruleIndex": 1,
"message": {
"text": "blah"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "notAvailable.ext"
},
"region": {
"startLine": 2,
"startColumn": 2,
"endLine": 3,
"endColumn": 1
}
}
}
]
},
{
"ruleId": "Foo",
"ruleIndex": 0,
"message": {
"text": "blah"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "notAvailable.ext"
},
"region": {
"startLine": 5,
"startColumn": 1,
"endLine": 5,
"endColumn": 11
}
}
}
]
}
],
"invocations": [
{
"executionSuccessful": true,
"toolConfigurationNotifications": [],
"toolExecutionNotifications": []
}
]
}
]
}

View File

@ -9,25 +9,6 @@
"version": "unknown",
"informationUri": "https://pmd.github.io/pmd/",
"rules": [
{
"id": "Boo",
"shortDescription": {
"text": "blah"
},
"fullDescription": {
"text": "desc"
},
"help": {
"text": "desc"
},
"properties": {
"ruleset": "RuleSet",
"priority": 1,
"tags": [
"RuleSet"
]
}
},
{
"id": "Foo",
"shortDescription": {
@ -46,36 +27,33 @@
"RuleSet"
]
}
},
{
"id": "Boo",
"shortDescription": {
"text": "blah"
},
"fullDescription": {
"text": "desc"
},
"help": {
"text": "desc"
},
"properties": {
"ruleset": "RuleSet",
"priority": 1,
"tags": [
"RuleSet"
]
}
}
]
}
},
"results": [
{
"ruleId": "Boo",
"ruleIndex": 0,
"message": {
"text": "blah"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "notAvailable.ext"
},
"region": {
"startLine": 1,
"startColumn": 1,
"endLine": 1,
"endColumn": 2
}
}
}
]
},
{
"ruleId": "Foo",
"ruleIndex": 1,
"ruleIndex": 0,
"message": {
"text": "blah"
},
@ -94,6 +72,28 @@
}
}
]
},
{
"ruleId": "Boo",
"ruleIndex": 1,
"message": {
"text": "blah"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "notAvailable.ext"
},
"region": {
"startLine": 1,
"startColumn": 1,
"endLine": 1,
"endColumn": 2
}
}
}
]
}
],
"invocations": [

View File

@ -107,4 +107,14 @@ public interface ASTSwitchLike extends JavaNode, Iterable<ASTSwitchBranch> {
default Iterator<ASTSwitchBranch> iterator() {
return children(ASTSwitchBranch.class).iterator();
}
/**
* Returns true if this a switch which uses fallthrough branches
* (old school {@code case label: break;}) and not arrow branches.
* If the switch has no branches, returns false.
*/
default boolean isFallthroughSwitch() {
return getBranches().filterIs(ASTSwitchFallthroughBranch.class).nonEmpty();
}
}

View File

@ -19,4 +19,5 @@ public final class ASTSwitchStatement extends AbstractStatement implements ASTSw
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
}

View File

@ -17,5 +17,52 @@ public class ASTSwitchStatementTest extends BaseParserTest {
.get(0);
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch()); // this should not throw a NPE...
Assert.assertTrue(switchStatement.hasDefaultCase());
Assert.assertTrue(switchStatement.isFallthroughSwitch());
}
@Test
public void defaultCaseWithArrowBlock() {
ASTSwitchStatement switchStatement = getNodes(ASTSwitchStatement.class,
"class Foo { void bar(int x) {"
+ "switch (x) { default -> { } } } }")
.get(0);
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertTrue(switchStatement.iterator().hasNext());
Assert.assertTrue(switchStatement.hasDefaultCase());
Assert.assertFalse(switchStatement.isFallthroughSwitch());
}
@Test
public void emptySwitch() {
ASTSwitchStatement switchStatement = getNodes(ASTSwitchStatement.class,
"class Foo { void bar(int x) {"
+ "switch (x) { } } }")
.get(0);
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertFalse(switchStatement.iterator().hasNext());
Assert.assertFalse(switchStatement.hasDefaultCase());
Assert.assertFalse(switchStatement.isFallthroughSwitch());
}
@Test
public void defaultCaseWithArrowExprs() {
ASTSwitchStatement switchStatement =
getNodes(ASTSwitchStatement.class,
"import net.sourceforge.pmd.lang.java.rule.bestpractices.switchstmtsshouldhavedefault.SimpleEnum;\n"
+ "\n"
+ " public class Foo {\n"
+ " void bar(SimpleEnum x) {\n"
+ " switch (x) {\n"
+ " case FOO -> System.out.println(\"it is on\");\n"
+ " case BAR -> System.out.println(\"it is off\");\n"
+ " default -> System.out.println(\"it is neither on nor off - should not happen? maybe null?\");\n"
+ " }\n"
+ " }\n"
+ " }")
.get(0);
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertTrue(switchStatement.iterator().hasNext());
Assert.assertFalse(switchStatement.isFallthroughSwitch());
Assert.assertTrue(switchStatement.hasDefaultCase());
}
}

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