Skip to content

Commit 54f6986

Browse files
CHANGE(pmd): @W-17310830@: Update pmd-wrapper to run rules by language forcefully (#156)
1 parent 928bc6e commit 54f6986

File tree

19 files changed

+609
-224
lines changed

19 files changed

+609
-224
lines changed
Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.salesforce.sfca.cpdwrapper;
22

3+
import com.salesforce.sfca.shared.CodeLocation;
4+
import com.salesforce.sfca.shared.ProcessingError;
5+
36
import java.util.ArrayList;
47
import java.util.List;
58

@@ -15,20 +18,6 @@ public static class Match {
1518
public int numTokensInBlock;
1619
public int numNonemptyLinesInBlock;
1720
public int numBlocks;
18-
public List<BlockLocation> blockLocations = new ArrayList<>();
19-
20-
public static class BlockLocation {
21-
public String file;
22-
public int startLine;
23-
public int startCol;
24-
public int endLine;
25-
public int endCol;
26-
}
27-
}
28-
29-
public static class ProcessingError {
30-
public String file;
31-
public String message;
32-
public String detail;
21+
public List<CodeLocation> blockLocations = new ArrayList<>();
3322
}
3423
}

packages/code-analyzer-pmd-engine/pmd-cpd-wrappers/src/main/java/com/salesforce/sfca/cpdwrapper/CpdRunInputData.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
class CpdRunInputData {
1010
public Map<String, LanguageSpecificRunData> runDataPerLanguage;
1111
public boolean skipDuplicateFiles;
12-
}
1312

14-
class LanguageSpecificRunData {
15-
public List<String> filesToScan;
16-
public int minimumTokens;
13+
static class LanguageSpecificRunData {
14+
public List<String> filesToScan;
15+
public int minimumTokens;
16+
}
1717
}

packages/code-analyzer-pmd-engine/pmd-cpd-wrappers/src/main/java/com/salesforce/sfca/cpdwrapper/CpdRunner.java

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.salesforce.sfca.cpdwrapper;
22

3+
import com.salesforce.sfca.shared.CodeLocation;
4+
import com.salesforce.sfca.shared.ProcessingError;
5+
import com.salesforce.sfca.shared.ProgressReporter;
36
import net.sourceforge.pmd.cpd.CPDConfiguration;
47
import net.sourceforge.pmd.cpd.CPDListener;
58
import net.sourceforge.pmd.cpd.CpdAnalysis;
69
import net.sourceforge.pmd.cpd.Mark;
710
import net.sourceforge.pmd.cpd.Match;
811
import net.sourceforge.pmd.lang.Language;
9-
import net.sourceforge.pmd.lang.document.FileLocation;
1012
import net.sourceforge.pmd.reporting.Report;
1113
import net.sourceforge.pmd.util.log.PmdReporter;
1214
import org.slf4j.event.Level;
@@ -30,12 +32,13 @@ class CpdRunner {
3032
public Map<String, CpdLanguageRunResults> run(CpdRunInputData runInputData) throws IOException {
3133
validateRunInputData(runInputData);
3234

33-
List<String> languagesToProcess = new ArrayList<>(runInputData.runDataPerLanguage.keySet());
34-
progressReporter.initialize(languagesToProcess);
35+
Map<String, Integer> languageFileCounts = runInputData.runDataPerLanguage.entrySet().stream()
36+
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().filesToScan.size()));
37+
progressReporter.initialize(languageFileCounts);
3538

3639
Map<String, CpdLanguageRunResults> results = new HashMap<>();
37-
for (String language : languagesToProcess) {
38-
LanguageSpecificRunData languageSpecificRunData = runInputData.runDataPerLanguage.get(language);
40+
for (String language : runInputData.runDataPerLanguage.keySet()) {
41+
CpdRunInputData.LanguageSpecificRunData languageSpecificRunData = runInputData.runDataPerLanguage.get(language);
3942
List<Path> pathsToScan = languageSpecificRunData.filesToScan.stream().map(Paths::get).collect(Collectors.toList());
4043
CpdLanguageRunResults languageRunResults = runLanguage(
4144
language, pathsToScan, languageSpecificRunData.minimumTokens, runInputData.skipDuplicateFiles);
@@ -47,6 +50,9 @@ public Map<String, CpdLanguageRunResults> run(CpdRunInputData runInputData) thro
4750
}
4851

4952
private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToScan, int minimumTokens, boolean skipDuplicateFiles) throws IOException {
53+
System.out.println("Running CPD for language '" + language + "' with " + pathsToScan.size() +
54+
" file(s) to scan using minimumTokens=" + minimumTokens + " and skipDuplicateFiles=" + skipDuplicateFiles + ".");
55+
5056
// Note that the name "minimumTokens" comes from the public facing documentation and the cli but
5157
// behind the scenes, it maps to MinimumTileSize. To learn more about the mappings to the config, see:
5258
// https://github.com/pmd/pmd/blob/main/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java
@@ -76,11 +82,8 @@ private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToSca
7682

7783
cpd.performAnalysis(report -> {
7884
for (Report.ProcessingError reportProcessingError : report.getProcessingErrors()) {
79-
CpdLanguageRunResults.ProcessingError processingErr = new CpdLanguageRunResults.ProcessingError();
80-
processingErr.file = reportProcessingError.getFileId().getAbsolutePath();
81-
processingErr.message = reportProcessingError.getMsg();
82-
processingErr.detail = reportProcessingError.getDetail();
83-
languageRunResults.processingErrors.add(processingErr);
85+
languageRunResults.processingErrors.add(
86+
ProcessingError.fromReportProcessingError(reportProcessingError));
8487
}
8588

8689
for (Match match : report.getMatches()) {
@@ -90,15 +93,8 @@ private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToSca
9093
cpdMatch.numNonemptyLinesInBlock = match.getLineCount();
9194

9295
for (Mark mark : match.getMarkSet()) {
93-
CpdLanguageRunResults.Match.BlockLocation blockLocation = new CpdLanguageRunResults.Match.BlockLocation();
94-
FileLocation location = mark.getLocation();
95-
blockLocation.file = location.getFileId().getAbsolutePath();
96-
blockLocation.startLine = location.getStartLine();
97-
blockLocation.startCol = location.getStartColumn();
98-
blockLocation.endLine = location.getEndLine();
99-
blockLocation.endCol = location.getEndColumn();
100-
101-
cpdMatch.blockLocations.add(blockLocation);
96+
cpdMatch.blockLocations.add(
97+
CodeLocation.fromFileLocation(mark.getLocation()));
10298
}
10399

104100
languageRunResults.matches.add(cpdMatch);
@@ -108,7 +104,7 @@ private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToSca
108104
// Instead of throwing exceptions and causing the entire run to fail, instead we report exceptions as
109105
// if they are processing errors so that they can better be handled on the typescript side
110106
for (Exception ex : errorListener.exceptionsCaught) {
111-
CpdLanguageRunResults.ProcessingError processingErr = new CpdLanguageRunResults.ProcessingError();
107+
ProcessingError processingErr = new ProcessingError();
112108
processingErr.file = "unknown";
113109
processingErr.message = getStackTraceAsString(ex);
114110
processingErr.detail = "[TERMINATING_EXCEPTION]"; // Marker to help typescript side know this isn't just a normal processing error
@@ -124,14 +120,14 @@ private void validateRunInputData(CpdRunInputData runInputData) {
124120
throw new RuntimeException("The \"runDataPerLanguage\" field was not set.");
125121
}
126122

127-
Set<Map.Entry<String, LanguageSpecificRunData>> entries = runInputData.runDataPerLanguage.entrySet();
123+
Set<Map.Entry<String, CpdRunInputData.LanguageSpecificRunData>> entries = runInputData.runDataPerLanguage.entrySet();
128124
if (entries.isEmpty()) {
129125
throw new RuntimeException("The \"runDataPerLanguage\" field didn't have any languages listed.");
130126
}
131127

132-
for (Map.Entry<String, LanguageSpecificRunData> entry: entries) {
128+
for (Map.Entry<String, CpdRunInputData.LanguageSpecificRunData> entry: entries) {
133129
String language = entry.getKey();
134-
LanguageSpecificRunData languageSpecificRunData = entry.getValue();
130+
CpdRunInputData.LanguageSpecificRunData languageSpecificRunData = entry.getValue();
135131

136132
if (languageSpecificRunData.filesToScan == null || languageSpecificRunData.filesToScan.isEmpty()) {
137133
throw new RuntimeException(("The \"filesToScan\" field was missing or empty for language: " + language));
@@ -176,38 +172,6 @@ public int numErrors() {
176172
}
177173
}
178174

179-
// This class helps us track the overall progress of all language runs
180-
class ProgressReporter {
181-
private Map<String, Float> progressPerLanguage = new HashMap<>();
182-
private float lastReportedProgress = 0.0f;
183-
184-
public void initialize(List<String> languages) {
185-
progressPerLanguage = new HashMap<>();
186-
languages.forEach(l -> this.updateProgressForLanguage(l, 0.0f));
187-
}
188-
189-
public void updateProgressForLanguage(String language, float percComplete) {
190-
progressPerLanguage.put(language, percComplete);
191-
}
192-
193-
public void reportOverallProgress() {
194-
float currentProgress = this.calculateOverallPercentage();
195-
// The progress goes very fast, so we make sure to only report progress if there has been a significant enough increase (at least 1%)
196-
if (currentProgress >= lastReportedProgress + 1) {
197-
System.out.println("[Progress]" + currentProgress);
198-
lastReportedProgress = currentProgress;
199-
}
200-
}
201-
202-
private float calculateOverallPercentage() {
203-
float sum = 0.0f;
204-
for (float progress : progressPerLanguage.values()) {
205-
sum += progress;
206-
}
207-
return sum / progressPerLanguage.size();
208-
}
209-
}
210-
211175
// This class is a specific listener for a run of cpd for a single language.
212176
class CpdLanguageRunListener implements CPDListener {
213177
private final ProgressReporter progressReporter;

packages/code-analyzer-pmd-engine/pmd-cpd-wrappers/src/main/java/com/salesforce/sfca/cpdwrapper/CpdWrapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* },
2525
* ...,
2626
* "xml": {
27-
* "filesToScan": ["full/path/to/xml_file1.xml", "/full/path/to/xml_file2.xml", ...],
27+
* "filesToScan": ["/full/path/to/xml_file1.xml", "/full/path/to/xml_file2.xml", ...],
2828
* "minimumTokens": 150
2929
* }
3030
* },

packages/code-analyzer-pmd-engine/pmd-cpd-wrappers/src/main/java/com/salesforce/sfca/pmdwrapper/PmdRuleRunner.java

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.salesforce.sfca.pmdwrapper;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
public class PmdRunInputData {
7+
public String ruleSetInputFile;
8+
public Map<String, LanguageSpecificRunData> runDataPerLanguage;
9+
10+
public static class LanguageSpecificRunData {
11+
public List<String> filesToScan;
12+
}
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.salesforce.sfca.pmdwrapper;
2+
3+
import com.salesforce.sfca.shared.CodeLocation;
4+
import com.salesforce.sfca.shared.ProcessingError;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
/**
10+
* Java object to help build pmd results for all languages.
11+
*/
12+
public class PmdRunResults {
13+
public List<Violation> violations = new ArrayList<>();
14+
public List<ProcessingError> processingErrors = new ArrayList<>();
15+
16+
public static class Violation {
17+
public String rule;
18+
public String message;
19+
public CodeLocation codeLocation;
20+
}
21+
}

0 commit comments

Comments
 (0)