Merge branch 'master' of https://github.com/Up2Go/pmd into Up2Go-master
This commit is contained in:
60
README.md
60
README.md
@ -1,58 +1,6 @@
|
||||
# PMD
|
||||
# PMD - Salesforce.com Apex
|
||||
|
||||
[![Build Status](https://travis-ci.org/pmd/pmd.svg?branch=master)](https://travis-ci.org/pmd/pmd)
|
||||
[PMD] (https://github.com/pmd/pmd) fork to develop and maintain a language module for [Salesforce.com Apex] (https://developer.salesforce.com/page/Apex)
|
||||
|
||||
## About
|
||||
|
||||
PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks,
|
||||
unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex, XML, XSL.
|
||||
Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code in
|
||||
Java, C, C++, C#, PHP, Ruby, Fortran, JavaScript, Salesforce.com Apex, Perl, Swift.
|
||||
|
||||
## Source
|
||||
|
||||
Our latest source of PMD can be found on [GitHub]. Fork us!
|
||||
|
||||
### How to build PMD?
|
||||
|
||||
You'll need to have a `~/.m2/toolchains.xml` file setup with jdk 1.6 (for pmd 5.3.x), jdk 1.7 (for pmd 5.4.x and pmd 5.5.x)
|
||||
and jdk 1.8 (for some features in pmd 5.5.x). See [maven toolchains](https://maven.apache.org/guides/mini/guide-using-toolchains.html).
|
||||
A example file can be found here: [example-toolchains.xml](https://github.com/pmd/pmd/blob/master/example-toolchains.xml).
|
||||
|
||||
Use maven in the top-level directory:
|
||||
|
||||
mvn clean package
|
||||
|
||||
This will create the zip files in the directory `pmd-dist/target`:
|
||||
|
||||
cd pmd-dist/target
|
||||
ls *.zip
|
||||
|
||||
That's all !
|
||||
|
||||
### How to build the documentation (maven site)?
|
||||
|
||||
Building the maven site is done with the following commands:
|
||||
|
||||
mvn clean install -DskipTests=true
|
||||
mvn install site site:stage -Psite
|
||||
|
||||
You'll find the built site in the directory `target/staging/`.
|
||||
|
||||
### Bug Reports
|
||||
|
||||
We are using Sourceforge for bug tracking. Please file your bugs at <https://sourceforge.net/p/pmd/bugs/>.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
Pull requests are always welcome: <https://github.com/pmd/pmd/pulls>
|
||||
|
||||
|
||||
## News and Website
|
||||
|
||||
More information can be found on our [Website] and on [SourceForge].
|
||||
|
||||
|
||||
[GitHub]: https://github.com/pmd/pmd
|
||||
[Website]: https://pmd.github.io
|
||||
[SourceForge]: https://sourceforge.net/projects/pmd/
|
||||
### Code Climate Engine
|
||||
There is also a Code Climate Engine based on this PMD module for Salesforce.com Apex. You can find more information about the engine on our [Code Climate Apex Engine GitHub repository] (https://github.com/Up2Go/codeclimate-apex).
|
||||
|
@ -16,6 +16,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTTryCatchFinallyBlockStatement;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTUserEnum;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTUserTrigger;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
|
||||
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
@ -96,6 +97,23 @@ public class StdCyclomaticComplexityRule extends AbstractApexRule {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTUserTrigger node, Object data) {
|
||||
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
|
||||
showClassesComplexity = getProperty(SHOW_CLASSES_COMPLEXITY_DESCRIPTOR);
|
||||
showMethodsComplexity = getProperty(SHOW_METHODS_COMPLEXITY_DESCRIPTOR);
|
||||
entryStack.push(new Entry(node));
|
||||
super.visit(node, data);
|
||||
if (showClassesComplexity) {
|
||||
Entry classEntry = entryStack.pop();
|
||||
if (classEntry.getComplexityAverage() >= reportLevel || classEntry.highestDecisionPoints >= reportLevel) {
|
||||
addViolation(data, node, new String[] { "class", node.getImage(),
|
||||
classEntry.getComplexityAverage() + " (Highest = " + classEntry.highestDecisionPoints + ')' });
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTUserInterface node, Object data) {
|
||||
|
@ -59,6 +59,17 @@ public class Foo {
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Simple trigger</description>
|
||||
<rule-property name="reportLevel">1</rule-property>
|
||||
<expected-problems>2</expected-problems>
|
||||
<code><![CDATA[
|
||||
trigger Accounts on Account (before insert, after insert, before update, after update, before delete, after delete) {
|
||||
fflib_SObjectDomain.triggerHandler(Accounts.class);
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Simple enum</description>
|
||||
<rule-property name="reportLevel">1</rule-property>
|
||||
|
@ -9,12 +9,16 @@ import static net.sourceforge.pmd.renderers.CodeClimateRule.CODECLIMATE_REMEDIAT
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.Rule;
|
||||
import net.sourceforge.pmd.RuleViolation;
|
||||
|
||||
@ -23,10 +27,10 @@ import net.sourceforge.pmd.RuleViolation;
|
||||
*/
|
||||
public class CodeClimateRenderer extends AbstractIncrementingRenderer {
|
||||
public static final String NAME = "codeclimate";
|
||||
public static final String BODY_PLACEHOLDER = "REPLACE_THIS_WITH_MARKDOWN";
|
||||
public static final int REMEDIATION_POINTS_DEFAULT = 50000;
|
||||
public static final String[] CODECLIMATE_DEFAULT_CATEGORIES = new String[]{ "Style" };
|
||||
|
||||
protected static final String EOL = System.getProperty("line.separator", "\n");
|
||||
// Note: required by https://github.com/codeclimate/spec/blob/master/SPEC.md
|
||||
protected static final String NULL_CHARACTER = "\u0000";
|
||||
|
||||
@ -40,11 +44,13 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
|
||||
@Override
|
||||
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
|
||||
Writer writer = getWriter();
|
||||
Gson gson = new Gson();
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
|
||||
while (violations.hasNext()) {
|
||||
RuleViolation rv = violations.next();
|
||||
writer.write(gson.toJson(asIssue(rv)) + NULL_CHARACTER + EOL);
|
||||
String json = gson.toJson(asIssue(rv));
|
||||
json = json.replace(BODY_PLACEHOLDER, getBody(rv));
|
||||
writer.write(json + NULL_CHARACTER + PMD.EOL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,8 +64,8 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
|
||||
|
||||
CodeClimateIssue issue = new CodeClimateIssue();
|
||||
issue.check_name = rule.getName();
|
||||
issue.description = rv.getDescription();
|
||||
issue.content = new CodeClimateIssue.Content(rule.getDescription());
|
||||
issue.description = cleaned(rv.getDescription());
|
||||
issue.content = new CodeClimateIssue.Content(BODY_PLACEHOLDER);
|
||||
issue.location = getLocation(rv);
|
||||
|
||||
switch(rule.getPriority()) {
|
||||
@ -76,22 +82,9 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
|
||||
break;
|
||||
}
|
||||
|
||||
issue.remediation_points = REMEDIATION_POINTS_DEFAULT;
|
||||
if(rule.hasDescriptor(CODECLIMATE_REMEDIATION_MULTIPLIER)) {
|
||||
issue.remediation_points *= rule.getProperty(CODECLIMATE_REMEDIATION_MULTIPLIER);
|
||||
}
|
||||
issue.remediation_points = getRemediationPoints(rule);
|
||||
issue.categories = getCategories(rule);
|
||||
|
||||
if(rule.hasDescriptor(CODECLIMATE_CATEGORIES)) {
|
||||
Object[] categories = rule.getProperty(CODECLIMATE_CATEGORIES);
|
||||
issue.categories = new String[categories.length];
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
issue.categories[i] = String.valueOf(categories[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
issue.categories = CODECLIMATE_DEFAULT_CATEGORIES;
|
||||
}
|
||||
|
||||
return issue;
|
||||
}
|
||||
|
||||
@ -107,4 +100,79 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer {
|
||||
rv.getEndLine());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String cleaned(String original) {
|
||||
String result = original.trim();
|
||||
result = result.replaceAll("\\s+", " ");
|
||||
result = result.replaceAll("\\s*[\\r\\n]+\\s*", "");
|
||||
result = result.replaceAll("'", "`");
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getBody(RuleViolation rv) {
|
||||
Rule rule = rv.getRule();
|
||||
|
||||
String result = "## " + rule.getName() + "\\n\\n" +
|
||||
"Since: PMD " + rule.getSince() + "\\n\\n" +
|
||||
"Priority: " + rule.getPriority() + "\\n\\n" +
|
||||
"[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): " + Arrays.toString(getCategories(rule)).replaceAll("[\\[\\]]","") + "\\n\\n" +
|
||||
"[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): " + getRemediationPoints(rule) + "\\n\\n" +
|
||||
cleaned(rule.getDescription());
|
||||
|
||||
if(!rule.getExamples().isEmpty()) {
|
||||
result += "\\n\\n### Example:\\n\\n";
|
||||
|
||||
for(String snippet : rule.getExamples()) {
|
||||
snippet = snippet.replaceAll("\\n", "\\\\n");
|
||||
snippet = snippet.replaceAll("\\t", "\\\\t");
|
||||
result += "```java\\n" + snippet + "\\n``` ";
|
||||
}
|
||||
}
|
||||
|
||||
if(!rule.getPropertyDescriptors().isEmpty()) {
|
||||
result += "\\n\\n### [PMD properties](http://pmd.github.io/pmd-5.1.3/pmd-developer.html)\\n\\n";
|
||||
result += "Name | Default Value | Description\\n";
|
||||
result += "--- | --- | ---\\n";
|
||||
|
||||
for(PropertyDescriptor<?> property : rule.getPropertyDescriptors()) {
|
||||
String defaultValue;
|
||||
try {
|
||||
defaultValue = Arrays.toString((String[])property.defaultValue()).replaceAll("[\\[\\]]","");
|
||||
}
|
||||
catch(Exception ignore) {
|
||||
defaultValue = property.defaultValue().toString();
|
||||
}
|
||||
result += property.name() + " | " + defaultValue + " | " + property.description() + "\\n";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getRemediationPoints(Rule rule) {
|
||||
int remediation_points = REMEDIATION_POINTS_DEFAULT;
|
||||
|
||||
if(rule.hasDescriptor(CODECLIMATE_REMEDIATION_MULTIPLIER)) {
|
||||
remediation_points *= rule.getProperty(CODECLIMATE_REMEDIATION_MULTIPLIER);
|
||||
}
|
||||
|
||||
return remediation_points;
|
||||
}
|
||||
|
||||
private String[] getCategories(Rule rule) {
|
||||
String[] result;
|
||||
|
||||
if(rule.hasDescriptor(CODECLIMATE_CATEGORIES)) {
|
||||
Object[] categories = rule.getProperty(CODECLIMATE_CATEGORIES);
|
||||
result = new String[categories.length];
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
result[i] = String.valueOf(categories[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = CODECLIMATE_DEFAULT_CATEGORIES;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ public class CodeClimateRendererTest extends AbstractRendererTst {
|
||||
|
||||
@Override
|
||||
public String getExpected() {
|
||||
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL;
|
||||
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\ndesc\\n\\n### [PMD properties](http://pmd.github.io/pmd-5.1.3/pmd-developer.html)\\n\\nName | Default Value | Description\\n--- | --- | ---\\nviolationSuppressRegex | null | Suppress violations with messages matching a regular expression\\nviolationSuppressXPath | null | Suppress violations on nodes which match a given relative XPath expression.\\n\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,8 +24,8 @@ public class CodeClimateRendererTest extends AbstractRendererTst {
|
||||
|
||||
@Override
|
||||
public String getExpectedMultiple() {
|
||||
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL +
|
||||
"{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"desc\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL;
|
||||
return "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\",\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\ndesc\\n\\n### [PMD properties](http://pmd.github.io/pmd-5.1.3/pmd-developer.html)\\n\\nName | Default Value | Description\\n--- | --- | ---\\nviolationSuppressRegex | null | Suppress violations with messages matching a regular expression\\nviolationSuppressXPath | null | Suppress violations on nodes which match a given relative XPath expression.\\n\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"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: Low\\n\\n[Categories](https://github.com/codeclimate/spec/blob/master/SPEC.md#categories): Style\\n\\n[Remediation Points](https://github.com/codeclimate/spec/blob/master/SPEC.md#remediation-points): 50000\\n\\ndesc\\n\\n### [PMD properties](http://pmd.github.io/pmd-5.1.3/pmd-developer.html)\\n\\nName | Default Value | Description\\n--- | --- | ---\\nviolationSuppressRegex | null | Suppress violations with messages matching a regular expression\\nviolationSuppressXPath | null | Suppress violations on nodes which match a given relative XPath expression.\\n\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"n/a\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL;
|
||||
}
|
||||
|
||||
public static junit.framework.Test suite() {
|
||||
|
Reference in New Issue
Block a user