Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHANGE(pmd): @W-17310830@: Update pmd-wrapper to run rules by language forcefully #156

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.salesforce.sfca.cpdwrapper;

import com.salesforce.sfca.shared.CodeLocation;
import com.salesforce.sfca.shared.ProcessingError;

import java.util.ArrayList;
import java.util.List;

Expand All @@ -15,20 +18,6 @@ public static class Match {
public int numTokensInBlock;
public int numNonemptyLinesInBlock;
public int numBlocks;
public List<BlockLocation> blockLocations = new ArrayList<>();

public static class BlockLocation {
public String file;
public int startLine;
public int startCol;
public int endLine;
public int endCol;
}
}

public static class ProcessingError {
public String file;
public String message;
public String detail;
public List<CodeLocation> blockLocations = new ArrayList<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
class CpdRunInputData {
public Map<String, LanguageSpecificRunData> runDataPerLanguage;
public boolean skipDuplicateFiles;
}

class LanguageSpecificRunData {
public List<String> filesToScan;
public int minimumTokens;
static class LanguageSpecificRunData {
public List<String> filesToScan;
public int minimumTokens;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.salesforce.sfca.cpdwrapper;

import com.salesforce.sfca.shared.CodeLocation;
import com.salesforce.sfca.shared.ProcessingError;
import com.salesforce.sfca.shared.ProgressReporter;
import net.sourceforge.pmd.cpd.CPDConfiguration;
import net.sourceforge.pmd.cpd.CPDListener;
import net.sourceforge.pmd.cpd.CpdAnalysis;
import net.sourceforge.pmd.cpd.Mark;
import net.sourceforge.pmd.cpd.Match;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.reporting.Report;
import net.sourceforge.pmd.util.log.PmdReporter;
import org.slf4j.event.Level;
Expand All @@ -30,12 +32,13 @@ class CpdRunner {
public Map<String, CpdLanguageRunResults> run(CpdRunInputData runInputData) throws IOException {
validateRunInputData(runInputData);

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

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

private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToScan, int minimumTokens, boolean skipDuplicateFiles) throws IOException {
System.out.println("Running CPD for language '" + language + "' with " + pathsToScan.size() +
" file(s) to scan using minimumTokens=" + minimumTokens + " and skipDuplicateFiles=" + skipDuplicateFiles + ".");

// Note that the name "minimumTokens" comes from the public facing documentation and the cli but
// behind the scenes, it maps to MinimumTileSize. To learn more about the mappings to the config, see:
// https://github.com/pmd/pmd/blob/main/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java
Expand Down Expand Up @@ -76,11 +82,8 @@ private CpdLanguageRunResults runLanguage(String language, List<Path> pathsToSca

cpd.performAnalysis(report -> {
for (Report.ProcessingError reportProcessingError : report.getProcessingErrors()) {
CpdLanguageRunResults.ProcessingError processingErr = new CpdLanguageRunResults.ProcessingError();
processingErr.file = reportProcessingError.getFileId().getAbsolutePath();
processingErr.message = reportProcessingError.getMsg();
processingErr.detail = reportProcessingError.getDetail();
languageRunResults.processingErrors.add(processingErr);
languageRunResults.processingErrors.add(
ProcessingError.fromReportProcessingError(reportProcessingError));
}

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

for (Mark mark : match.getMarkSet()) {
CpdLanguageRunResults.Match.BlockLocation blockLocation = new CpdLanguageRunResults.Match.BlockLocation();
FileLocation location = mark.getLocation();
blockLocation.file = location.getFileId().getAbsolutePath();
blockLocation.startLine = location.getStartLine();
blockLocation.startCol = location.getStartColumn();
blockLocation.endLine = location.getEndLine();
blockLocation.endCol = location.getEndColumn();

cpdMatch.blockLocations.add(blockLocation);
cpdMatch.blockLocations.add(
CodeLocation.fromFileLocation(mark.getLocation()));
}

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

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

for (Map.Entry<String, LanguageSpecificRunData> entry: entries) {
for (Map.Entry<String, CpdRunInputData.LanguageSpecificRunData> entry: entries) {
String language = entry.getKey();
LanguageSpecificRunData languageSpecificRunData = entry.getValue();
CpdRunInputData.LanguageSpecificRunData languageSpecificRunData = entry.getValue();

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

// This class helps us track the overall progress of all language runs
class ProgressReporter {
private Map<String, Float> progressPerLanguage = new HashMap<>();
private float lastReportedProgress = 0.0f;

public void initialize(List<String> languages) {
progressPerLanguage = new HashMap<>();
languages.forEach(l -> this.updateProgressForLanguage(l, 0.0f));
}

public void updateProgressForLanguage(String language, float percComplete) {
progressPerLanguage.put(language, percComplete);
}

public void reportOverallProgress() {
float currentProgress = this.calculateOverallPercentage();
// The progress goes very fast, so we make sure to only report progress if there has been a significant enough increase (at least 1%)
if (currentProgress >= lastReportedProgress + 1) {
System.out.println("[Progress]" + currentProgress);
lastReportedProgress = currentProgress;
}
}

private float calculateOverallPercentage() {
float sum = 0.0f;
for (float progress : progressPerLanguage.values()) {
sum += progress;
}
return sum / progressPerLanguage.size();
}
}

// This class is a specific listener for a run of cpd for a single language.
class CpdLanguageRunListener implements CPDListener {
private final ProgressReporter progressReporter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* },
* ...,
* "xml": {
* "filesToScan": ["full/path/to/xml_file1.xml", "/full/path/to/xml_file2.xml", ...],
* "filesToScan": ["/full/path/to/xml_file1.xml", "/full/path/to/xml_file2.xml", ...],
* "minimumTokens": 150
* }
* },
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.salesforce.sfca.pmdwrapper;

import java.util.List;
import java.util.Map;

public class PmdRunInputData {
public String ruleSetInputFile;
public Map<String, LanguageSpecificRunData> runDataPerLanguage;

public static class LanguageSpecificRunData {
public List<String> filesToScan;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.salesforce.sfca.pmdwrapper;

import com.salesforce.sfca.shared.CodeLocation;
import com.salesforce.sfca.shared.ProcessingError;

import java.util.ArrayList;
import java.util.List;

/**
* Java object to help build pmd results for all languages.
*/
public class PmdRunResults {
public List<Violation> violations = new ArrayList<>();
public List<ProcessingError> processingErrors = new ArrayList<>();

public static class Violation {
public String rule;
public String message;
public CodeLocation codeLocation;
}
}
Loading
Loading