diff --git a/.all-contributorsrc b/.all-contributorsrc index 531090e31d..7d94f872d8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -6539,6 +6539,15 @@ "code" ] }, + { + "login": "filiprafalowicz", + "name": "filiprafalowicz", + "avatar_url": "https://avatars.githubusercontent.com/u/24355557?v=4", + "profile": "https://github.com/filiprafalowicz", + "contributions": [ + "code" + ] + }, { "login": "JerritEic", "name": "JerritEic", diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87417153a5..b4b787a283 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 2 - uses: actions/cache@v2 diff --git a/.github/workflows/git-repo-sync.yml b/.github/workflows/git-repo-sync.yml index 2219f2330a..250dd00933 100644 --- a/.github/workflows/git-repo-sync.yml +++ b/.github/workflows/git-repo-sync.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 100 - name: Setup Environment diff --git a/.github/workflows/troubleshooting.yml b/.github/workflows/troubleshooting.yml index d58e0f2aa1..4554ddedf7 100644 --- a/.github/workflows/troubleshooting.yml +++ b/.github/workflows/troubleshooting.yml @@ -12,7 +12,7 @@ jobs: os: [ ubuntu-latest ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/cache@v2 with: path: | diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index c2d45320d9..fdac14b9c1 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -775,163 +775,164 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
accounts) {
@@ -21,6 +22,7 @@ public class Foo {
No describer options using Schema class
1
+ 3
accounts) {
@@ -89,4 +91,54 @@ public class Foo {
]]>
+
+ False positive with no describer options on SObjectField
+ 0
+
+
+
+
+
+ False positive on SObjectField with FieldDescribeOptions.FULL_DESCRIBE
+ 0
+
+
+
+
+ False positive on SObjectField with FieldDescribeOptions.DEFAULT
+ 0
+
+
+
+
+ False positive on SObjectField with FieldDescribeOptions.DEFAULT with noDefault=true
+ true
+ 0
+
+
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
index eb8fcb2381..9c1b261c88 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
@@ -6,21 +6,14 @@ package net.sourceforge.pmd;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.net.URISyntaxException;
-import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
-import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,8 +22,6 @@ import org.slf4j.event.Level;
import net.sourceforge.pmd.Report.GlobalReportBuilderListener;
import net.sourceforge.pmd.benchmark.TextTimingReportRenderer;
import net.sourceforge.pmd.benchmark.TimeTracker;
-import net.sourceforge.pmd.benchmark.TimedOperation;
-import net.sourceforge.pmd.benchmark.TimedOperationCategory;
import net.sourceforge.pmd.benchmark.TimingReport;
import net.sourceforge.pmd.benchmark.TimingReportRenderer;
import net.sourceforge.pmd.cache.NoopAnalysisCache;
@@ -38,38 +29,19 @@ import net.sourceforge.pmd.cli.PMDCommandLineInterface;
import net.sourceforge.pmd.cli.PmdParametersParseResult;
import net.sourceforge.pmd.cli.internal.CliMessages;
import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
-import net.sourceforge.pmd.internal.util.AssertionUtil;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageFilenameFilter;
-import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
-import net.sourceforge.pmd.processor.AbstractPMDProcessor;
import net.sourceforge.pmd.renderers.Renderer;
-import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
-import net.sourceforge.pmd.reporting.GlobalAnalysisListener.ViolationCounterListener;
-import net.sourceforge.pmd.util.ClasspathClassLoader;
-import net.sourceforge.pmd.util.FileUtil;
-import net.sourceforge.pmd.util.IOUtil;
-import net.sourceforge.pmd.util.database.DBMSMetadata;
-import net.sourceforge.pmd.util.database.DBURI;
-import net.sourceforge.pmd.util.database.SourceObject;
+import net.sourceforge.pmd.reporting.ReportStats;
+import net.sourceforge.pmd.reporting.ReportStatsListener;
import net.sourceforge.pmd.util.datasource.DataSource;
-import net.sourceforge.pmd.util.datasource.ReaderDataSource;
+import net.sourceforge.pmd.util.log.MessageReporter;
+import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter;
/**
- * This is the main class for interacting with PMD. The primary flow of all Rule
- * process is controlled via interactions with this class. A command line
- * interface is supported, as well as a programmatic API for integrating PMD
- * with other software such as IDEs and Ant.
- *
- * Main entrypoints are:
- *
- * - {@link #main(String[])} which exits the java process
- * - {@link #runPmd(String...)} which returns a {@link StatusCode}
- * - {@link #runPmd(PMDConfiguration)}
- * - {@link #processFiles(PMDConfiguration, List, Collection, List)}
- *
- *
+ * Entry point for PMD's CLI. Use {@link #runPmd(PMDConfiguration)}
+ * or {@link #runPmd(String...)} to mimic a CLI run. This class is
+ * not a supported programmatic API anymore, use {@link PmdAnalysis}
+ * for fine control over the PMD integration and execution.
+ *
* Warning: This class is not intended to be instantiated or subclassed. It will
* be made final in PMD7.
*/
@@ -81,158 +53,60 @@ public final class PMD {
/**
* The line delimiter used by PMD in outputs. Usually the platform specific
* line separator.
+ *
+ * @deprecated Use {@link System#lineSeparator()}
*/
- public static final String EOL = System.getProperty("line.separator", "\n");
+ @Deprecated
+ public static final String EOL = System.lineSeparator();
- /** The default suppress marker string. */
- public static final String SUPPRESS_MARKER = "NOPMD";
+ /**
+ * The default suppress marker string.
+ *
+ * @deprecated Use {@link PMDConfiguration#DEFAULT_SUPPRESS_MARKER}
+ */
+ @Deprecated
+ public static final String SUPPRESS_MARKER = PMDConfiguration.DEFAULT_SUPPRESS_MARKER;
private PMD() {
}
- /**
- * Parses the given string as a database uri and returns a list of
- * datasources.
- *
- * @param uriString the URI to parse
- *
- * @return list of data sources
- *
- * @throws IOException if the URI couldn't be parsed
- * @see DBURI
- *
- * @deprecated Will be hidden as part of the parsing of {@link PMD#getApplicableFiles(PMDConfiguration, Set)}
- */
- @Deprecated
- public static List getURIDataSources(String uriString) throws IOException {
- List dataSources = new ArrayList<>();
+ private static ReportStats runAndReturnStats(PmdAnalysis pmd) {
+ if (pmd.getRulesets().isEmpty()) {
+ return ReportStats.empty();
+ }
+
+ @SuppressWarnings("PMD.CloseResource")
+ ReportStatsListener listener = new ReportStatsListener();
+
+ pmd.addListener(listener);
try {
- DBURI dbUri = new DBURI(uriString);
- DBMSMetadata dbmsMetadata = new DBMSMetadata(dbUri);
- log.debug("DBMSMetadata retrieved");
- List sourceObjectList = dbmsMetadata.getSourceObjectList();
- log.debug("Located {} database source objects", sourceObjectList.size());
- for (SourceObject sourceObject : sourceObjectList) {
- String falseFilePath = sourceObject.getPseudoFileName();
- log.trace("Adding database source object {}", falseFilePath);
-
- try {
- dataSources.add(new ReaderDataSource(dbmsMetadata.getSourceCode(sourceObject), falseFilePath));
- } catch (SQLException ex) {
- log.warn("Cannot get SourceCode for {} - skipping ...", falseFilePath, ex);
- }
- }
- } catch (URISyntaxException e) {
- throw new IOException("Cannot get DataSources from DBURI - \"" + uriString + "\"", e);
- } catch (SQLException e) {
- throw new IOException("Cannot get DataSources from DBURI, couldn't access the database - \"" + uriString + "\"", e);
- } catch (ClassNotFoundException e) {
- throw new IOException("Cannot get DataSources from DBURI, probably missing database jdbc driver - \"" + uriString + "\"", e);
+ pmd.performAnalysis();
} catch (Exception e) {
- throw new IOException("Encountered unexpected problem with URI \"" + uriString + "\"", e);
+ pmd.getReporter().errorEx("Exception during processing", e);
+ ReportStats stats = listener.getResult();
+ printErrorDetected(1 + stats.getNumErrors());
+ return stats; // should have been closed
}
- return dataSources;
- }
+ ReportStats stats = listener.getResult();
- /**
- * This method is the main entry point for command line usage.
- *
- * @param configuration the configuration to use
- *
- * @return number of violations found.
- *
- * @deprecated Use {@link #runPmd(PMDConfiguration)}.
- */
- @Deprecated
- public static int doPMD(final PMDConfiguration configuration) {
-
- // Load the RuleSets
- final RuleSetLoader ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration);
- final List ruleSets = getRuleSetsWithBenchmark(configuration.getRuleSetPaths(), ruleSetFactory);
-
- final Set languages = getApplicableLanguages(configuration, ruleSets);
-
- try {
-
- final List files = getApplicableFiles(configuration, languages);
- Renderer renderer = configuration.createRenderer(true);
-
- @SuppressWarnings("PMD.CloseResource")
- ViolationCounterListener violationCounter = new ViolationCounterListener();
- @SuppressWarnings("PMD.CloseResource")
- GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener();
-
- List allListeners = listOf(
- reportBuilder,
- renderer.newListener(),
- violationCounter);
- try (GlobalAnalysisListener listener = GlobalAnalysisListener.tee(allListeners)) {
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) {
- processFiles(configuration, ruleSets, files, listener);
- }
- }
-
- Report report = reportBuilder.getResult();
- if (!report.getProcessingErrors().isEmpty()) {
- printErrorDetected(report.getProcessingErrors().size());
- }
-
- return violationCounter.getResult();
- } catch (final Exception e) {
- log.error("Exception during processing: {}", e.toString()); // only exception without stacktrace
- log.debug("Exception during processing", e); // with stacktrace
- printErrorDetected(1);
- return PMDCommandLineInterface.NO_ERRORS_STATUS; // fixme?
- } finally {
- /*
- * Make sure it's our own classloader before attempting to close it....
- * Maven + Jacoco provide us with a cloaseable classloader that if closed
- * will throw a ClassNotFoundException.
- */
- if (configuration.getClassLoader() instanceof ClasspathClassLoader) {
- IOUtil.tryCloseClassLoader(configuration.getClassLoader());
- }
+ if (stats.getNumErrors() > 0) {
+ printErrorDetected(stats.getNumErrors());
}
+
+ return stats;
}
- private static List getRuleSetsWithBenchmark(List rulesetPaths, RuleSetLoader factory) {
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) {
- List ruleSets;
- try {
- ruleSets = factory.loadFromResources(rulesetPaths);
- printRuleNamesInDebug(ruleSets);
- if (isEmpty(ruleSets)) {
- String msg = "No rules found. Maybe you misspelled a rule name? ("
- + String.join(",", rulesetPaths) + ')';
- log.error(msg);
- throw new IllegalArgumentException(msg);
- }
- } catch (RuleSetLoadException rsnfe) {
- log.error("Ruleset not found", rsnfe);
- throw rsnfe;
- }
- return ruleSets;
- }
- }
- private static boolean isEmpty(List rsets) {
- return rsets.stream().noneMatch(it -> it.size() > 0);
- }
-
- /**
- * If in debug modus, print the names of the rules.
- *
- * @param rulesets the RuleSets to print
- */
- private static void printRuleNamesInDebug(List rulesets) {
- if (log.isDebugEnabled()) {
- for (RuleSet rset : rulesets) {
- for (Rule r : rset.getRules()) {
- log.debug("Loaded rule {}", r.getName());
- }
- }
+ static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) {
+ if (!configuration.isIgnoreIncrementalAnalysis()
+ && configuration.getAnalysisCache() instanceof NoopAnalysisCache
+ && log.isWarnEnabled()) {
+ final String version =
+ PMDVersion.isUnknown() || PMDVersion.isSnapshot() ? "latest" : "pmd-" + PMDVersion.VERSION;
+ log.warn("This analysis could be faster, please consider using Incremental Analysis: "
+ + "https://pmd.github.io/{}/pmd_userdocs_incremental_analysis.html", version);
}
}
@@ -249,227 +123,25 @@ public final class PMD {
* @return Report in which violations are accumulated
*
* @throws Exception If there was a problem when opening or closing the renderers
+ *
+ * @deprecated Use {@link PmdAnalysis}
*/
- @SuppressWarnings("PMD.CloseResource")
+ @Deprecated
public static Report processFiles(PMDConfiguration configuration,
List ruleSets,
Collection extends DataSource> files,
List renderers) throws Exception {
-
- GlobalAnalysisListener rendererListeners = createComposedRendererListener(renderers);
- GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener();
-
- List allListeners = listOf(reportBuilder, rendererListeners);
-
- try (GlobalAnalysisListener listener = GlobalAnalysisListener.tee(allListeners)) {
- processFiles(configuration, ruleSets, new ArrayList<>(files), listener);
+ try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
+ pmd.addRuleSets(ruleSets);
+ pmd.addRenderers(renderers);
+ @SuppressWarnings("PMD.CloseResource")
+ GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener();
+ List sortedFiles = new ArrayList<>(files);
+ sortedFiles.sort(Comparator.comparing(ds -> ds.getNiceFileName(false, "")));
+ pmd.performAnalysisImpl(listOf(reportBuilder), sortedFiles);
+ return reportBuilder.getResult();
}
-
- return reportBuilder.getResult();
- }
-
- private static GlobalAnalysisListener createComposedRendererListener(List renderers) throws Exception {
- if (renderers.isEmpty()) {
- return GlobalAnalysisListener.noop();
- }
-
- List rendererListeners = new ArrayList<>(renderers.size());
- for (Renderer renderer : renderers) {
- try {
- rendererListeners.add(renderer.newListener());
- } catch (IOException ioe) {
- // close listeners so far, throw their close exception or the ioe
- IOUtil.ensureClosed(rendererListeners, ioe);
- throw AssertionUtil.shouldNotReachHere("ensureClosed should have thrown");
- }
- }
- return GlobalAnalysisListener.tee(rendererListeners);
- }
-
-
- /**
- * Run PMD using the given configuration. This replaces the other overload.
- *
- * @param configuration Configuration for the run. Note that the files,
- * and rulesets, are ignored, as they are supplied
- * as parameters
- * @param ruleSets Parsed rulesets
- * @param files Files to process, will be closed by this method.
- * @param listener Listener to which analysis events are forwarded.
- * The listener is NOT closed by this routine and should
- * be closed by the caller.
- *
- * @throws Exception If there was a problem when opening or closing the renderers
- */
- public static void processFiles(PMDConfiguration configuration,
- List ruleSets,
- List files,
- GlobalAnalysisListener listener) throws Exception {
-
- final RuleSets rs = new RuleSets(ruleSets);
-
- // todo Just like we throw for invalid properties, "broken rules"
- // shouldn't be a "config error". This is the only instance of
- // config errors...
-
- for (final Rule rule : removeBrokenRules(rs)) {
- listener.onConfigError(new Report.ConfigurationError(rule, rule.dysfunctionReason()));
- }
-
- encourageToUseIncrementalAnalysis(configuration);
-
- List sortedFiles = sortFiles(configuration, files);
-
- // Make sure the cache is listening for analysis results
- listener = GlobalAnalysisListener.tee(listOf(listener, configuration.getAnalysisCache()));
-
- configuration.getAnalysisCache().checkValidity(rs, configuration.getClassLoader());
-
- Exception ex = null;
- try (AbstractPMDProcessor processor = AbstractPMDProcessor.newFileProcessor(configuration)) {
- processor.processFiles(rs, sortedFiles, listener);
- } catch (Exception e) {
- ex = e;
- } finally {
- configuration.getAnalysisCache().persist();
- IOUtil.ensureClosed(sortedFiles, ex);
- }
- }
-
-
- /**
- * Remove and return the misconfigured rules from the rulesets and log them
- * for good measure.
- *
- * @param ruleSets RuleSets to prune of broken rules.
- *
- * @return Set
- */
- private static Set removeBrokenRules(final RuleSets ruleSets) {
- final Set brokenRules = new HashSet<>();
- ruleSets.removeDysfunctionalRules(brokenRules);
-
- for (final Rule rule : brokenRules) {
- log.warn("Removed misconfigured rule: {} cause: {}", rule.getName(), rule.dysfunctionReason());
- }
-
- return brokenRules;
- }
-
-
- private static List sortFiles(final PMDConfiguration configuration, Collection extends DataSource> files) {
- // the input collection may be unmodifiable
- List result = new ArrayList<>(files);
-
- if (configuration.isStressTest()) {
- // randomize processing order
- Collections.shuffle(result);
- } else {
- final boolean useShortNames = configuration.isReportShortNames();
- final String inputPaths = configuration.getInputPaths();
- result.sort((left, right) -> {
- String leftString = left.getNiceFileName(useShortNames, inputPaths);
- String rightString = right.getNiceFileName(useShortNames, inputPaths);
- return leftString.compareTo(rightString);
- });
- }
-
- return result;
- }
-
- private static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) {
- if (!configuration.isIgnoreIncrementalAnalysis()
- && configuration.getAnalysisCache() instanceof NoopAnalysisCache
- && log.isWarnEnabled()) {
- final String version =
- PMDVersion.isUnknown() || PMDVersion.isSnapshot() ? "latest" : "pmd-" + PMDVersion.VERSION;
- log.warn("This analysis could be faster, please consider using Incremental Analysis: "
- + "https://pmd.github.io/{}/pmd_userdocs_incremental_analysis.html", version);
- }
- }
-
- /**
- * Determines all the files, that should be analyzed by PMD.
- *
- * @param configuration
- * contains either the file path or the DB URI, from where to
- * load the files
- * @param languages
- * used to filter by file extension
- * @return List of {@link DataSource} of files
- */
- public static List getApplicableFiles(PMDConfiguration configuration, Set languages) throws IOException {
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.COLLECT_FILES)) {
- return internalGetApplicableFiles(configuration, languages);
- }
- }
-
- private static List internalGetApplicableFiles(PMDConfiguration configuration,
- Set languages) throws IOException {
- FilenameFilter fileSelector = configuration.isForceLanguageVersion() ? new AcceptAllFilenames() : new LanguageFilenameFilter(languages);
- List files = new ArrayList<>();
-
- if (null != configuration.getInputPaths()) {
- files.addAll(FileUtil.collectFiles(configuration.getInputPaths(), fileSelector));
- }
-
- if (null != configuration.getInputUri()) {
- String uriString = configuration.getInputUri();
- files.addAll(getURIDataSources(uriString));
- }
-
- if (null != configuration.getInputFilePath()) {
- String inputFilePath = configuration.getInputFilePath();
- File file = new File(inputFilePath);
- if (!file.exists()) {
- throw new FileNotFoundException(inputFilePath);
- }
-
- try {
- String filePaths = FileUtil.readFilelist(file);
- files.addAll(FileUtil.collectFiles(filePaths, fileSelector));
- } catch (IOException ex) {
- throw new IOException("Problem with Input File Path: " + inputFilePath, ex);
- }
-
- }
-
- if (null != configuration.getIgnoreFilePath()) {
- String ignoreFilePath = configuration.getIgnoreFilePath();
- File file = new File(ignoreFilePath);
- if (!file.exists()) {
- throw new FileNotFoundException(ignoreFilePath);
- }
-
- try {
- String filePaths = FileUtil.readFilelist(file);
- files.removeAll(FileUtil.collectFiles(filePaths, fileSelector));
- } catch (IOException ex) {
- log.error("Problem with Ignore File", ex);
- throw new RuntimeException("Problem with Ignore File Path: " + ignoreFilePath, ex);
- }
- }
- return files;
- }
-
- private static Set getApplicableLanguages(final PMDConfiguration configuration, final List ruleSets) {
- final Set languages = new HashSet<>();
- final LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();
-
- for (final RuleSet ruleSet : ruleSets) {
- for (Rule rule : ruleSet.getRules()) {
- final Language ruleLanguage = rule.getLanguage();
- if (!languages.contains(ruleLanguage)) {
- final LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage);
- if (RuleSet.applies(rule, version)) {
- languages.add(ruleLanguage);
- log.debug("Using {} version: {}", ruleLanguage.getShortName(), version.getShortName());
- }
- }
- }
- }
- return languages;
}
/**
@@ -483,23 +155,6 @@ public final class PMD {
PMDCommandLineInterface.setStatusCodeOrExit(exitCode.toInt());
}
- /**
- * Parses the command line arguments and executes PMD. Returns the
- * exit code without exiting the VM.
- *
- * @param args command line arguments
- *
- * @return the exit code, where 0
means successful execution,
- * 1
means error, 4
means there have been
- * violations found.
- *
- * @deprecated Use {@link #runPmd(String...)}.
- */
- @Deprecated
- public static int run(final String[] args) {
- return runPmd(args).toInt();
- }
-
/**
* Parses the command line arguments and executes PMD. Returns the
* status code without exiting the VM. Note that the arguments parsing
@@ -515,6 +170,7 @@ public final class PMD {
public static StatusCode runPmd(String... args) {
PmdParametersParseResult parseResult = PmdParametersParseResult.extractParameters(args);
+ // todo these warnings/errors should be output on a PmdRenderer
if (!parseResult.getDeprecatedOptionsUsed().isEmpty()) {
Entry first = parseResult.getDeprecatedOptionsUsed().entrySet().iterator().next();
log.warn("Some deprecated options were used on the command-line, including {}", first.getKey());
@@ -560,44 +216,68 @@ public final class PMD {
// only reconfigure logging, if debug flag was used on command line
// otherwise just use whatever is in conf/simplelogger.properties which happens automatically
if (configuration.isDebug()) {
- Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.DEBUG);
+ Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE);
// need to reload the logger with the new configuration
log = LoggerFactory.getLogger(PMD.class);
}
+ // create a top-level reporter
+ // TODO CLI errors should also be reported through this
+ // TODO this should not use the logger as backend, otherwise without
+ // slf4j implementation binding, errors are entirely ignored.
+ MessageReporter pmdReporter = new SimpleMessageReporter(log);
// always install java.util.logging to slf4j bridge
Slf4jSimpleConfiguration.installJulBridge();
// logging, mostly for testing purposes
Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel();
- log.atLevel(defaultLogLevel).log("Log level is at {}", defaultLogLevel);
+ log.info("Log level is at {}", defaultLogLevel);
- StatusCode status;
try {
- int violations = PMD.doPMD(configuration);
- if (violations > 0 && configuration.isFailOnViolation()) {
- status = StatusCode.VIOLATIONS_FOUND;
- } else {
- status = StatusCode.OK;
+ PmdAnalysis pmd;
+ try {
+ pmd = PmdAnalysis.create(configuration, pmdReporter);
+ } catch (Exception e) {
+ pmdReporter.errorEx("Could not initialize analysis", e);
+ return StatusCode.ERROR;
}
- } catch (Exception e) {
- System.err.println(e.getMessage());
- status = StatusCode.ERROR;
- } finally {
- if (configuration.isBenchmark()) {
- final TimingReport timingReport = TimeTracker.stopGlobalTracking();
-
- // TODO get specified report format from config
- final TimingReportRenderer renderer = new TextTimingReportRenderer();
- try {
- // Don't close this writer, we don't want to close stderr
- @SuppressWarnings("PMD.CloseResource")
- final Writer writer = new OutputStreamWriter(System.err);
- renderer.render(timingReport, writer);
- } catch (final IOException e) {
- System.err.println(e.getMessage());
+ try {
+ ReportStats stats;
+ stats = PMD.runAndReturnStats(pmd);
+ if (pmdReporter.numErrors() > 0) {
+ // processing errors are ignored
+ return StatusCode.ERROR;
+ } else if (stats.getNumViolations() > 0 && configuration.isFailOnViolation()) {
+ return StatusCode.VIOLATIONS_FOUND;
+ } else {
+ return StatusCode.OK;
}
+ } finally {
+ pmd.close();
+ }
+
+ } catch (Exception e) {
+ pmdReporter.errorEx("Exception while running PMD.", e);
+ printErrorDetected(1);
+ return StatusCode.ERROR;
+ } finally {
+ finishBenchmarker(configuration);
+ }
+ }
+
+ private static void finishBenchmarker(PMDConfiguration configuration) {
+ if (configuration.isBenchmark()) {
+ final TimingReport timingReport = TimeTracker.stopGlobalTracking();
+
+ // TODO get specified report format from config
+ final TimingReportRenderer renderer = new TextTimingReportRenderer();
+ try {
+ // Don't close this writer, we don't want to close stderr
+ @SuppressWarnings("PMD.CloseResource")
+ final Writer writer = new OutputStreamWriter(System.err);
+ renderer.render(timingReport, writer);
+ } catch (final IOException e) {
+ System.err.println(e.getMessage());
}
}
- return status;
}
/**
@@ -633,10 +313,4 @@ public final class PMD {
}
- private static final class AcceptAllFilenames implements FilenameFilter {
- @Override
- public boolean accept(File dir, String name) {
- return true;
- }
- }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java
index 1b9931f537..bc6e618aa2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java
@@ -13,12 +13,14 @@ import java.util.Objects;
import java.util.Properties;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.cache.AnalysisCache;
import net.sourceforge.pmd.cache.FileAnalysisCache;
import net.sourceforge.pmd.cache.NoopAnalysisCache;
import net.sourceforge.pmd.cli.PmdParametersParseResult;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
@@ -44,7 +46,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader;
* {@link #getClassLoader()}
* A means to configure a ClassLoader using a prepended classpath String,
* instead of directly setting it programmatically.
- * {@link #prependClasspath(String)}
+ * {@link #prependAuxClasspath(String)}
* A LanguageVersionDiscoverer instance, which defaults to using the default
* LanguageVersion of each Language. Means are provided to change the
* LanguageVersion for each Language.
@@ -88,15 +90,19 @@ import net.sourceforge.pmd.util.ClasspathClassLoader;
*
*/
public class PMDConfiguration extends AbstractConfiguration {
+
+ /** The default suppress marker string. */
+ public static final String DEFAULT_SUPPRESS_MARKER = "NOPMD";
+
// General behavior options
- private String suppressMarker = PMD.SUPPRESS_MARKER;
+ private String suppressMarker = DEFAULT_SUPPRESS_MARKER;
private int threads = Runtime.getRuntime().availableProcessors();
private ClassLoader classLoader = getClass().getClassLoader();
private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer();
private LanguageVersion forceLanguageVersion;
// Rule and source file options
- private List ruleSets;
+ private List ruleSets = new ArrayList<>();
private RulePriority minimumPriority = RulePriority.LOW;
private String inputPaths;
private String inputUri;
@@ -197,13 +203,46 @@ public class PMDConfiguration extends AbstractConfiguration {
* if the given classpath is invalid (e.g. does not exist)
* @see PMDConfiguration#setClassLoader(ClassLoader)
* @see ClasspathClassLoader
+ *
+ * @deprecated Use {@link #prependAuxClasspath(String)}, which doesn't
+ * throw a checked {@link IOException}
*/
+ @Deprecated
public void prependClasspath(String classpath) throws IOException {
- if (classLoader == null) {
- classLoader = PMDConfiguration.class.getClassLoader();
+ try {
+ prependAuxClasspath(classpath);
+ } catch (IllegalArgumentException e) {
+ throw new IOException(e);
}
- if (classpath != null) {
- classLoader = new ClasspathClassLoader(classpath, classLoader);
+ }
+
+ /**
+ * Prepend the specified classpath like string to the current ClassLoader of
+ * the configuration. If no ClassLoader is currently configured, the
+ * ClassLoader used to load the {@link PMDConfiguration} class will be used
+ * as the parent ClassLoader of the created ClassLoader.
+ *
+ * If the classpath String looks like a URL to a file (i.e. starts with
+ * file://
) the file will be read with each line representing
+ * an entry on the classpath.
+ *
+ * @param classpath The prepended classpath.
+ *
+ * @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist)
+ * @see PMDConfiguration#setClassLoader(ClassLoader)
+ */
+ public void prependAuxClasspath(String classpath) {
+ try {
+ if (classLoader == null) {
+ classLoader = PMDConfiguration.class.getClassLoader();
+ }
+ if (classpath != null) {
+ classLoader = new ClasspathClassLoader(classpath, classLoader);
+ }
+ } catch (IOException e) {
+ // Note: IOExceptions shouldn't appear anymore, they should already be converted
+ // to IllegalArgumentException in ClasspathClassLoader.
+ throw new IllegalArgumentException(e);
}
}
@@ -244,6 +283,7 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
public void setForceLanguageVersion(LanguageVersion forceLanguageVersion) {
this.forceLanguageVersion = forceLanguageVersion;
+ languageVersionDiscoverer.setForcedVersion(forceLanguageVersion);
}
/**
@@ -253,6 +293,7 @@ public class PMDConfiguration extends AbstractConfiguration {
* the LanguageVersion
*/
public void setDefaultLanguageVersion(LanguageVersion languageVersion) {
+ Objects.requireNonNull(languageVersion);
setDefaultLanguageVersions(Arrays.asList(languageVersion));
}
@@ -265,6 +306,7 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
public void setDefaultLanguageVersions(List languageVersions) {
for (LanguageVersion languageVersion : languageVersions) {
+ Objects.requireNonNull(languageVersion);
languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion);
}
}
@@ -310,7 +352,10 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
@Deprecated
@DeprecatedUntil700
- public String getRuleSets() {
+ public @Nullable String getRuleSets() {
+ if (ruleSets.isEmpty()) {
+ return null;
+ }
return String.join(",", ruleSets);
}
@@ -319,17 +364,34 @@ public class PMDConfiguration extends AbstractConfiguration {
*
* @see RuleSetLoader#loadFromResource(String)
*/
- public List getRuleSetPaths() {
+ public @NonNull List<@NonNull String> getRuleSetPaths() {
return ruleSets;
}
/**
- * Sets the rulesets.
+ * Sets the list of ruleset paths to load when starting the analysis.
+ *
+ * @param ruleSetPaths A list of ruleset paths, understandable by {@link RuleSetLoader#loadFromResource(String)}.
*
* @throws NullPointerException If the parameter is null
*/
- public void setRuleSets(@NonNull List ruleSets) {
- this.ruleSets = new ArrayList<>(ruleSets);
+ public void setRuleSets(@NonNull List<@NonNull String> ruleSetPaths) {
+ AssertionUtil.requireParamNotNull("ruleSetPaths", ruleSetPaths);
+ AssertionUtil.requireContainsNoNullValue("ruleSetPaths", ruleSetPaths);
+ this.ruleSets = new ArrayList<>(ruleSetPaths);
+ }
+
+ /**
+ * Add a new ruleset paths to load when starting the analysis.
+ * This list is initially empty.
+ *
+ * @param rulesetPath A ruleset path, understandable by {@link RuleSetLoader#loadFromResource(String)}.
+ *
+ * @throws NullPointerException If the parameter is null
+ */
+ public void addRuleSet(@NonNull String rulesetPath) {
+ AssertionUtil.requireParamNotNull("rulesetPath", rulesetPath);
+ this.ruleSets.add(rulesetPath);
}
/**
@@ -337,12 +399,16 @@ public class PMDConfiguration extends AbstractConfiguration {
*
* @param ruleSets the rulesets to set
*
- * @deprecated Use {@link #setRuleSets(List)}
+ * @deprecated Use {@link #setRuleSets(List)} or {@link #addRuleSet(String)}.
*/
@Deprecated
@DeprecatedUntil700
- public void setRuleSets(String ruleSets) {
- this.ruleSets = Arrays.asList(ruleSets.split(","));
+ public void setRuleSets(@Nullable String ruleSets) {
+ if (ruleSets == null) {
+ this.ruleSets = new ArrayList<>();
+ } else {
+ this.ruleSets = new ArrayList<>(Arrays.asList(ruleSets.split(",")));
+ }
}
/**
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java
new file mode 100644
index 0000000000..1b74fa5ab8
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java
@@ -0,0 +1,403 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd;
+
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.sourceforge.pmd.Report.GlobalReportBuilderListener;
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.benchmark.TimeTracker;
+import net.sourceforge.pmd.benchmark.TimedOperation;
+import net.sourceforge.pmd.benchmark.TimedOperationCategory;
+import net.sourceforge.pmd.cache.AnalysisCacheListener;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
+import net.sourceforge.pmd.internal.util.FileCollectionUtil;
+import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
+import net.sourceforge.pmd.lang.document.FileCollector;
+import net.sourceforge.pmd.processor.AbstractPMDProcessor;
+import net.sourceforge.pmd.renderers.Renderer;
+import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
+import net.sourceforge.pmd.util.ClasspathClassLoader;
+import net.sourceforge.pmd.util.IOUtil;
+import net.sourceforge.pmd.util.datasource.DataSource;
+import net.sourceforge.pmd.util.log.MessageReporter;
+import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter;
+
+/**
+ * Main programmatic API of PMD. Create and configure a {@link PMDConfiguration},
+ * then use {@link #create(PMDConfiguration)} to obtain an instance.
+ * You can perform additional configuration on the instance, eg adding
+ * files to process, or additional rulesets and renderers. Then, call
+ * {@link #performAnalysis()}. Example:
+ * {@code
+ * PMDConfiguration config = new PMDConfiguration();
+ * config.setDefaultLanguageVersion(LanguageRegistry.findLanguageByTerseName("java").getVersion("11"));
+ * config.setInputPaths("src/main/java");
+ * config.prependClasspath("target/classes");
+ * config.setMinimumPriority(RulePriority.HIGH);
+ * config.addRuleSet("rulesets/java/quickstart.xml");
+ * config.setReportFormat("xml");
+ * config.setReportFile("target/pmd-report.xml");
+ *
+ * try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ * // note: don't use `config` once a PmdAnalysis has been created.
+ * // optional: add more rulesets
+ * pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
+ * // optional: add more files
+ * pmd.files().addFile(Paths.get("src", "main", "more-java", "ExtraSource.java"));
+ * // optional: add more renderers
+ * pmd.addRenderer(renderer);
+ *
+ * pmd.performAnalysis();
+ * }
+ * }
+ *
+ */
+public final class PmdAnalysis implements AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PmdAnalysis.class);
+
+ private final FileCollector collector;
+ private final List renderers = new ArrayList<>();
+ private final List listeners = new ArrayList<>();
+ private final List ruleSets = new ArrayList<>();
+ private final PMDConfiguration configuration;
+ private final MessageReporter reporter;
+
+ private boolean closed;
+
+ /**
+ * Constructs a new instance. The files paths (input files, filelist,
+ * exclude list, etc) given in the configuration are collected into
+ * the file collector ({@link #files()}), but more can be added
+ * programmatically using the file collector.
+ */
+ private PmdAnalysis(PMDConfiguration config, MessageReporter reporter) {
+ this.configuration = config;
+ this.reporter = reporter;
+ this.collector = FileCollector.newCollector(
+ config.getLanguageVersionDiscoverer(),
+ reporter
+ );
+ }
+
+ /**
+ * Constructs a new instance from a configuration.
+ *
+ *
+ * - The files paths (input files, filelist,
+ * exclude list, etc) are explored and the files to analyse are
+ * collected into the file collector ({@link #files()}).
+ * More can be added programmatically using the file collector.
+ *
- The rulesets given in the configuration are loaded ({@link PMDConfiguration#getRuleSets()})
+ *
- A renderer corresponding to the parameters of the configuration
+ * is created and added (but not started).
+ *
+ */
+ public static PmdAnalysis create(PMDConfiguration config) {
+ return create(
+ config,
+ new SimpleMessageReporter(LoggerFactory.getLogger(PmdAnalysis.class))
+ );
+ }
+
+ @InternalApi
+ static PmdAnalysis create(PMDConfiguration config, MessageReporter reporter) {
+ PmdAnalysis pmd = new PmdAnalysis(config, reporter);
+
+ // note: do not filter files by language
+ // they could be ignored later. The problem is if you call
+ // addRuleSet later, then you could be enabling new languages
+ // So the files should not be pruned in advance
+ FileCollectionUtil.collectFiles(config, pmd.files());
+
+ if (config.getReportFormat() != null) {
+ Renderer renderer = config.createRenderer(true);
+ pmd.addRenderer(renderer);
+ }
+
+ if (!config.getRuleSetPaths().isEmpty()) {
+ final RuleSetLoader ruleSetLoader = pmd.newRuleSetLoader();
+ final List ruleSets = ruleSetLoader.loadRuleSetsWithoutException(config.getRuleSetPaths());
+ pmd.addRuleSets(ruleSets);
+ }
+ return pmd;
+ }
+
+ // test only
+ List rulesets() {
+ return ruleSets;
+ }
+
+ // test only
+ List renderers() {
+ return renderers;
+ }
+
+
+ /**
+ * Returns the file collector for the analysed sources.
+ */
+ public FileCollector files() {
+ return collector; // todo user can close collector programmatically
+ }
+
+ /**
+ * Returns a new ruleset loader, which can be used to create new
+ * rulesets (add them then with {@link #addRuleSet(RuleSet)}).
+ *
+ * {@code
+ * try (PmdAnalysis pmd = create(config)) {
+ * pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
+ * }
+ * }
+ */
+ public RuleSetLoader newRuleSetLoader() {
+ RuleSetLoader loader = RuleSetLoader.fromPmdConfig(configuration);
+ loader.setReporter(this.reporter);
+ return loader;
+ }
+
+ /**
+ * Add a new renderer. The given renderer must not already be started,
+ * it will be started by {@link #performAnalysis()}.
+ *
+ * @throws NullPointerException If the parameter is null
+ */
+ public void addRenderer(Renderer renderer) {
+ AssertionUtil.requireParamNotNull("renderer", renderer);
+ this.renderers.add(renderer);
+ }
+
+ /**
+ * Add several renderers at once.
+ *
+ * @throws NullPointerException If the parameter is null, or any of its items is null.
+ */
+ public void addRenderers(Collection renderers) {
+ renderers.forEach(this::addRenderer);
+ }
+
+ /**
+ * Add a new listener. As per the contract of {@link GlobalAnalysisListener},
+ * this object must be ready for interaction. However, nothing will
+ * be done with the listener until {@link #performAnalysis()} is called.
+ * The listener will be closed by {@link #performAnalysis()}, or
+ * {@link #close()}, whichever happens first.
+ *
+ * @throws NullPointerException If the parameter is null
+ */
+ public void addListener(GlobalAnalysisListener listener) {
+ AssertionUtil.requireParamNotNull("listener", listener);
+ this.listeners.add(listener);
+ }
+
+ /**
+ * Add several listeners at once.
+ *
+ * @throws NullPointerException If the parameter is null, or any of its items is null.
+ * @see #addListener(GlobalAnalysisListener)
+ */
+ public void addListeners(Collection extends GlobalAnalysisListener> listeners) {
+ listeners.forEach(this::addListener);
+ }
+
+ /**
+ * Add a new ruleset.
+ *
+ * @throws NullPointerException If the parameter is null
+ */
+ public void addRuleSet(RuleSet ruleSet) {
+ AssertionUtil.requireParamNotNull("rule set", ruleSet);
+ this.ruleSets.add(ruleSet);
+ }
+
+ /**
+ * Add several rulesets at once.
+ *
+ * @throws NullPointerException If the parameter is null, or any of its items is null.
+ */
+ public void addRuleSets(Collection ruleSets) {
+ ruleSets.forEach(this::addRuleSet);
+ }
+
+ /**
+ * Returns an unmodifiable view of the ruleset list. That will be
+ * processed.
+ */
+ public List getRulesets() {
+ return Collections.unmodifiableList(ruleSets);
+ }
+
+
+ /**
+ * Run PMD with the current state of this instance. This will start
+ * and finish the registered renderers, and close all
+ * {@linkplain #addListener(GlobalAnalysisListener) registered listeners}.
+ * All files collected in the {@linkplain #files() file collector} are
+ * processed. This does not return a report, as the analysis results
+ * are consumed by {@link GlobalAnalysisListener} instances (of which
+ * Renderers are a special case). Note that this does
+ * not throw, errors are instead accumulated into a {@link MessageReporter}.
+ */
+ public void performAnalysis() {
+ performAnalysisImpl(Collections.emptyList());
+ }
+
+ /**
+ * Run PMD with the current state of this instance. This will start
+ * and finish the registered renderers. All files collected in the
+ * {@linkplain #files() file collector} are processed. Returns the
+ * output report. Note that this does not throw, errors are instead
+ * accumulated into a {@link MessageReporter}.
+ */
+ public Report performAnalysisAndCollectReport() {
+ try (GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener()) {
+ performAnalysisImpl(listOf(reportBuilder)); // closes the report builder
+ return reportBuilder.getResultImpl();
+ }
+ }
+
+ void performAnalysisImpl(List extends GlobalReportBuilderListener> extraListeners) {
+ try (FileCollector files = collector) {
+ files.filterLanguages(getApplicableLanguages());
+ List dataSources = FileCollectionUtil.collectorToDataSource(files);
+ performAnalysisImpl(extraListeners, dataSources);
+ }
+ }
+
+ void performAnalysisImpl(List extends GlobalReportBuilderListener> extraListeners, List dataSources) {
+ RuleSets rulesets = new RuleSets(this.ruleSets);
+
+ GlobalAnalysisListener listener;
+ try {
+ @SuppressWarnings("PMD.CloseResource") AnalysisCacheListener cacheListener = new AnalysisCacheListener(configuration.getAnalysisCache(), rulesets, configuration.getClassLoader());
+ listener = GlobalAnalysisListener.tee(listOf(createComposedRendererListener(renderers), GlobalAnalysisListener.tee(listeners), GlobalAnalysisListener.tee(extraListeners), cacheListener));
+ } catch (Exception e) {
+ reporter.errorEx("Exception while initializing analysis listeners", e);
+ throw new RuntimeException("Exception while initializing analysis listeners", e);
+ }
+
+ try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) {
+
+ for (final Rule rule : removeBrokenRules(rulesets)) {
+ // todo Just like we throw for invalid properties, "broken rules"
+ // shouldn't be a "config error". This is the only instance of
+ // config errors...
+ listener.onConfigError(new Report.ConfigurationError(rule, rule.dysfunctionReason()));
+ }
+
+ PMD.encourageToUseIncrementalAnalysis(configuration);
+ try (AbstractPMDProcessor processor = AbstractPMDProcessor.newFileProcessor(configuration)) {
+ processor.processFiles(rulesets, dataSources, listener);
+ }
+ } finally {
+ try {
+ listener.close();
+ } catch (Exception e) {
+ reporter.errorEx("Exception while initializing analysis listeners", e);
+ // todo better exception
+ throw new RuntimeException("Exception while initializing analysis listeners", e);
+ }
+ }
+ }
+
+
+ private static GlobalAnalysisListener createComposedRendererListener(List renderers) throws Exception {
+ if (renderers.isEmpty()) {
+ return GlobalAnalysisListener.noop();
+ }
+
+ List rendererListeners = new ArrayList<>(renderers.size());
+ for (Renderer renderer : renderers) {
+ try {
+ @SuppressWarnings("PMD.CloseResource")
+ GlobalAnalysisListener listener =
+ Objects.requireNonNull(renderer.newListener(), "Renderer should provide non-null listener");
+ rendererListeners.add(listener);
+ } catch (Exception ioe) {
+ // close listeners so far, throw their close exception or the ioe
+ IOUtil.ensureClosed(rendererListeners, ioe);
+ throw AssertionUtil.shouldNotReachHere("ensureClosed should have thrown");
+ }
+ }
+ return GlobalAnalysisListener.tee(rendererListeners);
+ }
+
+ private Set getApplicableLanguages() {
+ final Set languages = new HashSet<>();
+ final LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();
+
+ for (RuleSet ruleSet : ruleSets) {
+ for (final Rule rule : ruleSet.getRules()) {
+ final Language ruleLanguage = rule.getLanguage();
+ if (!languages.contains(ruleLanguage)) {
+ final LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage);
+ if (RuleSet.applies(rule, version)) {
+ languages.add(ruleLanguage);
+ LOG.trace("Using {} version ''{}''", version.getLanguage().getName(), version.getTerseName());
+ }
+ }
+ }
+ }
+ return languages;
+ }
+
+ /**
+ * Remove and return the misconfigured rules from the rulesets and log them
+ * for good measure.
+ */
+ private Set removeBrokenRules(final RuleSets ruleSets) {
+ final Set brokenRules = new HashSet<>();
+ ruleSets.removeDysfunctionalRules(brokenRules);
+
+ for (final Rule rule : brokenRules) {
+ reporter.warn("Removed misconfigured rule: {} cause: {}",
+ rule.getName(), rule.dysfunctionReason());
+ }
+
+ return brokenRules;
+ }
+
+
+ public MessageReporter getReporter() {
+ return reporter;
+ }
+
+ @Override
+ public void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ collector.close();
+
+ // close listeners if analysis is not run.
+ IOUtil.closeAll(listeners);
+
+ /*
+ * Make sure it's our own classloader before attempting to close it....
+ * Maven + Jacoco provide us with a cloaseable classloader that if closed
+ * will throw a ClassNotFoundException.
+ */
+ if (configuration.getClassLoader() instanceof ClasspathClassLoader) {
+ IOUtil.tryCloseClassLoader(configuration.getClassLoader());
+ }
+ }
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java
index 9513f5901e..78f4861cc4 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java
@@ -28,7 +28,7 @@ import net.sourceforge.pmd.util.datasource.DataSource;
* and configuration errors.
*
* A report may be created by a {@link GlobalReportBuilderListener} that you
- * use as the {@link GlobalAnalysisListener} in {@linkplain PMD#processFiles(PMDConfiguration, List, List, GlobalAnalysisListener) PMD's entry point}.
+ * use as the {@linkplain GlobalAnalysisListener} in {@link PmdAnalysis#performAnalysisAndCollectReport() PMD's entry point}.
* You can also create one manually with {@link #buildReport(Consumer)}.
*/
public final class Report {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
index 414fc8d2b3..e37c849670 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
@@ -6,10 +6,8 @@ package net.sourceforge.pmd;
import java.util.List;
-import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
import net.sourceforge.pmd.properties.PropertySource;
@@ -251,31 +249,6 @@ public interface Rule extends PropertySource {
void setPriority(RulePriority priority);
- /**
- * Returns true if this rule depends on the given processing stage
- * to run. If so, any ruleset including this rule, in which the rule
- * is not misconfigured, will execute the analysis reified in the
- * given stage before applying rules on the AST.
- *
- *
The default returns false. Each language should implement this
- * method in its abstract rule base class, and probably mark its
- * implementation as final for consistency within the language
- * implementation. AST processing stages are language-specific, and
- * any non-trivial implementation should throw an {@link IllegalArgumentException}
- * when given a stage that isn't defined on the language of the rule.
- *
- * @param stage Processing stage for which to check for a dependency.
- *
- * @return True if this rule depends on the given processing stage.
- *
- * @since 7.0.0
- */
- @Experimental
- default boolean dependsOn(AstProcessingStage> stage) {
- return true;
- }
-
-
/**
* Returns the object that selects the nodes to which this rule applies.
* The selected nodes will be handed to {@link #apply(Node, RuleContext)}.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java
index e177ee4eb6..1a75e5d3a5 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java
@@ -16,11 +16,16 @@ import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sourceforge.pmd.util.ResourceLoader;
+import net.sourceforge.pmd.util.log.MessageReporter;
+import net.sourceforge.pmd.util.log.internal.NoopReporter;
/**
* Configurable object to load rulesets from XML resources.
@@ -29,12 +34,26 @@ import net.sourceforge.pmd.util.ResourceLoader;
* or some such overload.
*/
public final class RuleSetLoader {
+ private static final Logger LOG = LoggerFactory.getLogger(RuleSetLoader.class);
private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader());
private RulePriority minimumPriority = RulePriority.LOW;
private boolean warnDeprecated = true;
private @NonNull RuleSetFactoryCompatibility compatFilter = RuleSetFactoryCompatibility.DEFAULT;
private boolean includeDeprecatedRuleReferences = false;
+ private MessageReporter reporter = new NoopReporter(); // non-null
+
+ /**
+ * Create a new RuleSetLoader with a default configuration.
+ * The defaults are described on each configuration method of this class.
+ */
+ public RuleSetLoader() { // NOPMD UnnecessaryConstructor
+ // default
+ }
+
+ void setReporter(MessageReporter reporter) {
+ this.reporter = reporter;
+ }
/**
* Specify that the given classloader should be used to resolve
@@ -145,7 +164,7 @@ public final class RuleSetLoader {
*
* @throws RuleSetLoadException If any error occurs (eg, invalid syntax)
*/
- public RuleSet loadFromString(String filename, String rulesetXmlContent) {
+ public RuleSet loadFromString(String filename, final String rulesetXmlContent) {
return loadFromResource(new RuleSetReferenceId(filename) {
@Override
public InputStream getInputStream(ResourceLoader rl) {
@@ -171,6 +190,52 @@ public final class RuleSetLoader {
return ruleSets;
}
+ /**
+ * Loads a list of rulesets, if any has an error, report it on the contextual
+ * error reporter instead of aborting, and continue loading the rest.
+ *
+ *
Internal API: might be published later, or maybe in PMD 7 this
+ * will be the default behaviour of every method of this class.
+ */
+ @InternalApi
+ public List loadRuleSetsWithoutException(List rulesetPaths) {
+ List ruleSets = new ArrayList<>(rulesetPaths.size());
+ boolean anyRules = false;
+ for (String path : rulesetPaths) {
+ try {
+ RuleSet ruleset = this.loadFromResource(path);
+ anyRules |= !ruleset.getRules().isEmpty();
+ printRulesInDebug(path, ruleset);
+ ruleSets.add(ruleset);
+ } catch (RuleSetLoadException e) {
+ if (e.getCause() != null) {
+ // eg RuleSetNotFoundException
+ reporter.errorEx("Cannot load ruleset {0}", new Object[] { path }, e.getCause());
+ } else {
+ reporter.errorEx("Cannot load ruleset {0}", new Object[] { path }, e);
+ }
+ }
+ }
+ if (!anyRules) {
+ reporter.warn("No rules found. Maybe you misspelled a rule name? ({})",
+ StringUtils.join(rulesetPaths, ','));
+ }
+ return ruleSets;
+ }
+
+ void printRulesInDebug(String path, RuleSet ruleset) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Rules loaded from {}:", path);
+ for (Rule rule : ruleset.getRules()) {
+ LOG.debug("- {} ({})", rule.getName(), rule.getLanguage().getName());
+ }
+ }
+ if (ruleset.getRules().isEmpty()) {
+ reporter.warn("No rules found in ruleset {}", path);
+ }
+
+ }
+
/**
* Parses several resources into a list of rulesets.
*
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java
index 0f3f8f3114..4117010f49 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java
@@ -4,10 +4,9 @@
package net.sourceforge.pmd.ant.internal;
-import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
@@ -22,12 +21,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
-import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
-import net.sourceforge.pmd.Rule;
+import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.RulePriority;
-import net.sourceforge.pmd.RuleSet;
-import net.sourceforge.pmd.RuleSetLoadException;
import net.sourceforge.pmd.RuleSetLoader;
import net.sourceforge.pmd.ant.Formatter;
import net.sourceforge.pmd.ant.PMDTask;
@@ -38,11 +34,11 @@ import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
-import net.sourceforge.pmd.reporting.GlobalAnalysisListener.ViolationCounterListener;
+import net.sourceforge.pmd.reporting.ReportStats;
+import net.sourceforge.pmd.reporting.ReportStatsListener;
import net.sourceforge.pmd.util.ClasspathClassLoader;
import net.sourceforge.pmd.util.IOUtil;
import net.sourceforge.pmd.util.datasource.DataSource;
-import net.sourceforge.pmd.util.datasource.FileDataSource;
public class PMDTaskImpl {
@@ -51,7 +47,7 @@ public class PMDTaskImpl {
private final List formatters = new ArrayList<>();
private final List filesets = new ArrayList<>();
private final PMDConfiguration configuration = new PMDConfiguration();
- private final String rulesetPaths;
+ private boolean failOnError;
private boolean failOnRuleViolation;
private int maxRuleViolations = 0;
private String failuresPropertyName;
@@ -62,12 +58,16 @@ public class PMDTaskImpl {
if (task.getSuppressMarker() != null) {
configuration.setSuppressMarker(task.getSuppressMarker());
}
+ this.failOnError = task.isFailOnError();
this.failOnRuleViolation = task.isFailOnRuleViolation();
this.maxRuleViolations = task.getMaxRuleViolations();
if (this.maxRuleViolations > 0) {
this.failOnRuleViolation = true;
}
- this.rulesetPaths = task.getRulesetFiles() == null ? "" : task.getRulesetFiles();
+ if (task.getRulesetFiles() != null) {
+ configuration.setRuleSets(Arrays.asList(task.getRulesetFiles().split(",")));
+ }
+
configuration.setRuleSetFactoryCompatibilityEnabled(!task.isNoRuleSetCompatibility());
if (task.getEncoding() != null) {
configuration.setSourceEncoding(task.getEncoding());
@@ -100,46 +100,49 @@ public class PMDTaskImpl {
private void doTask() {
setupClassLoader();
- // Setup RuleSetFactory and validate RuleSets
- RuleSetLoader rulesetLoader = RuleSetLoader.fromPmdConfig(configuration)
- .loadResourcesWith(setupResourceLoader());
-
- List rules = loadRulesets(rulesetLoader);
-
if (configuration.getSuppressMarker() != null) {
project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE);
}
- @SuppressWarnings("PMD.CloseResource")
- ViolationCounterListener reportSizeListener = new ViolationCounterListener();
-
- final List files = new ArrayList<>();
- final List reportShortNamesPaths = new ArrayList<>();
+ @SuppressWarnings("PMD.CloseResource") final List reportShortNamesPaths = new ArrayList<>();
StringJoiner fullInputPath = new StringJoiner(",");
- for (FileSet fs : filesets) {
- DirectoryScanner ds = fs.getDirectoryScanner(project);
- for (String srcFile : ds.getIncludedFiles()) {
- File file = new File(ds.getBasedir() + File.separator + srcFile);
- files.add(new FileDataSource(file));
+ List ruleSetPaths = expandRuleSetPaths(configuration.getRuleSetPaths());
+ // don't let PmdAnalysis.create create rulesets itself.
+ configuration.setRuleSets(Collections.emptyList());
+
+ ReportStats stats;
+ try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
+ RuleSetLoader rulesetLoader =
+ pmd.newRuleSetLoader().loadResourcesWith(setupResourceLoader());
+ pmd.addRuleSets(rulesetLoader.loadRuleSetsWithoutException(ruleSetPaths));
+
+ for (FileSet fileset : filesets) {
+ DirectoryScanner ds = fileset.getDirectoryScanner(project);
+ for (String srcFile : ds.getIncludedFiles()) {
+ pmd.files().addFile(ds.getBasedir().toPath().resolve(srcFile));
+ }
+
+ final String commonInputPath = ds.getBasedir().getPath();
+ fullInputPath.add(commonInputPath);
+ if (configuration.isReportShortNames()) {
+ reportShortNamesPaths.add(commonInputPath);
+ }
}
- final String commonInputPath = ds.getBasedir().getPath();
- fullInputPath.add(commonInputPath);
- if (configuration.isReportShortNames()) {
- reportShortNamesPaths.add(commonInputPath);
+ @SuppressWarnings("PMD.CloseResource")
+ ReportStatsListener reportStatsListener = new ReportStatsListener();
+ pmd.addListener(getListener(reportStatsListener, reportShortNamesPaths, fullInputPath.toString()));
+
+ pmd.performAnalysis();
+ stats = reportStatsListener.getResult();
+ if (failOnError && pmd.getReporter().numErrors() > 0) {
+ throw new BuildException("Some errors occurred while running PMD");
}
}
- configuration.setInputPaths(fullInputPath.toString());
- try (GlobalAnalysisListener listener = getListener(reportSizeListener, reportShortNamesPaths)) {
- PMD.processFiles(configuration, rules, files, listener);
- } catch (Exception e) {
- throw new BuildException("Exception while closing data sources", e);
- }
-
- int problemCount = reportSizeListener.getResult();
+ int problemCount = stats.getNumViolations();
project.log(problemCount + " problems found", Project.MSG_VERBOSE);
if (failuresPropertyName != null && problemCount > 0) {
@@ -152,34 +155,27 @@ public class PMDTaskImpl {
}
}
- private List loadRulesets(RuleSetLoader rulesetLoader) {
- try {
- // This is just used to validate and display rules. Each thread will create its own ruleset
- // Substitute env variables/properties
- String ruleSetString = project.replaceProperties(rulesetPaths);
-
- List rulesets = Arrays.asList(ruleSetString.split(","));
- List rulesetList = rulesetLoader.loadFromResources(rulesets);
- if (rulesetList.isEmpty()) {
- throw new BuildException("No rulesets");
- }
- logRulesUsed(rulesetList);
- return rulesetList;
- } catch (RuleSetLoadException e) {
- throw new BuildException(e.getMessage(), e);
+ private List expandRuleSetPaths(List ruleSetPaths) {
+ List paths = new ArrayList<>(ruleSetPaths);
+ for (int i = 0; i < paths.size(); i++) {
+ paths.set(i, project.replaceProperties(paths.get(i)));
}
+ return paths;
}
- private @NonNull GlobalAnalysisListener getListener(ViolationCounterListener reportSizeListener, List reportShortNamesPaths) {
+ private @NonNull GlobalAnalysisListener getListener(ReportStatsListener reportSizeListener,
+ List reportShortNamesPaths,
+ String inputPaths) {
List renderers = new ArrayList<>(formatters.size() + 1);
try {
- renderers.add(makeLogListener(configuration.getInputPaths()));
+ renderers.add(makeLogListener(inputPaths));
renderers.add(reportSizeListener);
for (Formatter formatter : formatters) {
project.log("Sending a report to " + formatter, Project.MSG_VERBOSE);
renderers.add(formatter.newListener(project, reportShortNamesPaths));
}
- } catch (IOException e) {
+ return GlobalAnalysisListener.tee(renderers);
+ } catch (Exception e) {
// close those opened so far
Exception e2 = IOUtil.closeAll(renderers);
if (e2 != null) {
@@ -187,8 +183,6 @@ public class PMDTaskImpl {
}
throw new BuildException("Exception while initializing renderers", e);
}
-
- return GlobalAnalysisListener.tee(renderers);
}
private GlobalAnalysisListener makeLogListener(String commonInputPath) {
@@ -233,9 +227,9 @@ public class PMDTaskImpl {
try {
if (auxClasspath != null) {
project.log("Using auxclasspath: " + auxClasspath, Project.MSG_VERBOSE);
- configuration.prependClasspath(auxClasspath.toString());
+ configuration.prependAuxClasspath(auxClasspath.toString());
}
- } catch (IOException ioe) {
+ } catch (IllegalArgumentException ioe) {
throw new BuildException(ioe.getMessage(), ioe);
}
}
@@ -257,13 +251,4 @@ public class PMDTaskImpl {
}
}
- private void logRulesUsed(List rulesets) {
- project.log("Using these rulesets: " + rulesetPaths, Project.MSG_VERBOSE);
-
- for (RuleSet ruleSet : rulesets) {
- for (Rule rule : ruleSet.getRules()) {
- project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
- }
- }
- }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/TimeTracker.java b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/TimeTracker.java
index 2eb42776bb..c008ec4ff5 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/TimeTracker.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/TimeTracker.java
@@ -12,6 +12,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
/**
* A time tracker class to measure time spent on different sections of PMD analysis.
@@ -157,6 +158,18 @@ public final class TimeTracker {
}
}
+ public static void bench(String label, Runnable runnable) {
+ try (TimedOperation ignored = startOperation(TimedOperationCategory.LANGUAGE_SPECIFIC_PROCESSING, label)) {
+ runnable.run();
+ }
+ }
+
+ public static T bench(String label, Supplier runnable) {
+ try (TimedOperation ignored = startOperation(TimedOperationCategory.LANGUAGE_SPECIFIC_PROCESSING, label)) {
+ return runnable.get();
+ }
+ }
+
/**
* An entry in the open timers queue. Defines an operation that has started and hasn't finished yet.
*/
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java
index 9089eb05bd..624515579c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.PMDVersion;
+import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.annotation.InternalApi;
@@ -210,13 +211,26 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
}
@Override
- public FileAnalysisListener startFileAnalysis(DataSource filename) {
- return violation -> {
- final AnalysisResult analysisResult =
- updatedResultsCache.get(violation.getFilename());
+ public FileAnalysisListener startFileAnalysis(DataSource dataSource) {
+ String fileName = dataSource.getNiceFileName(false, "");
+ File sourceFile = new File(fileName);
+ AnalysisResult analysisResult = updatedResultsCache.get(fileName);
+ if (analysisResult == null) {
+ analysisResult = new AnalysisResult(sourceFile);
+ }
+ final AnalysisResult nonNullAnalysisResult = analysisResult;
- synchronized (analysisResult) {
- analysisResult.addViolation(violation);
+ return new FileAnalysisListener() {
+ @Override
+ public void onRuleViolation(RuleViolation violation) {
+ synchronized (nonNullAnalysisResult) {
+ nonNullAnalysisResult.addViolation(violation);
+ }
+ }
+
+ @Override
+ public void onError(ProcessingError error) {
+ analysisFailed(sourceFile);
}
};
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java
index ea1f6f5273..f16490263a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java
@@ -5,6 +5,7 @@
package net.sourceforge.pmd.cache;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import net.sourceforge.pmd.RuleSets;
@@ -12,6 +13,7 @@ import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
+import net.sourceforge.pmd.util.datasource.DataSource;
/**
* An analysis cache for incremental analysis.
@@ -22,12 +24,12 @@ import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
*/
@Deprecated
@InternalApi
-public interface AnalysisCache extends GlobalAnalysisListener {
+public interface AnalysisCache {
/**
* Persists the updated analysis results on whatever medium is used by the cache.
*/
- void persist();
+ void persist() throws IOException;
/**
* Checks if a given file is up to date in the cache and can be skipped from analysis.
@@ -59,8 +61,16 @@ public interface AnalysisCache extends GlobalAnalysisListener {
* cache is invalidated. This needs to be called before analysis, as it
* conditions the good behaviour of {@link #isUpToDate(File)}.
*
- * @param ruleSets The rulesets configured for this analysis.
+ * @param ruleSets The rulesets configured for this analysis.
* @param auxclassPathClassLoader The class loader for auxclasspath configured for this analysis.
*/
void checkValidity(RuleSets ruleSets, ClassLoader auxclassPathClassLoader);
+
+ /**
+ * Returns a listener that will be used like in {@link GlobalAnalysisListener#startFileAnalysis(DataSource)}.
+ * This should record violations, and call {@link #analysisFailed(File)}
+ * upon error.
+ */
+ FileAnalysisListener startFileAnalysis(DataSource file);
+
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCacheListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCacheListener.java
new file mode 100644
index 0000000000..f6c4d12558
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCacheListener.java
@@ -0,0 +1,40 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cache;
+
+import java.io.IOException;
+
+import net.sourceforge.pmd.RuleSets;
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.reporting.FileAnalysisListener;
+import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
+import net.sourceforge.pmd.util.datasource.DataSource;
+
+/**
+ * Adapter to wrap {@link AnalysisCache} behaviour in a {@link GlobalAnalysisListener}.
+ */
+@Deprecated
+@InternalApi
+public class AnalysisCacheListener implements GlobalAnalysisListener {
+
+ private final AnalysisCache cache;
+
+ public AnalysisCacheListener(AnalysisCache cache, RuleSets ruleSets, ClassLoader classLoader) {
+ this.cache = cache;
+ cache.checkValidity(ruleSets, classLoader);
+ }
+
+
+ @Override
+ public FileAnalysisListener startFileAnalysis(DataSource file) {
+ return cache.startFileAnalysis(file);
+ }
+
+ @Override
+ public void close() throws IOException {
+ cache.persist();
+ }
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java
index 5c1f29f4db..d2739cfc92 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java
@@ -151,11 +151,6 @@ public class FileAnalysisCache extends AbstractAnalysisCache {
}
}
- @Override
- public void close() throws Exception {
- // nothing to do, PMD calls persist explicitly
- }
-
@Override
protected boolean cacheExists() {
return cacheFile.exists() && cacheFile.isFile() && cacheFile.length() > 0;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/NoopAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/NoopAnalysisCache.java
index 56127ea4a7..3a3dee4470 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/NoopAnalysisCache.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/NoopAnalysisCache.java
@@ -53,8 +53,4 @@ public class NoopAnalysisCache implements AnalysisCache {
return FileAnalysisListener.noop();
}
- @Override
- public void close() throws Exception {
- // noop
- }
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java
index 7419d11afd..5e90676c06 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java
@@ -8,6 +8,7 @@ import java.util.Properties;
import java.util.stream.Collectors;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.PMD.StatusCode;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
@@ -28,13 +29,39 @@ import com.beust.jcommander.ParameterException;
@InternalApi
public final class PMDCommandLineInterface {
+ @Deprecated
public static final String PROG_NAME = "pmd";
+ /**
+ * @deprecated This is used for testing, but support for it will be removed in PMD 7.
+ * Use {@link PMD#runPmd(String...)} or an overload to avoid exiting the VM. In PMD 7,
+ * {@link PMD#main(String[])} will call {@link System#exit(int)} always.
+ */
+ @Deprecated
public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit";
+
+ /**
+ * @deprecated This is used for testing, but support for it will be removed in PMD 7.
+ * Use {@link PMD#runPmd(String...)} or an overload to avoid exiting the VM. In PMD 7,
+ * {@link PMD#main(String[])} will call {@link System#exit(int)} always.
+ */
+ @Deprecated
public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status";
+ /**
+ * @deprecated Use {@link StatusCode#OK}
+ */
+ @Deprecated
public static final int NO_ERRORS_STATUS = 0;
+ /**
+ * @deprecated Use {@link StatusCode#ERROR}
+ */
+ @Deprecated
public static final int ERROR_STATUS = 1;
+ /**
+ * @deprecated Use {@link StatusCode#VIOLATIONS_FOUND}
+ */
+ @Deprecated
public static final int VIOLATIONS_FOUND = 4;
private PMDCommandLineInterface() { }
@@ -126,7 +153,10 @@ public final class PMDCommandLineInterface {
* For testing purpose only...
*
* @param args
+ *
+ * @deprecated Use {@link PMD#runPmd(String...)}
*/
+ @Deprecated
public static void main(String[] args) {
System.out.println(PMDCommandLineInterface.buildUsageText());
}
@@ -160,14 +190,6 @@ public final class PMDCommandLineInterface {
return buf.toString();
}
- /**
- * @deprecated Use {@link PMD#main(String[])}
- */
- @Deprecated
- public static void run(String[] args) {
- setStatusCodeOrExit(PMD.run(args));
- }
-
public static void setStatusCodeOrExit(int status) {
if (isExitAfterRunSet()) {
System.exit(status);
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java
index 892093af24..7cea583a26 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java
@@ -4,7 +4,6 @@
package net.sourceforge.pmd.cli;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -235,8 +234,8 @@ public class PMDParameters {
}
try {
- configuration.prependClasspath(this.getAuxclasspath());
- } catch (IOException e) {
+ configuration.prependAuxClasspath(this.getAuxclasspath());
+ } catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid auxiliary classpath: " + e.getMessage(), e);
}
return configuration;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/RulesetStageDependencyHelper.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/RulesetStageDependencyHelper.java
deleted file mode 100644
index cd02f2e6d1..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/RulesetStageDependencyHelper.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.internal;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
-
-import net.sourceforge.pmd.PMDConfiguration;
-import net.sourceforge.pmd.Rule;
-import net.sourceforge.pmd.RuleSets;
-import net.sourceforge.pmd.benchmark.TimeTracker;
-import net.sourceforge.pmd.benchmark.TimedOperation;
-import net.sourceforge.pmd.benchmark.TimedOperationCategory;
-import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.ast.AstAnalysisContext;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
-import net.sourceforge.pmd.lang.ast.RootNode;
-
-/**
- *
- * @author ClΓ©ment Fournier
- */
-public class RulesetStageDependencyHelper {
-
- // cache by ruleset
- private final Map>>> dependenciesByRuleset = new ConcurrentHashMap<>();
- private final PMDConfiguration configuration;
-
- public RulesetStageDependencyHelper(PMDConfiguration configuration) {
- this.configuration = configuration;
- }
-
-
- List> testOnlyGetDependencies(RuleSets ruleSets, LanguageVersion languageVersion) {
- return getDependencies(ruleSets, languageVersion);
- }
-
- /** Gets the stage dependencies of the ruleset for the given language version. */
- private List> getDependencies(RuleSets ruleSets, LanguageVersion languageVersion) {
- Map>> byLanguage = dependenciesByRuleset.computeIfAbsent(ruleSets, r -> new ConcurrentHashMap<>());
-
- return byLanguage.computeIfAbsent(languageVersion, l -> buildDependencyList(ruleSets, l));
- }
-
-
- public void runLanguageSpecificStages(RuleSets ruleSets, LanguageVersion languageVersion, RootNode rootNode) {
- AstAnalysisContext context = buildContext(languageVersion);
-
- getDependencies(ruleSets, languageVersion)
- .forEach(stage -> executeProcessingStage(stage, rootNode, context));
-
- }
-
- private void executeProcessingStage(AstProcessingStage> stage, RootNode root, AstAnalysisContext context) {
-
- String label = stage.getLanguage().getShortName() + ": " + stage.getDisplayName();
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.LANGUAGE_SPECIFIC_PROCESSING, label)) {
- stage.processAST(root, context);
- }
- }
-
- /** Builds a sorted list of the dependencies of the given ruleset. */
- private List> buildDependencyList(RuleSets ruleSets, LanguageVersion languageVersion) {
- List> stages = new ArrayList<>(languageVersion.getLanguageVersionHandler().getProcessingStages());
- SortedSet> result = new TreeSet<>(AstProcessingStage.COMPARATOR);
-
- // this loops runs until either all stages have already been
- // picked or there are no rules left, whichever comes first
- for (Rule rule : ruleSets.getAllRules()) {
- if (stages.isEmpty()) {
- break; // to the return
- }
- for (AstProcessingStage> stage : stages) {
- if (rule.dependsOn(stage)) {
- result.add(stage);
- result.addAll(stage.getDependencies());
- }
- }
- stages.removeAll(result);
- }
-
- return Collections.unmodifiableList(new ArrayList<>(result));
- }
-
-
- // TODO this could be made language specific as well
- private AstAnalysisContext buildContext(LanguageVersion languageVersion) {
- return new AstAnalysisContext() {
- @Override
- public ClassLoader getTypeResolutionClassLoader() {
- return configuration.getClassLoader();
- }
-
-
- @Override
- public LanguageVersion getLanguageVersion() {
- return languageVersion;
- }
- };
- }
-
-}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java
index b1034a6252..fca762b178 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java
@@ -25,10 +25,12 @@ public final class AssertionUtil {
/** @throws NullPointerException if $name */
public static void requireContainsNoNullValue(String name, Collection> c) {
+ int i = 0;
for (Object o : c) {
if (o == null) {
- throw new NullPointerException(name + " contains null elements");
+ throw new NullPointerException(name + " contains a null element at index " + i);
}
+ i++;
}
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java
new file mode 100644
index 0000000000..697d16f927
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java
@@ -0,0 +1,183 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.internal.util;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.sourceforge.pmd.PMDConfiguration;
+import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.document.FileCollector;
+import net.sourceforge.pmd.lang.document.TextFile;
+import net.sourceforge.pmd.util.FileUtil;
+import net.sourceforge.pmd.util.database.DBMSMetadata;
+import net.sourceforge.pmd.util.database.DBURI;
+import net.sourceforge.pmd.util.database.SourceObject;
+import net.sourceforge.pmd.util.datasource.DataSource;
+import net.sourceforge.pmd.util.log.MessageReporter;
+import net.sourceforge.pmd.util.log.internal.ErrorsAsWarningsReporter;
+
+/**
+ * @author ClΓ©ment Fournier
+ */
+public final class FileCollectionUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FileCollectionUtil.class);
+
+ private FileCollectionUtil() {
+
+ }
+
+ public static List collectorToDataSource(FileCollector collector) {
+ List result = new ArrayList<>();
+ for (TextFile file : collector.getCollectedFiles()) {
+ result.add(file.toDataSourceCompat());
+ }
+ return result;
+ }
+
+ public static FileCollector collectFiles(PMDConfiguration configuration, Set languages, MessageReporter reporter) {
+ FileCollector collector = collectFiles(configuration, reporter);
+ collector.filterLanguages(languages);
+ return collector;
+ }
+
+ private static FileCollector collectFiles(PMDConfiguration configuration, MessageReporter reporter) {
+ FileCollector collector = FileCollector.newCollector(
+ configuration.getLanguageVersionDiscoverer(),
+ reporter
+ );
+ collectFiles(configuration, collector);
+ return collector;
+ }
+
+ public static void collectFiles(PMDConfiguration configuration, FileCollector collector) {
+ if (configuration.getSourceEncoding() != null) {
+ collector.setCharset(configuration.getSourceEncoding());
+ }
+
+ if (configuration.getInputPaths() != null) {
+ collectFiles(collector, configuration.getInputPaths());
+ }
+
+ if (configuration.getInputUri() != null) {
+ collectDB(collector, configuration.getInputUri());
+ }
+
+ if (configuration.getInputFilePath() != null) {
+ collectFileList(collector, configuration.getInputFilePath());
+ }
+
+ if (configuration.getIgnoreFilePath() != null) {
+ // This is to be able to interpret the log (will report 'adding' xxx)
+ LOG.debug("Now collecting files to exclude.");
+ // errors like "excluded file does not exist" are reported as warnings.
+ // todo better reporting of *where* exactly the path is
+ MessageReporter mutedLog = new ErrorsAsWarningsReporter(collector.getReporter());
+ try (FileCollector excludeCollector = collector.newCollector(mutedLog)) {
+ collectFileList(excludeCollector, configuration.getIgnoreFilePath());
+ collector.exclude(excludeCollector);
+ }
+ }
+ }
+
+
+ public static void collectFiles(FileCollector collector, String fileLocations) {
+ for (String rootLocation : fileLocations.split(",")) {
+ try {
+ collector.relativizeWith(rootLocation);
+ addRoot(collector, rootLocation);
+ } catch (IOException e) {
+ collector.getReporter().errorEx("Error collecting " + rootLocation, e);
+ }
+ }
+ }
+
+ public static void collectFileList(FileCollector collector, String fileListLocation) {
+ LOG.debug("Reading file list {}.", fileListLocation);
+ Path path = Paths.get(fileListLocation);
+ if (!Files.exists(path)) {
+ collector.getReporter().error("No such file {}", fileListLocation);
+ return;
+ }
+
+ String filePaths;
+ try {
+ filePaths = FileUtil.readFilelist(path.toFile());
+ } catch (IOException e) {
+ collector.getReporter().errorEx("Error reading {}", new Object[] { fileListLocation }, e);
+ return;
+ }
+ collectFiles(collector, filePaths);
+ }
+
+ private static void addRoot(FileCollector collector, String rootLocation) throws IOException {
+ Path path = Paths.get(rootLocation);
+ if (!Files.exists(path)) {
+ collector.getReporter().error("No such file {}", path);
+ return;
+ }
+
+ if (Files.isDirectory(path)) {
+ LOG.debug("Adding directory {}.", path);
+ collector.addDirectory(path);
+ } else if (rootLocation.endsWith(".zip") || rootLocation.endsWith(".jar")) {
+ LOG.debug("Adding zip file {}.", path);
+ @SuppressWarnings("PMD.CloseResource")
+ FileSystem fs = collector.addZipFile(path);
+ if (fs == null) {
+ return;
+ }
+ for (Path zipRoot : fs.getRootDirectories()) {
+ collector.addFileOrDirectory(zipRoot);
+ }
+ } else if (Files.isRegularFile(path)) {
+ LOG.debug("Adding regular file {}.", path);
+ collector.addFile(path);
+ } else {
+ LOG.debug("Ignoring {}: not a regular file or directory", path);
+ }
+ }
+
+ public static void collectDB(FileCollector collector, String uriString) {
+ try {
+ LOG.debug("Connecting to {}", uriString);
+ DBURI dbUri = new DBURI(uriString);
+ DBMSMetadata dbmsMetadata = new DBMSMetadata(dbUri);
+ LOG.trace("DBMSMetadata retrieved");
+ List sourceObjectList = dbmsMetadata.getSourceObjectList();
+ LOG.trace("Located {} database source objects", sourceObjectList.size());
+ for (SourceObject sourceObject : sourceObjectList) {
+ String falseFilePath = sourceObject.getPseudoFileName();
+ LOG.trace("Adding database source object {}", falseFilePath);
+
+ try (Reader sourceCode = dbmsMetadata.getSourceCode(sourceObject)) {
+ String source = IOUtils.toString(sourceCode);
+ collector.addSourceFile(source, falseFilePath);
+ } catch (SQLException ex) {
+ collector.getReporter().warnEx("Cannot get SourceCode for {} - skipping ...",
+ new Object[] { falseFilePath },
+ ex);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ collector.getReporter().errorEx("Cannot get files from DB - probably missing database JDBC driver", e);
+ } catch (Exception e) {
+ collector.getReporter().errorEx("Cannot get files from DB - ''{}''", new Object[] { uriString }, e);
+ }
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java
index 0444155638..cf02bee9c2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java
@@ -4,13 +4,8 @@
package net.sourceforge.pmd.lang;
-import java.util.Collections;
-import java.util.List;
import java.util.Locale;
-import org.apache.commons.lang3.EnumUtils;
-
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertySource;
@@ -25,38 +20,6 @@ import net.sourceforge.pmd.properties.PropertySource;
public abstract class AbstractPmdLanguageVersionHandler extends AbstractLanguageVersionHandler {
- private final List extends AstProcessingStage>> processingStages;
-
-
- /**
- * Declare processing stages within an enum. An enum is the best way
- * to declare them since the illegality of forward references naturally
- * prevents circular dependencies to be declared. The natural ordering
- * on enums is also a sound and stable ordering for processing stages.
- *
- * @param processingStagesEnum Enum class
- * @param Type of the enum class
- */
- protected & AstProcessingStage> AbstractPmdLanguageVersionHandler(Class processingStagesEnum) {
- this.processingStages = EnumUtils.getEnumList(processingStagesEnum);
- }
-
-
- /**
- * Declare no optional processing stages as of yet.
- */
- protected AbstractPmdLanguageVersionHandler() {
- this.processingStages = Collections.emptyList();
- }
-
-
- @Override
- public final List extends AstProcessingStage>> getProcessingStages() {
- return processingStages;
- }
-
-
-
/**
* Returns the environment variable name that a user can set in order to override the default value.
*/
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java
index 4413c302ca..4491ff2a42 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/BaseLanguageModule.java
@@ -154,7 +154,7 @@ public abstract class BaseLanguageModule implements Language {
@Override
public String toString() {
- return "LanguageModule:" + name + '(' + this.getClass().getSimpleName() + ')';
+ return getTerseName();
}
@Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java
index 94bf68af88..b1ac43e348 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java
@@ -35,7 +35,7 @@ public class LanguageVersion implements Comparable {
private final Language language;
private final String version;
- private final LanguageVersionHandler languageVersionHandler;
+ private final LanguageVersionHandler languageVersionHandler; // note: this is null if this is a cpd-only language...
/**
* @deprecated Use {@link Language#getVersion(String)}. This is only
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
index 61e997c29c..4e22b03a4f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java
@@ -8,6 +8,11 @@ import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+
+import net.sourceforge.pmd.internal.util.AssertionUtil;
/**
* This class can discover the LanguageVersion of a source file. Further, every
@@ -17,6 +22,23 @@ import java.util.Map;
public class LanguageVersionDiscoverer {
private Map languageToLanguageVersion = new HashMap<>();
+ private LanguageVersion forcedVersion;
+
+ public LanguageVersionDiscoverer() {
+ this(null);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * @param forcedVersion If non-null, all files should be assigned this version.
+ * The methods of this class still work as usual and do not
+ * care about the forced language version.
+ */
+ public LanguageVersionDiscoverer(LanguageVersion forcedVersion) {
+ this.forcedVersion = forcedVersion;
+ }
+
/**
* Set the given LanguageVersion as the current default for it's Language.
*
@@ -25,6 +47,7 @@ public class LanguageVersionDiscoverer {
* @return The previous default version for the language.
*/
public LanguageVersion setDefaultLanguageVersion(LanguageVersion languageVersion) {
+ AssertionUtil.requireParamNotNull("languageVersion", languageVersion);
LanguageVersion currentLanguageVersion = languageToLanguageVersion.put(languageVersion.getLanguage(),
languageVersion);
if (currentLanguageVersion == null) {
@@ -41,6 +64,7 @@ public class LanguageVersionDiscoverer {
* @return The current default version for the language.
*/
public LanguageVersion getDefaultLanguageVersion(Language language) {
+ Objects.requireNonNull(language);
LanguageVersion languageVersion = languageToLanguageVersion.get(language);
if (languageVersion == null) {
languageVersion = language.getDefaultVersion();
@@ -81,6 +105,14 @@ public class LanguageVersionDiscoverer {
return languageVersion;
}
+ public LanguageVersion getForcedVersion() {
+ return forcedVersion;
+ }
+
+ public void setForcedVersion(LanguageVersion forceLanguageVersion) {
+ this.forcedVersion = forceLanguageVersion;
+ }
+
/**
* Get the Languages of a given source file.
*
@@ -106,11 +138,8 @@ public class LanguageVersionDiscoverer {
// Get the extensions from a file
private String getExtension(String fileName) {
- String extension = null;
- int extensionIndex = 1 + fileName.lastIndexOf('.');
- if (extensionIndex > 0) {
- extension = fileName.substring(extensionIndex);
- }
- return extension;
+ return StringUtils.substringAfterLast(fileName, ".");
}
+
+
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java
index e10e42ec75..1b228e8dc6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java
@@ -4,12 +4,7 @@
package net.sourceforge.pmd.lang;
-import static java.util.Collections.emptyList;
-
-import java.util.List;
-
import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
@@ -37,17 +32,6 @@ public interface LanguageVersionHandler {
}
- /**
- * Returns the list of all supported optional processing stages.
- *
- * @return A list of all optional processing stages.
- */
- @Experimental
- default List extends AstProcessingStage>> getProcessingStages() {
- return emptyList();
- }
-
-
/**
* @deprecated This is transitional
*/
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstAnalysisContext.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstAnalysisContext.java
deleted file mode 100644
index d98b41d066..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstAnalysisContext.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.ast;
-
-import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.LanguageVersion;
-
-
-/**
- * Configuration relevant to e.g. an {@link AstProcessingStage}.
- *
- * @author ClΓ©ment Fournier
- * @since 7.0.0
- */
-@Experimental
-public interface AstAnalysisContext {
-
-
- /**
- * Gets the classloader used for type resolution.
- *
- * @return The classloader.
- */
- ClassLoader getTypeResolutionClassLoader();
-
-
- /**
- * Returns the language version used for this analysis.
- */
- LanguageVersion getLanguageVersion();
-
-}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstProcessingStage.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstProcessingStage.java
deleted file mode 100644
index ecc8f301c7..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstProcessingStage.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.ast;
-
-import static java.util.Collections.emptyList;
-
-import java.util.Comparator;
-import java.util.List;
-
-import net.sourceforge.pmd.RuleSets;
-import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageVersionHandler;
-
-// @formatter:off
-/**
- * Represents one of the stages applying on the AST
- * after parsing is done. Each of these stages implicitly
- * depends on the parser stage.
- *
- * An analysis on a file goes through the following stages:
- *
- * - Parsing stage: taking the source and configuration, and returning an AST
- *
- Language-specific AST visits: sequence of stages specific to each language.
- * Each stage performs side effects on the AST, e.g. to resolve such things as comments,
- * types, DFA graph, etc.
- *
- Rulechain application: all rulechain rules are run on the final AST
- *
- Rule application: other rules are run
- *
- *
- * These steps are run on each file during the analysis, unless the cache entry of the file is up-to-date.
- * They're all run sequentially by the same thread. Rule application performs side-effects on the Report,
- * which is rendered after all files have been processed.
- *
- *
Parsing and rule[chain] application stages are considered special and are handled differently for now.
- * A {@link LanguageVersionHandler} is responsible for listing all available {@link AstProcessingStage}s
- * (see {@link LanguageVersionHandler#getProcessingStages()}). The actual set of stages that will get executed
- * for a run is the union of the dependencies of the rules in the run {@link RuleSets}.
- *
- *
Additional doc, to be moved elsewhere probably:
- *
PMD's execution goes through other more global stages (not sure about the exact order):
- *
- * - Rule loading: creates a {@link RuleSets} from the ruleset files
- *
- Cache loading: loads the cache file for incremental analysis if any, creates the new cache
- *
- Report creation: creates a report object for the rules to act on
- *
- File collection: collects the files to analyse and dispatches them to worker threads.
- * Each file undergoes the steps described above.
- *
- Report rendering
- *
- Cache persisting
- *
- *
- *
- * @author ClΓ©ment Fournier
- * @since 7.0.0
- */
-// @formatter:on
-@Experimental
-public interface AstProcessingStage> extends Comparable {
-
- /**
- * Compares processing stages of possibly different kinds.
- */
- Comparator> COMPARATOR = AstProcessingStage::compare;
-
-
- /**
- * Returns the language this processing stage applies to.
- */
- Language getLanguage();
-
-
- /**
- * Gets the stages on which this stage depends.
- * E.g. the type resolution stage may depend on the
- * qualified name resolution stage.
- *
- * Returns an empty list if this stage only depends
- * on the parser stage.
- */
- default List getDependencies() {
- return emptyList();
- }
-
-
- /**
- * Returns the name of this stage, used e.g. to display in a
- * benchmark report.
- *
- * @return The name of the stage.
- */
- String getDisplayName();
-
-
- /**
- * Performs some side effects on the AST, e.g. to resolve something.
- *
- * @param rootNode Root of the tree
- * @param configuration Configuration
- */
- void processAST(RootNode rootNode, AstAnalysisContext configuration);
-
-
- /**
- * Same contract as {@link Comparable#compareTo(Object)}, but we can't extend
- * Comparable with that type argument if we implement processing stages within
- * an enum.
- *
- * @param t the object to compare
- *
- * @return a negative integer, zero, or a positive integer as this object
- * is less than, equal to, or greater than the specified object.
- */
- @SuppressWarnings("unchecked")
- default int compare(AstProcessingStage> t) {
- return this.compareTo((T) t);
- }
-
-
-}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java
index 7e7b04fec1..348d104a3a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java
@@ -4,11 +4,13 @@
package net.sourceforge.pmd.lang.ast;
+import static net.sourceforge.pmd.internal.util.AssertionUtil.requireParamNotNull;
+
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
-import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.properties.AbstractPropertySource;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@@ -48,23 +50,29 @@ public interface Parser {
private final String filepath;
private final String sourceText;
private final SemanticErrorReporter reporter;
+ private final ClassLoader auxclasspathClassLoader;
private final PropertySource propertySource;
- public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter) {
- this.lv = Objects.requireNonNull(lv, "lv was null");
- this.filepath = Objects.requireNonNull(filepath, "filepath was null");
- this.sourceText = Objects.requireNonNull(sourceText, "sourceText was null");
- this.reporter = Objects.requireNonNull(reporter, "reporter was null");
+ public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter, ClassLoader auxclasspathClassLoader) {
+ this.lv = requireParamNotNull("language version", lv);
+ this.filepath = requireParamNotNull("filepath", filepath);
+ this.sourceText = requireParamNotNull("sourceText", sourceText);
+ this.reporter = requireParamNotNull("reporter", reporter);
+ this.auxclasspathClassLoader = requireParamNotNull("auxclasspathClassLoader", auxclasspathClassLoader);
this.propertySource = new ParserTaskProperties();
propertySource.definePropertyDescriptor(COMMENT_MARKER);
}
+ public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter) {
+ this(lv, filepath, sourceText, reporter, Parser.class.getClassLoader());
+ }
+
public static final PropertyDescriptor COMMENT_MARKER =
PropertyFactory.stringProperty("suppressionCommentMarker")
.desc("deprecated! NOPMD")
- .defaultValue(PMD.SUPPRESS_MARKER)
+ .defaultValue(PMDConfiguration.DEFAULT_SUPPRESS_MARKER)
.build();
@Deprecated // transitional until language properties are implemented
@@ -72,6 +80,10 @@ public interface Parser {
return propertySource;
}
+ @Deprecated // transitional until language properties are implemented
+ public ClassLoader getAuxclasspathClassLoader() {
+ return auxclasspathClassLoader;
+ }
public LanguageVersion getLanguageVersion() {
return lv;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java
index 1ea0dae5da..7caaa8e15c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/AbstractJjtreeNode.java
@@ -38,6 +38,7 @@ public abstract class AbstractJjtreeNode, N e
}
@Override
+ // @Deprecated // todo deprecate, will change tree dump tests
public String getImage() {
return image;
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java
new file mode 100644
index 0000000000..55c7fb5d24
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java
@@ -0,0 +1,400 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.ProviderNotFoundException;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.sourceforge.pmd.PmdAnalysis;
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
+import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
+import net.sourceforge.pmd.util.IOUtil;
+import net.sourceforge.pmd.util.log.MessageReporter;
+
+/**
+ * Collects files to analyse before a PMD run. This API allows opening
+ * zip files and makes sure they will be closed at the end of a run.
+ *
+ * @author ClΓ©ment Fournier
+ */
+@SuppressWarnings("PMD.CloseResource")
+public final class FileCollector implements AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(FileCollector.class);
+
+ private final List allFilesToProcess = new ArrayList<>();
+ private final List resourcesToClose = new ArrayList<>();
+ private Charset charset = StandardCharsets.UTF_8;
+ private final LanguageVersionDiscoverer discoverer;
+ private final MessageReporter reporter;
+ private final List relativizeRoots = new ArrayList<>();
+ private boolean closed;
+
+ // construction
+
+ private FileCollector(LanguageVersionDiscoverer discoverer, MessageReporter reporter) {
+ this.discoverer = discoverer;
+ this.reporter = reporter;
+ }
+
+ /**
+ * Internal API: please use {@link PmdAnalysis#files()} instead of
+ * creating a collector yourself.
+ */
+ @InternalApi
+ public static FileCollector newCollector(LanguageVersionDiscoverer discoverer, MessageReporter reporter) {
+ return new FileCollector(discoverer, reporter);
+ }
+
+ /**
+ * Returns a new collector using the configuration except for the logger.
+ */
+ @InternalApi
+ public FileCollector newCollector(MessageReporter logger) {
+ FileCollector fileCollector = new FileCollector(discoverer, logger);
+ fileCollector.charset = this.charset;
+ fileCollector.relativizeRoots.addAll(this.relativizeRoots);
+ return fileCollector;
+ }
+
+ // public behaviour
+
+ /**
+ * Returns an unmodifiable list of all files that have been collected.
+ *
+ * Internal: This might be unstable until PMD 7, but it's internal.
+ */
+ @InternalApi
+ public List getCollectedFiles() {
+ if (closed) {
+ throw new IllegalStateException("Collector was closed!");
+ }
+ allFilesToProcess.sort(Comparator.comparing(TextFile::getPathId));
+ return Collections.unmodifiableList(allFilesToProcess);
+ }
+
+
+ /**
+ * Returns the reporter for the file collection phase.
+ */
+ @InternalApi
+ public MessageReporter getReporter() {
+ return reporter;
+ }
+
+ /**
+ * Close registered resources like zip files.
+ */
+ @Override
+ public void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ Exception exception = IOUtil.closeAll(resourcesToClose);
+ if (exception != null) {
+ reporter.errorEx("Error while closing resources", exception);
+ }
+ }
+
+ // collection
+
+ /**
+ * Add a file, language is determined automatically from
+ * the extension/file patterns. The encoding is the current
+ * encoding ({@link #setCharset(Charset)}).
+ *
+ * @param file File to add
+ *
+ * @return True if the file has been added
+ */
+ public boolean addFile(Path file) {
+ if (!Files.isRegularFile(file)) {
+ reporter.error("Not a regular file {}", file);
+ return false;
+ }
+ LanguageVersion languageVersion = discoverLanguage(file.toString());
+ if (languageVersion != null) {
+ addFileImpl(new NioTextFile(file, charset, languageVersion, getDisplayName(file)));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Add a file with the given language (which overrides the file patterns).
+ * The encoding is the current encoding ({@link #setCharset(Charset)}).
+ *
+ * @param file Path to a file
+ * @param language A language. The language version will be taken to be the
+ * contextual default version.
+ *
+ * @return True if the file has been added
+ */
+ public boolean addFile(Path file, Language language) {
+ AssertionUtil.requireParamNotNull("language", language);
+ if (!Files.isRegularFile(file)) {
+ reporter.error("Not a regular file {}", file);
+ return false;
+ }
+ NioTextFile nioTextFile = new NioTextFile(file, charset, discoverer.getDefaultLanguageVersion(language), getDisplayName(file));
+ addFileImpl(nioTextFile);
+ return true;
+ }
+
+ /**
+ * Add a pre-configured text file. The language version will be checked
+ * to match the contextual default for the language (the file cannot be added
+ * if it has a different version).
+ *
+ * @return True if the file has been added
+ */
+ public boolean addFile(TextFile textFile) {
+ AssertionUtil.requireParamNotNull("textFile", textFile);
+ if (checkContextualVersion(textFile)) {
+ addFileImpl(textFile);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Add a text file given its contents and a name. The language version
+ * will be determined from the name as usual.
+ *
+ * @return True if the file has been added
+ */
+ public boolean addSourceFile(String pathId, String sourceContents) {
+ AssertionUtil.requireParamNotNull("sourceContents", sourceContents);
+ AssertionUtil.requireParamNotNull("pathId", pathId);
+
+ LanguageVersion version = discoverLanguage(pathId);
+ if (version != null) {
+ addFileImpl(new StringTextFile(sourceContents, pathId, pathId, version));
+ return true;
+ }
+
+ return false;
+ }
+
+ private void addFileImpl(TextFile textFile) {
+ LOG.trace("Adding file {} (lang: {}) ", textFile.getPathId(), textFile.getLanguageVersion().getTerseName());
+ allFilesToProcess.add(textFile);
+ }
+
+ private LanguageVersion discoverLanguage(String file) {
+ if (discoverer.getForcedVersion() != null) {
+ return discoverer.getForcedVersion();
+ }
+ List languages = discoverer.getLanguagesForFile(file);
+
+ if (languages.isEmpty()) {
+ LOG.trace("File {} matches no known language, ignoring", file);
+ return null;
+ }
+ Language lang = languages.get(0);
+ if (languages.size() > 1) {
+ LOG.trace("File {} matches multiple languages ({}), selecting {}", file, languages, lang);
+ }
+ return discoverer.getDefaultLanguageVersion(lang);
+ }
+
+ /**
+ * Whether the LanguageVersion of the file matches the one set in
+ * the {@link LanguageVersionDiscoverer}. This is required to ensure
+ * that all files for a given language have the same language version.
+ */
+ private boolean checkContextualVersion(TextFile textFile) {
+ LanguageVersion fileVersion = textFile.getLanguageVersion();
+ Language language = fileVersion.getLanguage();
+ LanguageVersion contextVersion = discoverer.getDefaultLanguageVersion(language);
+ if (!fileVersion.equals(contextVersion)) {
+ reporter.error(
+ "Cannot add file {}: version ''{}'' does not match ''{}''",
+ textFile.getPathId(),
+ fileVersion,
+ contextVersion
+ );
+ return false;
+ }
+ return true;
+ }
+
+ private String getDisplayName(Path file) {
+ return getDisplayName(file, relativizeRoots);
+ }
+
+ /**
+ * Return the textfile's display name.
+ * test only
+ */
+ static String getDisplayName(Path file, List relativizeRoots) {
+ String fileName = file.toString();
+ for (String root : relativizeRoots) {
+ if (file.startsWith(root)) {
+ if (fileName.startsWith(File.separator, root.length())) {
+ // remove following '/'
+ return fileName.substring(root.length() + 1);
+ }
+ return fileName.substring(root.length());
+ }
+ }
+ return fileName;
+ }
+
+
+ /**
+ * Add a directory recursively using {@link #addFile(Path)} on
+ * all regular files.
+ *
+ * @param dir Directory path
+ *
+ * @return True if the directory has been added
+ */
+ public boolean addDirectory(Path dir) throws IOException {
+ if (!Files.isDirectory(dir)) {
+ reporter.error("Not a directory {}", dir);
+ return false;
+ }
+ Files.walkFileTree(dir, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (attrs.isRegularFile()) {
+ FileCollector.this.addFile(file);
+ }
+ return super.visitFile(file, attrs);
+ }
+ });
+ return true;
+ }
+
+
+ /**
+ * Add a file or directory recursively. Language is determined automatically
+ * from the extension/file patterns.
+ *
+ * @return True if the file or directory has been added
+ */
+ public boolean addFileOrDirectory(Path file) throws IOException {
+ if (Files.isDirectory(file)) {
+ return addDirectory(file);
+ } else if (Files.isRegularFile(file)) {
+ return addFile(file);
+ } else {
+ reporter.error("Not a file or directory {}", file);
+ return false;
+ }
+ }
+
+ /**
+ * Opens a zip file and returns a FileSystem for its contents, so
+ * it can be explored with the {@link Path} API. You can then call
+ * {@link #addFile(Path)} and such. The zip file is registered as
+ * a resource to close at the end of analysis.
+ */
+ public FileSystem addZipFile(Path zipFile) {
+ if (!Files.isRegularFile(zipFile)) {
+ throw new IllegalArgumentException("Not a regular file: " + zipFile);
+ }
+ URI zipUri = URI.create("zip:" + zipFile.toUri());
+ try {
+ FileSystem fs = FileSystems.getFileSystem(zipUri);
+ resourcesToClose.add(fs);
+ return fs;
+ } catch (FileSystemNotFoundException | ProviderNotFoundException e) {
+ reporter.errorEx("Cannot open zip file " + zipFile, e);
+ return null;
+ }
+ }
+
+ // configuration
+
+ /**
+ * Sets the charset to use for subsequent calls to {@link #addFile(Path)}
+ * and other overloads using a {@link Path}.
+ *
+ * @param charset A charset
+ */
+ public void setCharset(Charset charset) {
+ this.charset = Objects.requireNonNull(charset);
+ }
+
+ /**
+ * Add a prefix that is used to relativize file paths as their display name.
+ * For instance, when adding a file {@code /tmp/src/main/java/org/foo.java},
+ * and relativizing with {@code /tmp/src/}, the registered {@link TextFile}
+ * will have a path id of {@code /tmp/src/main/java/org/foo.java}, and a
+ * display name of {@code main/java/org/foo.java}.
+ *
+ * This only matters for files added from a {@link Path} object.
+ *
+ * @param prefix Prefix to relativize (if a directory, include a trailing slash)
+ */
+ public void relativizeWith(String prefix) {
+ this.relativizeRoots.add(Objects.requireNonNull(prefix));
+ }
+
+ // filtering
+
+ /**
+ * Remove all files collected by the given collector from this one.
+ */
+ public void exclude(FileCollector excludeCollector) {
+ Set toExclude = new HashSet<>(excludeCollector.allFilesToProcess);
+ for (Iterator iterator = allFilesToProcess.iterator(); iterator.hasNext();) {
+ TextFile file = iterator.next();
+ if (toExclude.contains(file)) {
+ LOG.trace("Excluding file {}", file.getPathId());
+ iterator.remove();
+ }
+ }
+ }
+
+ /**
+ * Exclude all collected files whose language is not part of the given
+ * collection.
+ */
+ public void filterLanguages(Set languages) {
+ for (Iterator iterator = allFilesToProcess.iterator(); iterator.hasNext();) {
+ TextFile file = iterator.next();
+ Language lang = file.getLanguageVersion().getLanguage();
+ if (!languages.contains(lang)) {
+ LOG.trace("Filtering out {}, no rules for language {}", file.getPathId(), lang);
+ iterator.remove();
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "FileCollector{filesToProcess=" + allFilesToProcess + '}';
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java
new file mode 100644
index 0000000000..705ef530a7
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java
@@ -0,0 +1,101 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+
+import org.apache.commons.io.IOUtils;
+
+import net.sourceforge.pmd.annotation.Experimental;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.util.datasource.DataSource;
+import net.sourceforge.pmd.util.datasource.FileDataSource;
+
+/**
+ * A {@link TextFile} backed by a file in some {@link FileSystem}.
+ */
+@Experimental
+class NioTextFile implements TextFile {
+
+ private final Path path;
+ private final Charset charset;
+ private final LanguageVersion languageVersion;
+ private final String displayName;
+ private final String pathId;
+
+ NioTextFile(Path path, Charset charset, LanguageVersion languageVersion, String displayName) {
+ AssertionUtil.requireParamNotNull("path", path);
+ AssertionUtil.requireParamNotNull("charset", charset);
+ AssertionUtil.requireParamNotNull("language version", languageVersion);
+
+ this.displayName = displayName;
+ this.path = path;
+ this.charset = charset;
+ this.languageVersion = languageVersion;
+ this.pathId = path.toAbsolutePath().toString();
+ }
+
+ @Override
+ public LanguageVersion getLanguageVersion() {
+ return languageVersion;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ @Override
+ public String getPathId() {
+ return pathId;
+ }
+
+
+ @Override
+ public String readContents() throws IOException {
+
+ if (!Files.isRegularFile(path)) {
+ throw new IOException("Not a regular file: " + path);
+ }
+
+ try (BufferedReader br = Files.newBufferedReader(path, charset)) {
+ return IOUtils.toString(br);
+ }
+ }
+
+ @Override
+ public DataSource toDataSourceCompat() {
+ return new FileDataSource(path.toFile());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NioTextFile that = (NioTextFile) o;
+ return Objects.equals(path, that.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pathId);
+ }
+
+ @Override
+ public String toString() {
+ return getPathId();
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java
new file mode 100644
index 0000000000..598823e555
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java
@@ -0,0 +1,94 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import java.io.StringReader;
+import java.util.Objects;
+
+import net.sourceforge.pmd.annotation.Experimental;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.util.datasource.DataSource;
+import net.sourceforge.pmd.util.datasource.ReaderDataSource;
+
+/**
+ * Read-only view on a string.
+ *
+ * @author ClΓ©ment Fournier
+ */
+@Experimental
+class StringTextFile implements TextFile {
+
+ private final String content;
+ private final String pathId;
+ private final String displayName;
+ private final LanguageVersion languageVersion;
+
+ StringTextFile(String content,
+ String pathId,
+ String displayName,
+ LanguageVersion languageVersion) {
+ AssertionUtil.requireParamNotNull("source text", content);
+ AssertionUtil.requireParamNotNull("file name", displayName);
+ AssertionUtil.requireParamNotNull("file ID", pathId);
+ AssertionUtil.requireParamNotNull("language version", languageVersion);
+
+ this.languageVersion = languageVersion;
+ this.content = content;
+ this.pathId = pathId;
+ this.displayName = displayName;
+ }
+
+
+ @Override
+ public LanguageVersion getLanguageVersion() {
+ return languageVersion;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ @Override
+ public String getPathId() {
+ return pathId;
+ }
+
+ @Override
+ public String readContents() {
+ return content;
+ }
+
+ @Override
+ public DataSource toDataSourceCompat() {
+ return new ReaderDataSource(
+ new StringReader(content),
+ pathId
+ );
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ StringTextFile that = (StringTextFile) o;
+ return Objects.equals(pathId, that.pathId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pathId);
+ }
+
+ @Override
+ public String toString() {
+ return getPathId();
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java
new file mode 100644
index 0000000000..34d232d38a
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java
@@ -0,0 +1,98 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import java.io.File;
+import java.io.IOException;
+
+import net.sourceforge.pmd.PmdAnalysis;
+import net.sourceforge.pmd.annotation.Experimental;
+import net.sourceforge.pmd.cpd.SourceCode;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.util.datasource.DataSource;
+
+/**
+ * Represents some location containing character data. Despite the name,
+ * it's not necessarily backed by a file in the file-system: it may be
+ * eg an in-memory buffer, or a zip entry, ie it's an abstraction. Text
+ * files are the input which PMD and CPD process.
+ *
+ * Text files must provide read access, and may provide write access.
+ * This interface only provides block IO operations, while {@link TextDocument} adds logic
+ * about incremental edition (eg replacing a single region of text).
+ *
+ *
This interface is meant to replace {@link DataSource} and {@link SourceCode.CodeLoader}.
+ * "DataSource" is not an appropriate name for a file which can be written
+ * to, also, the "data" it provides is text, not bytes.
+ *
+ *
Experimental
+ * This interface will change in PMD 7 to support read/write operations
+ * and other things. You don't need to use it in PMD 6, as {@link FileCollector}
+ * decouples you from this. A file collector is available through {@link PmdAnalysis#files()}.
+ */
+@Experimental
+public interface TextFile {
+
+ /**
+ * The name used for a file that has no name. This is mostly only
+ * relevant for unit tests.
+ */
+ String UNKNOWN_FILENAME = "(unknown file)";
+
+
+ /**
+ * Returns the language version which should be used to process this
+ * file. This is a property of the file, which allows sources for
+ * several different language versions to be processed in the same
+ * PMD run. It also makes it so, that the file extension is not interpreted
+ * to find out the language version after the initial file collection
+ * phase.
+ *
+ * @return A language version
+ */
+ LanguageVersion getLanguageVersion();
+
+
+ /**
+ * Returns an identifier for the path of this file. This should not
+ * be interpreted as a {@link File}, it may not be a file on this
+ * filesystem. The only requirement for this method, is that two
+ * distinct text files should have distinct path IDs, and that from
+ * one analysis to the next, the path ID of logically identical files
+ * be the same.
+ *
+ * Basically this may be implemented as a URL, or a file path. It
+ * is used to index violation caches.
+ */
+ String getPathId();
+
+
+ /**
+ * Returns a display name for the file. This name is used for
+ * reporting and should not be interpreted. It may be relative
+ * to a directory, may use platform-specific path separators,
+ * may not be normalized. Use {@link #getPathId()} when you
+ * want an identifier.
+ */
+ String getDisplayName();
+
+
+ /**
+ * Reads the contents of the underlying character source.
+ *
+ * @return The most up-to-date content
+ *
+ * @throws IOException If this instance is closed
+ * @throws IOException If reading causes an IOException
+ */
+ String readContents() throws IOException;
+
+ /**
+ * Compatibility with {@link DataSource} (pmd internals still use DataSource in PMD 6).
+ */
+ @Deprecated
+ DataSource toDataSourceCompat();
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/internal/LanguageDiscoverer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/internal/LanguageDiscoverer.java
new file mode 100644
index 0000000000..0544cc175c
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/internal/LanguageDiscoverer.java
@@ -0,0 +1,63 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document.internal;
+
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.LanguageRegistry;
+
+/**
+ * Discovers the languages applicable to a file.
+ */
+public class LanguageDiscoverer {
+
+
+ private final Language forcedLanguage;
+
+ /**
+ * Build a new instance.
+ *
+ * @param forcedLanguage If non-null, all files will be assigned this language.
+ */
+ public LanguageDiscoverer(Language forcedLanguage) {
+ this.forcedLanguage = forcedLanguage;
+ }
+
+ /**
+ * Get the Languages of a given source file.
+ *
+ * @param sourceFile The file.
+ *
+ * @return The Languages for the source file, may be empty.
+ */
+ public List getLanguagesForFile(Path sourceFile) {
+ return getLanguagesForFile(sourceFile.getFileName().toString());
+ }
+
+ /**
+ * Get the Languages of a given source file.
+ *
+ * @param fileName The file name.
+ *
+ * @return The Languages for the source file, may be empty.
+ */
+ public List getLanguagesForFile(String fileName) {
+ if (forcedLanguage != null) {
+ return Collections.singletonList(forcedLanguage);
+ }
+ String extension = getExtension(fileName);
+ return LanguageRegistry.findByExtension(extension);
+ }
+
+ // Get the extensions from a file
+ private String getExtension(String fileName) {
+ return StringUtils.substringAfterLast(fileName, ".");
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java
index 1cd20d9363..57ba908c5c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java
@@ -16,7 +16,6 @@ import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleSetReference;
import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.util.StringUtil;
@@ -278,12 +277,6 @@ public class RuleReference extends AbstractDelegateRule {
}
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- return getRule().dependsOn(stage);
- }
-
-
public RuleSetReference getRuleSetReference() {
return ruleSetReference;
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
index 7e0039d637..32f18070be 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java
@@ -19,7 +19,6 @@ import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
@@ -196,12 +195,6 @@ public final class XPathRule extends AbstractRule {
return null;
}
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- // FIXME must be made language-specific
- return true;
- }
-
private static Map getXPathVersions() {
Map tmp = new HashMap<>();
for (XPathVersion v : XPathVersion.values()) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java
index ea49880b17..87df7b0eb6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java
@@ -7,6 +7,9 @@ package net.sourceforge.pmd.processor;
import java.io.File;
import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleSets;
@@ -14,7 +17,6 @@ import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.benchmark.TimedOperation;
import net.sourceforge.pmd.benchmark.TimedOperationCategory;
-import net.sourceforge.pmd.internal.RulesetStageDependencyHelper;
import net.sourceforge.pmd.internal.SystemProps;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
@@ -32,14 +34,13 @@ import net.sourceforge.pmd.util.datasource.DataSource;
*/
abstract class PmdRunnable implements Runnable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PmdRunnable.class);
private final DataSource dataSource;
private final File file;
private final GlobalAnalysisListener globalListener;
private final PMDConfiguration configuration;
- private final RulesetStageDependencyHelper dependencyHelper;
-
PmdRunnable(DataSource dataSource,
GlobalAnalysisListener globalListener,
PMDConfiguration configuration) {
@@ -50,7 +51,6 @@ abstract class PmdRunnable implements Runnable {
this.file = new File(realFileName);
this.globalListener = globalListener;
this.configuration = configuration;
- this.dependencyHelper = new RulesetStageDependencyHelper(configuration);
}
/**
@@ -82,7 +82,6 @@ abstract class PmdRunnable implements Runnable {
if (e instanceof Error && !SystemProps.isErrorRecoveryMode()) { // NOPMD:
throw e;
}
- configuration.getAnalysisCache().analysisFailed(file);
// The listener handles logging if needed,
// it may also rethrow the error, as a FileAnalysisException (which we let through below)
@@ -130,7 +129,8 @@ abstract class PmdRunnable implements Runnable {
languageVersion,
filename,
sourceCode,
- SemanticErrorReporter.noop() // TODO
+ SemanticErrorReporter.reportToLogger(LOGGER),
+ configuration.getClassLoader()
);
@@ -143,8 +143,6 @@ abstract class PmdRunnable implements Runnable {
RootNode rootNode = parse(parser, task);
- dependencyHelper.runLanguageSpecificStages(ruleSets, languageVersion, rootNode);
-
ruleSets.apply(rootNode, listener);
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
index 05385eff45..226f2737e7 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
@@ -15,6 +15,7 @@ import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.properties.PropertyDescriptor;
/**
@@ -61,6 +62,7 @@ public final class RendererFactory {
* @return A Renderer instance.
*/
public static Renderer createRenderer(String reportFormat, Properties properties) {
+ AssertionUtil.requireParamNotNull("reportFormat", reportFormat);
Class extends Renderer> rendererClass = getRendererClass(reportFormat);
Constructor extends Renderer> constructor = getRendererConstructor(rendererClass);
@@ -101,6 +103,7 @@ public final class RendererFactory {
@SuppressWarnings("unchecked")
private static Class extends Renderer> getRendererClass(String reportFormat) {
+ AssertionUtil.requireParamNotNull("reportFormat", reportFormat);
Class extends Renderer> rendererClass = REPORT_FORMAT_TO_RENDERER.get(reportFormat);
// Look up a custom renderer class
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/GlobalAnalysisListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/GlobalAnalysisListener.java
index ca71b7a608..c2a8f7d586 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/GlobalAnalysisListener.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/GlobalAnalysisListener.java
@@ -85,17 +85,7 @@ public interface GlobalAnalysisListener extends AutoCloseable {
* A listener that does nothing.
*/
static GlobalAnalysisListener noop() {
- return new GlobalAnalysisListener() {
- @Override
- public FileAnalysisListener startFileAnalysis(DataSource file) {
- return FileAnalysisListener.noop();
- }
-
- @Override
- public void close() {
- // do nothing
- }
- };
+ return NoopAnalysisListener.INSTANCE;
}
/**
@@ -111,10 +101,11 @@ public interface GlobalAnalysisListener extends AutoCloseable {
*/
static GlobalAnalysisListener tee(Collection extends GlobalAnalysisListener> listeners) {
AssertionUtil.requireParamNotNull("Listeners", listeners);
- AssertionUtil.requireNotEmpty("Listeners", listeners);
AssertionUtil.requireContainsNoNullValue("Listeners", listeners);
- if (listeners.size() == 1) {
+ if (listeners.isEmpty()) {
+ return noop();
+ } else if (listeners.size() == 1) {
return listeners.iterator().next();
}
@@ -150,6 +141,7 @@ public interface GlobalAnalysisListener extends AutoCloseable {
List myList =
listeners.stream()
.flatMap(l -> l instanceof TeeListener ? ((TeeListener) l).myList.stream() : Stream.of(l))
+ .filter(l -> !(l instanceof NoopAnalysisListener))
.collect(CollectionUtil.toUnmodifiableList());
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/NoopAnalysisListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/NoopAnalysisListener.java
new file mode 100644
index 0000000000..fcd5da121c
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/NoopAnalysisListener.java
@@ -0,0 +1,29 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.reporting;
+
+import net.sourceforge.pmd.util.datasource.DataSource;
+
+/**
+ * @author ClΓ©ment Fournier
+ */
+final class NoopAnalysisListener implements GlobalAnalysisListener {
+
+ static final NoopAnalysisListener INSTANCE = new NoopAnalysisListener();
+
+ private NoopAnalysisListener() {
+
+ }
+
+ @Override
+ public FileAnalysisListener startFileAnalysis(DataSource file) {
+ return FileAnalysisListener.noop();
+ }
+
+ @Override
+ public void close() {
+ // do nothing
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java
new file mode 100644
index 0000000000..eef7cd7dc5
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStats.java
@@ -0,0 +1,38 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.reporting;
+
+/**
+ * Summarized info about a report.
+ *
+ * @author ClΓ©ment Fournier
+ */
+public final class ReportStats {
+
+ private final int numErrors;
+ private final int numViolations;
+
+ ReportStats(int numErrors, int numViolations) {
+ this.numErrors = numErrors;
+ this.numViolations = numViolations;
+ }
+
+ public static ReportStats empty() {
+ return new ReportStats(0, 0);
+ }
+
+ public int getNumErrors() {
+ return numErrors;
+ }
+
+ public int getNumViolations() {
+ return numViolations;
+ }
+
+ @Override
+ public String toString() {
+ return "ReportStats{numErrors=" + numErrors + ", numViolations=" + numViolations + '}';
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStatsListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStatsListener.java
new file mode 100644
index 0000000000..baabb2d12b
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/ReportStatsListener.java
@@ -0,0 +1,63 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.reporting;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import net.sourceforge.pmd.Report.ProcessingError;
+import net.sourceforge.pmd.RuleViolation;
+import net.sourceforge.pmd.util.BaseResultProducingCloseable;
+import net.sourceforge.pmd.util.datasource.DataSource;
+
+/**
+ * Collects summarized info about a PMD run.
+ *
+ * @author ClΓ©ment Fournier
+ */
+public final class ReportStatsListener extends BaseResultProducingCloseable implements GlobalAnalysisListener {
+
+ private final AtomicInteger numErrors = new AtomicInteger(0);
+ private final AtomicInteger numViolations = new AtomicInteger(0);
+
+ @Override
+ public FileAnalysisListener startFileAnalysis(DataSource file) {
+ return new FileAnalysisListener() {
+ // this object does not need thread-safety so we avoid using atomics,
+ // except during the merge.
+ private int numErrors = 0;
+ private int numViolations = 0;
+
+ @Override
+ public void onRuleViolation(RuleViolation violation) {
+ numViolations++;
+ }
+
+ @Override
+ public void onError(ProcessingError error) {
+ numErrors++;
+ }
+
+ @Override
+ public void close() {
+ if (numErrors > 0) {
+ ReportStatsListener.this.numErrors.addAndGet(this.numErrors);
+ }
+ if (numViolations > 0) {
+ ReportStatsListener.this.numViolations.addAndGet(this.numViolations);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected ReportStats getResultImpl() {
+ return new ReportStats(
+ numErrors.get(),
+ numViolations.get()
+ );
+ }
+
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java
index 47313a12f7..beabbc6080 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java
@@ -75,7 +75,7 @@ public class RuleBuilder {
Language lang = LanguageRegistry.findLanguageByTerseName(languageName);
if (lang == null) {
throw new IllegalArgumentException(
- "Unknown Language '" + languageName + "' for rule" + name + ", supported Languages are "
+ "Unknown Language '" + languageName + "' for rule " + name + ", supported Languages are "
+ LanguageRegistry.getLanguages().stream().map(Language::getTerseName).collect(Collectors.joining(", "))
);
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/ClasspathClassLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/ClasspathClassLoader.java
index 6e31ee3986..0266574901 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/ClasspathClassLoader.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/ClasspathClassLoader.java
@@ -20,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
/**
* Create a ClassLoader which loads classes using a CLASSPATH like String. If
@@ -57,17 +58,19 @@ public class ClasspathClassLoader extends URLClassLoader {
return urlList.toArray(new URL[0]);
}
- private static URL[] initURLs(String classpath) throws IOException {
- if (classpath == null) {
- throw new IllegalArgumentException("classpath argument cannot be null");
- }
+ private static URL[] initURLs(String classpath) {
+ AssertionUtil.requireParamNotNull("classpath", classpath);
final List urls = new ArrayList<>();
- if (classpath.startsWith("file:")) {
- // Treat as file URL
- addFileURLs(urls, new URL(classpath));
- } else {
- // Treat as classpath
- addClasspathURLs(urls, classpath);
+ try {
+ if (classpath.startsWith("file:")) {
+ // Treat as file URL
+ addFileURLs(urls, new URL(classpath));
+ } else {
+ // Treat as classpath
+ addClasspathURLs(urls, classpath);
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot prepend classpath " + classpath + "\n" + e.getMessage(), e);
}
return urls.toArray(new URL[0]);
}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java
index 28f1aa886c..38484692ed 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java
@@ -99,7 +99,7 @@ public final class FileUtil {
}
private static List collect(List dataSources, String fileLocation,
- FilenameFilter filenameFilter) {
+ FilenameFilter filenameFilter) {
File file = new File(fileLocation);
if (!file.exists()) {
throw new RuntimeException("File " + file.getName() + " doesn't exist");
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
index d25bf1027a..cde70a9b78 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.util;
+import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
@@ -513,6 +514,13 @@ public final class StringUtil {
return retval.toString();
}
+ /**
+ * Escape the string so that it appears literally when interpreted
+ * by a {@link MessageFormat}.
+ */
+ public static String quoteMessageFormat(String str) {
+ return str.replaceAll("'", "''");
+ }
public enum CaseConvention {
/** SCREAMING_SNAKE_CASE. */
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java
new file mode 100644
index 0000000000..689e4f41c4
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java
@@ -0,0 +1,69 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.log;
+
+import java.text.MessageFormat;
+
+import org.slf4j.event.Level;
+
+import net.sourceforge.pmd.annotation.InternalApi;
+
+/**
+ * Façade to report user-facing messages (info, warning and error).
+ * Note: messages are formatted using {@link MessageFormat}.
+ *
+ * Internal API: this is a transitional API that will be significantly
+ * changed in PMD 7, with the transition to SLF4J. See https://github.com/pmd/pmd/issues/3816
+ *
+ * TODO rename to PmdReporter
+ *
+ * @author ClΓ©ment Fournier
+ */
+@InternalApi
+public interface MessageReporter {
+
+ boolean isLoggable(Level level);
+
+ void log(Level level, String message, Object... formatArgs);
+
+ void logEx(Level level, String message, Object[] formatArgs, Throwable error);
+
+ default void info(String message, Object... formatArgs) {
+ log(Level.INFO, message, formatArgs);
+ }
+
+ default void warn(String message, Object... formatArgs) {
+ log(Level.WARN, message, formatArgs);
+ }
+
+ default void warnEx(String message, Throwable error) {
+ logEx(Level.WARN, message, new Object[0], error);
+ }
+
+ default void warnEx(String message, Object[] formatArgs, Throwable error) {
+ logEx(Level.WARN, message, formatArgs, error);
+ }
+
+ default void error(String message, Object... formatArgs) {
+ log(Level.ERROR, message, formatArgs);
+ }
+
+ default void errorEx(String message, Throwable error) {
+ logEx(Level.ERROR, message, new Object[0], error);
+ }
+
+ default void errorEx(String message, Object[] formatArgs, Throwable error) {
+ logEx(Level.ERROR, message, formatArgs, error);
+ }
+
+ /**
+ * Returns the number of errors reported on this instance.
+ * Any call to {@link #log(Level, String, Object...)} or
+ * {@link #logEx(Level, String, Object[], Throwable)} with a level
+ * of {@link Level#ERROR} should increment this number.
+ */
+ int numErrors();
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java
new file mode 100644
index 0000000000..80270d9163
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java
@@ -0,0 +1,41 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.log.internal;
+
+import org.slf4j.event.Level;
+
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.util.log.MessageReporter;
+
+/**
+ * Turns errors into warnings reported on another logger.
+ *
+ * @author ClΓ©ment Fournier
+ */
+@InternalApi
+public final class ErrorsAsWarningsReporter extends MessageReporterBase {
+
+ private final MessageReporter backend;
+
+ public ErrorsAsWarningsReporter(MessageReporter backend) {
+ this.backend = backend;
+ }
+
+ @Override
+ protected boolean isLoggableImpl(Level level) {
+ if (level == Level.ERROR) {
+ level = Level.WARN;
+ }
+ return super.isLoggableImpl(level);
+ }
+
+ @Override
+ protected void logImpl(Level level, String message, Object[] formatArgs) {
+ if (level == Level.ERROR) {
+ level = Level.WARN;
+ }
+ backend.log(level, message, formatArgs);
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java
new file mode 100644
index 0000000000..64f045174d
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java
@@ -0,0 +1,80 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.log.internal;
+
+import java.text.MessageFormat;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.event.Level;
+
+import net.sourceforge.pmd.util.StringUtil;
+import net.sourceforge.pmd.util.log.MessageReporter;
+
+/**
+ * Base implementation.
+ *
+ * @author ClΓ©ment Fournier
+ */
+abstract class MessageReporterBase implements MessageReporter {
+
+ private int numErrors;
+ private Level minLevel = Level.TRACE;
+
+ /**
+ * null level means off.
+ */
+ public final void setLevel(Level minLevel) {
+ this.minLevel = minLevel;
+ }
+
+ @Override
+ public final boolean isLoggable(Level level) {
+ return minLevel != null
+ && minLevel.compareTo(level) >= 0
+ && isLoggableImpl(level);
+ }
+
+ protected boolean isLoggableImpl(Level level) {
+ return true;
+ }
+
+ @Override
+ public void logEx(Level level, String message, Object[] formatArgs, Throwable error) {
+ if (isLoggable(level)) {
+ message = MessageFormat.format(message, formatArgs);
+ String errorMessage = error.getMessage();
+ if (errorMessage == null) {
+ errorMessage = error.getClass().getSimpleName();
+ }
+ errorMessage = StringUtil.quoteMessageFormat(errorMessage);
+ log(level, message + ": " + errorMessage);
+ if (isLoggable(Level.DEBUG)) {
+ String stackTrace = StringUtil.quoteMessageFormat(ExceptionUtils.getStackTrace(error));
+ log(Level.DEBUG, stackTrace);
+ }
+ }
+ }
+
+ @Override
+ public final void log(Level level, String message, Object... formatArgs) {
+ if (level == Level.ERROR) {
+ this.numErrors++;
+ }
+ if (isLoggable(level)) {
+ logImpl(level, message, formatArgs);
+ }
+ }
+
+ /**
+ * Perform logging assuming {@link #isLoggable(Level)} is true.
+ */
+ protected abstract void logImpl(Level level, String message, Object[] formatArgs);
+
+
+ @Override
+ public int numErrors() {
+ return numErrors;
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java
new file mode 100644
index 0000000000..98eb4a72a3
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java
@@ -0,0 +1,31 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.log.internal;
+
+import org.slf4j.event.Level;
+
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.util.log.MessageReporter;
+
+/**
+ * A logger that ignores all messages.
+ *
+ * @author ClΓ©ment Fournier
+ */
+@InternalApi
+public final class NoopReporter extends MessageReporterBase implements MessageReporter {
+
+ // note: not singleton because PmdLogger accumulates error count.
+
+ @Override
+ protected boolean isLoggableImpl(Level level) {
+ return false;
+ }
+
+ @Override
+ protected void logImpl(Level level, String message, Object[] formatArgs) {
+ // noop
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java
new file mode 100644
index 0000000000..672e40df98
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java
@@ -0,0 +1,36 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.util.log.internal;
+
+import org.slf4j.Logger;
+import org.slf4j.event.Level;
+
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.util.log.MessageReporter;
+
+/**
+ * A {@link Logger} (java.util) based logger impl.
+ *
+ * @author ClΓ©ment Fournier
+ */
+@InternalApi
+public class SimpleMessageReporter extends MessageReporterBase implements MessageReporter {
+
+ private final Logger backend;
+
+ public SimpleMessageReporter(Logger backend) {
+ this.backend = backend;
+ }
+
+ @Override
+ protected boolean isLoggableImpl(Level level) {
+ return backend.isEnabledForLevel(level);
+ }
+
+ @Override
+ protected void logImpl(Level level, String message, Object[] formatArgs) {
+ backend.atLevel(level).log(message, formatArgs);
+ }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
index 9df543dc00..923e0b970a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
@@ -26,7 +26,6 @@ import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
-import net.sourceforge.pmd.lang.ast.AstAnalysisContext;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.ast.Parser.ParserTask;
import net.sourceforge.pmd.lang.ast.RootNode;
@@ -185,20 +184,6 @@ public class TreeExportCli {
ParserTask task = new ParserTask(langVersion, filename, fullSource, SemanticErrorReporter.noop());
RootNode root = parser.parse(task);
- AstAnalysisContext ctx = new AstAnalysisContext() {
- @Override
- public ClassLoader getTypeResolutionClassLoader() {
- return getClass().getClassLoader();
- }
-
- @Override
- public LanguageVersion getLanguageVersion() {
- return langVersion;
- }
- };
-
- languageHandler.getProcessingStages().forEach(it -> it.processAST(root, ctx));
-
renderer.renderSubtree(root, System.out);
} finally {
source.close();
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java
new file mode 100644
index 0000000000..5c437fc1a9
--- /dev/null
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java
@@ -0,0 +1,75 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+
+import net.sourceforge.pmd.renderers.Renderer;
+
+/**
+ * @author ClΓ©ment Fournier
+ */
+public class PmdAnalysisTest {
+
+ @Test
+ public void testPmdAnalysisWithEmptyConfig() {
+ PMDConfiguration config = new PMDConfiguration();
+ try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ assertThat(pmd.files().getCollectedFiles(), empty());
+ assertThat(pmd.rulesets(), empty());
+ assertThat(pmd.renderers(), empty());
+ }
+ }
+
+ @Test
+ public void testRendererInteractions() throws IOException {
+ PMDConfiguration config = new PMDConfiguration();
+ config.setInputPaths("sample-source/dummy");
+ Renderer renderer = spy(Renderer.class);
+ try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ pmd.addRenderer(renderer);
+ verify(renderer, never()).start();
+ pmd.performAnalysis();
+ }
+
+ verify(renderer, times(1)).renderFileReport(ArgumentMatchers.any());
+ verify(renderer, times(1)).start();
+ verify(renderer, times(1)).end();
+ verify(renderer, times(1)).flush();
+ }
+
+ @Test
+ public void testRulesetLoading() {
+ PMDConfiguration config = new PMDConfiguration();
+ config.addRuleSet("rulesets/dummy/basic.xml");
+ try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ assertThat(pmd.rulesets(), hasSize(1));
+ }
+ }
+
+ @Test
+ public void testRulesetWhenSomeoneHasAnError() {
+ PMDConfiguration config = new PMDConfiguration();
+ config.addRuleSet("rulesets/dummy/basic.xml");
+ config.addRuleSet("rulesets/xxxe/notaruleset.xml");
+ try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ assertThat(pmd.rulesets(), hasSize(1)); // no failure
+ assertThat(pmd.getReporter().numErrors(), equalTo(1));
+ }
+ }
+
+}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java
similarity index 84%
rename from pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java
rename to pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java
index d765920004..99c321530f 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java
@@ -4,9 +4,13 @@
package net.sourceforge.pmd;
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -16,6 +20,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.Collections;
import java.util.Properties;
import org.junit.Assert;
@@ -29,7 +34,7 @@ import net.sourceforge.pmd.renderers.CSVRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.util.ClasspathClassLoader;
-public class ConfigurationTest {
+public class PmdConfigurationTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@@ -37,7 +42,7 @@ public class ConfigurationTest {
@Test
public void testSuppressMarker() {
PMDConfiguration configuration = new PMDConfiguration();
- assertEquals("Default suppress marker", PMD.SUPPRESS_MARKER, configuration.getSuppressMarker());
+ assertEquals("Default suppress marker", PMDConfiguration.DEFAULT_SUPPRESS_MARKER, configuration.getSuppressMarker());
configuration.setSuppressMarker("CUSTOM_MARKER");
assertEquals("Changed suppress marker", "CUSTOM_MARKER", configuration.getSuppressMarker());
}
@@ -51,10 +56,10 @@ public class ConfigurationTest {
}
@Test
- public void testClassLoader() throws IOException {
+ public void testClassLoader() {
PMDConfiguration configuration = new PMDConfiguration();
assertEquals("Default ClassLoader", PMDConfiguration.class.getClassLoader(), configuration.getClassLoader());
- configuration.prependClasspath("some.jar");
+ configuration.prependAuxClasspath("some.jar");
assertEquals("Prepended ClassLoader class", ClasspathClassLoader.class,
configuration.getClassLoader().getClass());
URL[] urls = ((ClasspathClassLoader) configuration.getClassLoader()).getURLs();
@@ -68,31 +73,31 @@ public class ConfigurationTest {
}
@Test
- public void auxClasspathWithRelativeFileEmpty() throws IOException {
+ public void auxClasspathWithRelativeFileEmpty() {
String relativeFilePath = "src/test/resources/net/sourceforge/pmd/cli/auxclasspath-empty.cp";
PMDConfiguration configuration = new PMDConfiguration();
- configuration.prependClasspath("file:" + relativeFilePath);
+ configuration.prependAuxClasspath("file:" + relativeFilePath);
URL[] urls = ((ClasspathClassLoader) configuration.getClassLoader()).getURLs();
Assert.assertEquals(0, urls.length);
}
@Test
- public void auxClasspathWithRelativeFileEmpty2() throws IOException {
+ public void auxClasspathWithRelativeFileEmpty2() {
String relativeFilePath = "./src/test/resources/net/sourceforge/pmd/cli/auxclasspath-empty.cp";
PMDConfiguration configuration = new PMDConfiguration();
- configuration.prependClasspath("file:" + relativeFilePath);
+ configuration.prependAuxClasspath("file:" + relativeFilePath);
URL[] urls = ((ClasspathClassLoader) configuration.getClassLoader()).getURLs();
Assert.assertEquals(0, urls.length);
}
@Test
- public void auxClasspathWithRelativeFile() throws IOException, URISyntaxException {
+ public void auxClasspathWithRelativeFile() throws URISyntaxException {
final String FILE_SCHEME = "file";
String currentWorkingDirectory = new File("").getAbsoluteFile().toURI().getPath();
String relativeFilePath = "src/test/resources/net/sourceforge/pmd/cli/auxclasspath.cp";
PMDConfiguration configuration = new PMDConfiguration();
- configuration.prependClasspath("file:" + relativeFilePath);
+ configuration.prependAuxClasspath("file:" + relativeFilePath);
URL[] urls = ((ClasspathClassLoader) configuration.getClassLoader()).getURLs();
URI[] uris = new URI[urls.length];
for (int i = 0; i < urls.length; i++) {
@@ -111,6 +116,30 @@ public class ConfigurationTest {
Assert.assertArrayEquals(expectedUris, uris);
}
+ @Test
+ public void testRuleSetsLegacy() {
+ PMDConfiguration configuration = new PMDConfiguration();
+ assertNull("Default RuleSets", configuration.getRuleSets());
+ configuration.setRuleSets("/rulesets/basic.xml");
+ assertEquals("Changed RuleSets", "/rulesets/basic.xml", configuration.getRuleSets());
+ configuration.setRuleSets((String) null);
+ assertNull(configuration.getRuleSets());
+ }
+
+ @Test
+ public void testRuleSets() {
+ PMDConfiguration configuration = new PMDConfiguration();
+ assertThat(configuration.getRuleSetPaths(), empty());
+ configuration.setRuleSets(listOf("/rulesets/basic.xml"));
+ assertEquals(listOf("/rulesets/basic.xml"), configuration.getRuleSetPaths());
+ configuration.addRuleSet("foo.xml");
+ assertEquals(listOf("/rulesets/basic.xml", "foo.xml"), configuration.getRuleSetPaths());
+ configuration.setRuleSets(Collections.emptyList());
+ assertThat(configuration.getRuleSetPaths(), empty());
+ // should be addable even though we set it to an unmodifiable empty list
+ configuration.addRuleSet("foo.xml");
+ assertEquals(listOf("foo.xml"), configuration.getRuleSetPaths());
+ }
@Test
public void testMinimumPriority() {
@@ -232,7 +261,7 @@ public class ConfigurationTest {
}
@Test
- public void testAnalysisCacheLocation() throws IOException {
+ public void testAnalysisCacheLocation() {
final PMDConfiguration configuration = new PMDConfiguration();
configuration.setAnalysisCacheLocation(null);
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
index c7bfe16b0c..81acee181d 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java
@@ -91,7 +91,7 @@ public class PMDTaskTest {
try (InputStream in = new FileInputStream("target/pmd-ant-test.txt")) {
String actual = IOUtils.toString(in, StandardCharsets.UTF_8);
// remove any trailing newline
- actual = actual.replaceAll("\n|\r", "");
+ actual = actual.trim();
Assert.assertEquals("sample.dummy:1:\tSampleXPathRule:\tTest Rule 2", actual);
}
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java
index 78bbb13f4f..a81cbb55b5 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/cache/FileAnalysisCacheTest.java
@@ -82,21 +82,21 @@ public class FileAnalysisCacheTest {
}
@Test
- public void testStoreCreatesFile() {
+ public void testStoreCreatesFile() throws Exception {
final FileAnalysisCache cache = new FileAnalysisCache(unexistingCacheFile);
cache.persist();
assertTrue("Cache file doesn't exist after store", unexistingCacheFile.exists());
}
@Test
- public void testStoreOnUnwritableFileShouldntThrow() {
+ public void testStoreOnUnwritableFileShouldntThrow() throws IOException {
emptyCacheFile.setWritable(false);
final FileAnalysisCache cache = new FileAnalysisCache(emptyCacheFile);
cache.persist();
}
@Test
- public void testStorePersistsFilesWithViolations() {
+ public void testStorePersistsFilesWithViolations() throws IOException {
final FileAnalysisCache cache = new FileAnalysisCache(newCacheFile);
cache.checkValidity(mock(RuleSets.class), mock(ClassLoader.class));
cache.isUpToDate(sourceFile);
@@ -107,7 +107,9 @@ public class FileAnalysisCacheTest {
when(rule.getLanguage()).thenReturn(mock(Language.class));
when(rv.getRule()).thenReturn(rule);
- cache.startFileAnalysis(mock(DataSource.class)).onRuleViolation(rv);
+ DataSource ds = mock(DataSource.class);
+ when(ds.getNiceFileName(false, "")).thenReturn(sourceFile.getPath());
+ cache.startFileAnalysis(ds).onRuleViolation(rv);
cache.persist();
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
@@ -120,7 +122,7 @@ public class FileAnalysisCacheTest {
}
@Test
- public void testCacheValidityWithNoChanges() {
+ public void testCacheValidityWithNoChanges() throws IOException {
final RuleSets rs = mock(RuleSets.class);
final ClassLoader cl = mock(ClassLoader.class);
@@ -150,7 +152,7 @@ public class FileAnalysisCacheTest {
}
@Test
- public void testRulesetChangeInvalidatesCache() {
+ public void testRulesetChangeInvalidatesCache() throws IOException {
final RuleSets rs = mock(RuleSets.class);
final ClassLoader cl = mock(ClassLoader.class);
@@ -367,7 +369,7 @@ public class FileAnalysisCacheTest {
}
private void setupCacheWithFiles(final File cacheFile, final RuleSets ruleSets,
- final ClassLoader classLoader, final File... files) {
+ final ClassLoader classLoader, final File... files) throws IOException {
// Setup a cache file with an entry for an empty Source.java with no violations
final FileAnalysisCache cache = new FileAnalysisCache(cacheFile);
cache.checkValidity(ruleSets, classLoader);
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
index ed7078f2af..47106eb626 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
@@ -5,6 +5,7 @@
package net.sourceforge.pmd.cli;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.containsStringIgnoringCase;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
@@ -19,6 +20,7 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import org.apache.commons.io.IOUtils;
import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.Before;
@@ -124,6 +126,19 @@ public class CoreCliTest {
assertTrue("Report file should have been created", Files.exists(reportFile));
}
+ @Test
+ public void testFileCollectionWithUnknownFiles() throws IOException {
+ Path reportFile = tempRoot().resolve("out/reportFile.txt");
+ Files.createFile(srcDir.resolve("foo.not_analysable"));
+ assertFalse("Report file should not exist", Files.exists(reportFile));
+
+ runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile, "--debug");
+
+ assertTrue("Report file should have been created", Files.exists(reportFile));
+ String reportText = IOUtils.toString(Files.newBufferedReader(reportFile, StandardCharsets.UTF_8));
+ assertThat(reportText, not(containsStringIgnoringCase("error")));
+ }
+
@Test
public void testNonExistentReportFileDeprecatedOptions() {
Path reportFile = tempRoot().resolve("out/reportFile.txt");
@@ -180,13 +195,13 @@ public class CoreCliTest {
@Test
public void debugLogging() {
runPmdSuccessfully("--debug", "--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET);
- errStreamCaptor.getLog().contains("[main] DEBUG net.sourceforge.pmd.PMD - Log level is at DEBUG");
+ assertThat(errStreamCaptor.getLog(), containsString("[main] INFO net.sourceforge.pmd.PMD - Log level is at TRACE"));
}
@Test
public void defaultLogging() {
runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET);
- errStreamCaptor.getLog().contains("[main] INFO net.sourceforge.pmd.PMD - Log level is at INFO");
+ assertThat(errStreamCaptor.getLog(), containsString("[main] INFO net.sourceforge.pmd.PMD - Log level is at INFO"));
}
@@ -240,8 +255,8 @@ public class CoreCliTest {
}
private static void runPmd(int expectedExitCode, Object[] args) {
- int actualExitCode = PMD.run(argsToString(args));
- assertEquals("Exit code", expectedExitCode, actualExitCode);
+ StatusCode actualExitCode = PMD.runPmd(argsToString(args));
+ assertEquals("Exit code", expectedExitCode, actualExitCode.toInt());
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java
index d93dbd738b..e0e5d2d8e6 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java
@@ -4,93 +4,85 @@
package net.sourceforge.pmd.cli;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.hasSize;
-import org.junit.Assert;
+import java.io.IOException;
+import java.util.List;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
-import net.sourceforge.pmd.lang.DummyLanguageModule;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.util.datasource.DataSource;
+import net.sourceforge.pmd.internal.util.FileCollectionUtil;
+import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
+import net.sourceforge.pmd.lang.document.FileCollector;
+import net.sourceforge.pmd.lang.document.TextFile;
+import net.sourceforge.pmd.util.log.internal.NoopReporter;
public class PMDFilelistTest {
+
+ private static @NonNull FileCollector newCollector() {
+ return FileCollector.newCollector(new LanguageVersionDiscoverer(), new NoopReporter());
+ }
+
@Test
public void testGetApplicableFiles() throws IOException {
- Set languages = new HashSet<>();
- languages.add(new DummyLanguageModule());
+ FileCollector collector = newCollector();
- PMDConfiguration configuration = new PMDConfiguration();
- configuration.setInputFilePath("src/test/resources/net/sourceforge/pmd/cli/filelist.txt");
+ FileCollectionUtil.collectFileList(collector, "src/test/resources/net/sourceforge/pmd/cli/filelist.txt");
- List applicableFiles = PMD.getApplicableFiles(configuration, languages);
- Assert.assertEquals(2, applicableFiles.size());
- Assert.assertTrue(applicableFiles.get(0).getNiceFileName(false, "").endsWith("somefile.dummy"));
- Assert.assertTrue(applicableFiles.get(1).getNiceFileName(false, "").endsWith("anotherfile.dummy"));
+ List applicableFiles = collector.getCollectedFiles();
+ assertThat(applicableFiles, hasSize(2));
+ assertThat(applicableFiles.get(0).getPathId(), endsWith("anotherfile.dummy"));
+ assertThat(applicableFiles.get(1).getPathId(), endsWith("somefile.dummy"));
}
@Test
public void testGetApplicableFilesMultipleLines() throws IOException {
- Set languages = new HashSet<>();
- languages.add(new DummyLanguageModule());
+ FileCollector collector = newCollector();
- PMDConfiguration configuration = new PMDConfiguration();
- configuration.setInputFilePath("src/test/resources/net/sourceforge/pmd/cli/filelist2.txt");
+ FileCollectionUtil.collectFileList(collector, "src/test/resources/net/sourceforge/pmd/cli/filelist2.txt");
- List applicableFiles = PMD.getApplicableFiles(configuration, languages);
- Assert.assertEquals(3, applicableFiles.size());
- Assert.assertTrue(applicableFiles.get(0).getNiceFileName(false, "").endsWith("somefile.dummy"));
- Assert.assertTrue(applicableFiles.get(1).getNiceFileName(false, "").endsWith("anotherfile.dummy"));
- Assert.assertTrue(applicableFiles.get(2).getNiceFileName(false, "").endsWith("somefile.dummy"));
+ List applicableFiles = collector.getCollectedFiles();
+ assertThat(applicableFiles, hasSize(3));
+ assertThat(applicableFiles.get(0).getPathId(), endsWith("anotherfile.dummy"));
+ assertThat(applicableFiles.get(1).getPathId(), endsWith("somefile.dummy"));
+ assertThat(applicableFiles.get(2).getPathId(), endsWith("somefile.dummy"));
}
@Test
- public void testGetApplicatbleFilesWithIgnores() throws IOException {
- Set languages = new HashSet<>();
- languages.add(new DummyLanguageModule());
+ public void testGetApplicableFilesWithIgnores() throws IOException {
+ FileCollector collector = newCollector();
PMDConfiguration configuration = new PMDConfiguration();
configuration.setInputFilePath("src/test/resources/net/sourceforge/pmd/cli/filelist3.txt");
configuration.setIgnoreFilePath("src/test/resources/net/sourceforge/pmd/cli/ignorelist.txt");
+ FileCollectionUtil.collectFiles(configuration, collector);
- List applicableFiles = PMD.getApplicableFiles(configuration, languages);
- Assert.assertEquals(2, applicableFiles.size());
- Assert.assertTrue(applicableFiles.get(0).getNiceFileName(false, "").endsWith("somefile2.dummy"));
- Assert.assertTrue(applicableFiles.get(1).getNiceFileName(false, "").endsWith("somefile4.dummy"));
+ List applicableFiles = collector.getCollectedFiles();
+ assertThat(applicableFiles, hasSize(2));
+ assertThat(applicableFiles.get(0).getPathId(), endsWith("somefile2.dummy"));
+ assertThat(applicableFiles.get(1).getPathId(), endsWith("somefile4.dummy"));
}
@Test
- public void testGetApplicatbleFilesWithDirAndIgnores() throws IOException {
- Set languages = new HashSet<>();
- languages.add(new DummyLanguageModule());
+ public void testGetApplicableFilesWithDirAndIgnores() throws IOException {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setInputPaths("src/test/resources/net/sourceforge/pmd/cli/src");
configuration.setIgnoreFilePath("src/test/resources/net/sourceforge/pmd/cli/ignorelist.txt");
- List applicableFiles = PMD.getApplicableFiles(configuration, languages);
- Assert.assertEquals(4, applicableFiles.size());
- Collections.sort(applicableFiles, new Comparator() {
- @Override
- public int compare(DataSource o1, DataSource o2) {
- if (o1 == null && o2 != null) {
- return -1;
- } else if (o1 != null && o2 == null) {
- return 1;
- } else {
- return o1.getNiceFileName(false, "").compareTo(o2.getNiceFileName(false, ""));
- }
- }
- });
- Assert.assertTrue(applicableFiles.get(0).getNiceFileName(false, "").endsWith("anotherfile.dummy"));
- Assert.assertTrue(applicableFiles.get(1).getNiceFileName(false, "").endsWith("somefile.dummy"));
- Assert.assertTrue(applicableFiles.get(2).getNiceFileName(false, "").endsWith("somefile2.dummy"));
- Assert.assertTrue(applicableFiles.get(3).getNiceFileName(false, "").endsWith("somefile4.dummy"));
+ FileCollector collector = newCollector();
+ FileCollectionUtil.collectFiles(configuration, collector);
+
+ List applicableFiles = collector.getCollectedFiles();
+ assertThat(applicableFiles, hasSize(4));
+ assertThat(applicableFiles.get(0).getPathId(), endsWith("anotherfile.dummy"));
+ assertThat(applicableFiles.get(1).getPathId(), endsWith("somefile.dummy"));
+ assertThat(applicableFiles.get(2).getPathId(), endsWith("somefile2.dummy"));
+ assertThat(applicableFiles.get(3).getPathId(), endsWith("somefile4.dummy"));
}
+
}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java
deleted file mode 100644
index 7d4d8595af..0000000000
--- a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.internal;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import net.sourceforge.pmd.PMDConfiguration;
-import net.sourceforge.pmd.Rule;
-import net.sourceforge.pmd.RuleContext;
-import net.sourceforge.pmd.RuleSet;
-import net.sourceforge.pmd.RuleSets;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
-import net.sourceforge.pmd.lang.ast.DummyAstStages;
-import net.sourceforge.pmd.lang.ast.DummyNode;
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.ast.Parser;
-import net.sourceforge.pmd.lang.ast.Parser.ParserTask;
-import net.sourceforge.pmd.lang.ast.RootNode;
-import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
-import net.sourceforge.pmd.lang.rule.AbstractRule;
-
-public class StageDependencyTest {
-
- private final LanguageVersion version = LanguageRegistry.findLanguageByTerseName("dummy").getVersion("1.0");
-
- private DummyNode process(String source, RuleSets ruleSets) {
- return process(source, ruleSets, new RulesetStageDependencyHelper(new PMDConfiguration()));
- }
-
- private DummyNode process(String source, RuleSets ruleSets, RulesetStageDependencyHelper helper) {
-
- // TODO Handle Rules having different parser options.
-
- Parser parser = version.getLanguageVersionHandler().getParser();
-
- ParserTask task = new ParserTask(version, "dummyfile.dummy", source, SemanticErrorReporter.noop());
-
- RootNode rootNode = parser.parse(task);
-
- helper.runLanguageSpecificStages(ruleSets, version, rootNode);
-
- return (DummyNode) rootNode;
- }
-
-
- @Test
- public void testSimpleDependency() {
-
- DummyNode root = process("foo bar", withRules(new PredicateTestRule(DummyAstStages.FOO)));
-
- Assert.assertTrue(DummyAstStages.FOO.hasProcessed(root));
- Assert.assertFalse(DummyAstStages.BAR.hasProcessed(root));
- }
-
- @Test
- public void testNoDependency() {
-
- DummyNode root = process("foo bar", withRules(new PredicateTestRule()));
-
- Assert.assertFalse(DummyAstStages.FOO.hasProcessed(root));
- Assert.assertFalse(DummyAstStages.BAR.hasProcessed(root));
- }
-
- @Test
- public void testDependencyUnion() {
-
- DummyNode root =
- process("foo bar",
- withRules(
- new PredicateTestRule(DummyAstStages.FOO),
- new PredicateTestRule(DummyAstStages.BAR)
- )
- );
-
- Assert.assertTrue(DummyAstStages.FOO.hasProcessed(root));
- Assert.assertTrue(DummyAstStages.BAR.hasProcessed(root));
- }
-
- @Test
- public void testTransitiveDependency() {
-
- DummyNode root = process("foo bar", withRules(new PredicateTestRule(DummyAstStages.RUNS_FOO)));
-
- Assert.assertTrue(DummyAstStages.FOO.hasProcessed(root));
- Assert.assertFalse(DummyAstStages.BAR.hasProcessed(root));
- Assert.assertTrue(DummyAstStages.RUNS_FOO.hasProcessed(root));
- }
-
- @Test
- public void testNoRecomputation() {
-
- PMDConfiguration configuration = new PMDConfiguration();
- RulesetStageDependencyHelper helper = new RulesetStageDependencyHelper(configuration);
-
- RuleSets ruleSets = withRules(new PredicateTestRule(DummyAstStages.RUNS_FOO));
-
- List> stages1 = helper.testOnlyGetDependencies(ruleSets, version);
-
- process("foo bar", ruleSets);
-
- List> stages2 = helper.testOnlyGetDependencies(ruleSets, version);
-
- Assert.assertSame(stages1, stages2);
- }
-
- @Test
- public void testDependencyOrdering() {
-
- PMDConfiguration configuration = new PMDConfiguration();
- RulesetStageDependencyHelper helper = new RulesetStageDependencyHelper(configuration);
-
- RuleSets ruleSets = withRules(
- new PredicateTestRule(DummyAstStages.FOO),
- new PredicateTestRule(DummyAstStages.BAR)
- );
-
- RuleSets ruleSets2 = withRules(
- new PredicateTestRule(DummyAstStages.BAR),
- new PredicateTestRule(DummyAstStages.FOO)
- );
-
- List> stages1 = helper.testOnlyGetDependencies(ruleSets, version);
- List> stages2 = helper.testOnlyGetDependencies(ruleSets2, version);
-
- Assert.assertNotSame(stages1, stages2);
- Assert.assertEquals(stages1, stages2);
- }
-
-
- private static RuleSets withRules(Rule r, Rule... rs) {
- List rsets = new ArrayList<>();
- rsets.add(RuleSet.forSingleRule(r));
- for (Rule rule : rs) {
- rsets.add(RuleSet.forSingleRule(rule));
- }
-
- return new RuleSets(rsets);
- }
-
- private static class PredicateTestRule extends AbstractRule {
-
- private final List dependencies;
-
- PredicateTestRule(DummyAstStages... dependencies) {
- this.dependencies = Arrays.asList(dependencies);
- }
-
- @Override
- public Language getLanguage() {
- return LanguageRegistry.findLanguageByTerseName("dummy");
- }
-
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- return dependencies.contains(stage);
- }
-
- @Override
- public void apply(Node target, RuleContext ctx) {
-
- }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public boolean equals(Object o) {
- return this == o;
- }
- }
-
-
-}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java
index 55ff4de166..3b9d4f6d72 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java
@@ -8,7 +8,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
-import net.sourceforge.pmd.lang.ast.DummyAstStages;
import net.sourceforge.pmd.lang.ast.DummyRoot;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.Parser;
@@ -38,9 +37,6 @@ public class DummyLanguageModule extends BaseLanguageModule {
}
public static class Handler extends AbstractPmdLanguageVersionHandler {
- public Handler() {
- super(DummyAstStages.class);
- }
@Override
public RuleViolationFactory getRuleViolationFactory() {
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyAstStages.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyAstStages.java
deleted file mode 100644
index 1644db9433..0000000000
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyAstStages.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.ast;
-
-import java.util.Collections;
-import java.util.List;
-
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-
-/**
- * @author ClΓ©ment Fournier
- */
-public enum DummyAstStages implements AstProcessingStage {
- FOO,
- BAR,
- RUNS_FOO {
- @Override
- public List getDependencies() {
- return Collections.singletonList(FOO);
- }
- };
-
-
- @Override
- public Language getLanguage() {
- return LanguageRegistry.findLanguageByTerseName("dummy");
- }
-
- @Override
- public List getDependencies() {
- return Collections.emptyList();
- }
-
- public boolean hasProcessed(DummyNode node) {
- return node.getUserData().containsKey(name() + "_STAGE");
- }
-
- @Override
- public String getDisplayName() {
- return name();
- }
-
- @Override
- public void processAST(RootNode rootNode, AstAnalysisContext configuration) {
- ((DummyNode) rootNode).getUserData().put(name() + "_STAGE", "done");
- }
-
-}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java
new file mode 100644
index 0000000000..191c004db0
--- /dev/null
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java
@@ -0,0 +1,144 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.LanguageRegistry;
+import net.sourceforge.pmd.lang.LanguageVersion;
+import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
+
+/**
+ * @author ClΓ©ment Fournier
+ */
+public class FileCollectorTest {
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Test
+ public void testAddFile() throws IOException {
+ Path root = tempFolder.getRoot().toPath();
+ Path foo = newFile(root, "foo.dummy");
+ Path bar = newFile(root, "bar.unknown");
+
+ FileCollector collector = newCollector();
+
+ assertTrue("should be dummy language", collector.addFile(foo));
+ assertFalse("should be unknown language", collector.addFile(bar));
+
+ assertCollected(collector, listOf("foo.dummy"));
+ }
+
+ @Test
+ public void testAddFileForceLanguage() throws IOException {
+ Path root = tempFolder.getRoot().toPath();
+ Path bar = newFile(root, "bar.unknown");
+
+ Language dummy = LanguageRegistry.findLanguageByTerseName("dummy");
+
+ FileCollector collector = newCollector(dummy.getDefaultVersion());
+
+ assertTrue("should be unknown language", collector.addFile(bar, dummy));
+ assertCollected(collector, listOf("bar.unknown"));
+ assertNoErrors(collector);
+ }
+
+ @Test
+ public void testAddFileNotExists() {
+ Path root = tempFolder.getRoot().toPath();
+
+ FileCollector collector = newCollector();
+
+ assertFalse(collector.addFile(root.resolve("does_not_exist.dummy")));
+ assertEquals(1, collector.getReporter().numErrors());
+ }
+
+ @Test
+ public void testAddFileNotAFile() throws IOException {
+ Path root = tempFolder.getRoot().toPath();
+ Path dir = root.resolve("src");
+ Files.createDirectories(dir);
+
+ FileCollector collector = newCollector();
+ assertFalse(collector.addFile(dir));
+ assertEquals(1, collector.getReporter().numErrors());
+ }
+
+ @Test
+ public void testAddDirectory() throws IOException {
+ Path root = tempFolder.getRoot().toPath();
+ newFile(root, "src/foo.dummy");
+ newFile(root, "src/bar.unknown");
+ newFile(root, "src/x/bar.dummy");
+
+ FileCollector collector = newCollector();
+
+ collector.addDirectory(root.resolve("src"));
+
+ assertCollected(collector, listOf("src/foo.dummy", "src/x/bar.dummy"));
+ }
+
+ @Test
+ public void testRelativize() throws IOException {
+ String displayName = FileCollector.getDisplayName(Paths.get("a", "b", "c"), listOf(Paths.get("a").toString()));
+ assertEquals(displayName, Paths.get("b", "c").toString());
+ }
+
+ private Path newFile(Path root, String path) throws IOException {
+ Path resolved = root.resolve(path);
+ Files.createDirectories(resolved.getParent());
+ Files.createFile(resolved);
+ return resolved;
+ }
+
+ private void assertCollected(FileCollector collector, List relPaths) {
+ Map actual = new LinkedHashMap<>();
+ for (TextFile file : collector.getCollectedFiles()) {
+ actual.put(file.getDisplayName(), file.getLanguageVersion().getTerseName());
+ }
+
+ relPaths = new ArrayList<>(relPaths);
+ for (int i = 0; i < relPaths.size(); i++) {
+ // normalize, we want display names to be platform-specific
+ relPaths.set(i, relPaths.get(i).replace('/', File.separatorChar));
+ }
+
+ assertEquals(relPaths, new ArrayList<>(actual.keySet()));
+ }
+
+ private void assertNoErrors(FileCollector collector) {
+ assertEquals("No errors expected", 0, collector.getReporter().numErrors());
+ }
+
+ private FileCollector newCollector() {
+ return newCollector(null);
+ }
+
+ private FileCollector newCollector(LanguageVersion forcedVersion) {
+ LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(forcedVersion);
+ FileCollector collector = FileCollector.newCollector(discoverer, new TestMessageReporter());
+ collector.relativizeWith(tempFolder.getRoot().getAbsolutePath());
+ return collector;
+ }
+}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TestMessageReporter.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TestMessageReporter.java
new file mode 100644
index 0000000000..8689530e30
--- /dev/null
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TestMessageReporter.java
@@ -0,0 +1,23 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.document;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter;
+
+/**
+ * @author ClΓ©ment Fournier
+ */
+public class TestMessageReporter extends SimpleMessageReporter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TestMessageReporter.class.getName());
+
+ public TestMessageReporter() {
+ super(LOG);
+ setLevel(null);
+ }
+}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java
index 6d749e5775..d10f676ca5 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java
@@ -4,7 +4,6 @@
package net.sourceforge.pmd.processor;
-import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
@@ -13,15 +12,13 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import java.util.List;
-
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.Test;
import org.mockito.Mockito;
import net.sourceforge.pmd.FooRule;
-import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
+import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
@@ -31,20 +28,11 @@ import net.sourceforge.pmd.lang.ast.FileAnalysisException;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener.ViolationCounterListener;
-import net.sourceforge.pmd.util.datasource.DataSource;
public class GlobalListenerTest {
static final int NUM_DATA_SOURCES = 3;
- static List mockDataSources() {
- return listOf(
- DataSource.forString("abc", "fname1.dummy"),
- DataSource.forString("abcd", "fname2.dummy"),
- DataSource.forString("abcd", "fname21.dummy")
- );
- }
-
@Test
public void testViolationCounter() throws Exception {
@@ -89,7 +77,7 @@ public class GlobalListenerTest {
runPmd(config, GlobalAnalysisListener.noop(), rule);
verify(mockCache).checkValidity(any(), any());
- verify(mockCache).persist();
+ verify(mockCache, times(1)).persist();
verify(mockCache, times(NUM_DATA_SOURCES)).isUpToDate(any());
}
@@ -105,7 +93,7 @@ public class GlobalListenerTest {
// cache methods are called regardless
verify(mockCache).checkValidity(any(), any());
- verify(mockCache).persist();
+ verify(mockCache, times(1)).persist();
verify(mockCache, times(NUM_DATA_SOURCES)).isUpToDate(any());
}
@@ -127,7 +115,7 @@ public class GlobalListenerTest {
// cache methods are called regardless
verify(mockCache).checkValidity(any(), any());
- verify(mockCache).persist();
+ verify(mockCache, times(1)).persist();
verify(mockCache, times(1)).isUpToDate(any());
}
@@ -140,16 +128,14 @@ public class GlobalListenerTest {
return config;
}
- private void runPmd(PMDConfiguration config, GlobalAnalysisListener listener, Rule rule) throws Exception {
- try {
- PMD.processFiles(
- config,
- listOf(RuleSet.forSingleRule(rule)),
- mockDataSources(),
- listener
- );
- } finally {
- listener.close();
+ private void runPmd(PMDConfiguration config, GlobalAnalysisListener listener, Rule rule) {
+ try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
+ pmd.addRuleSet(RuleSet.forSingleRule(rule));
+ pmd.files().addSourceFile("fname1.dummy", "abc");
+ pmd.files().addSourceFile("fname2.dummy", "abcd");
+ pmd.files().addSourceFile("fname21.dummy", "abcd");
+ pmd.addListener(listener);
+ pmd.performAnalysis();
}
}
@@ -159,7 +145,7 @@ public class GlobalListenerTest {
@Override
public void apply(Node node, RuleContext ctx) {
if (node.getAstInfo().getFileName().contains("1")) {
- addViolation(ctx, node);
+ ctx.addViolation(node);
}
}
}
diff --git a/pmd-core/src/test/resources/sample-source/dummy/foo.dummy b/pmd-core/src/test/resources/sample-source/dummy/foo.dummy
new file mode 100644
index 0000000000..4b1d189a0f
--- /dev/null
+++ b/pmd-core/src/test/resources/sample-source/dummy/foo.dummy
@@ -0,0 +1 @@
+A dummy file.
diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
index 890185cd17..5ad517cf14 100644
--- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
+++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java
@@ -108,7 +108,7 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest {
result = PMDExecutor.runPMD(tempDir, "-d", srcDir, "-R", "src/test/resources/rulesets/sample-ruleset.xml",
"-r", folder.newFile().toString(), "--debug");
result.assertExecutionResult(4);
- result.assertErrorOutputContains("[main] DEBUG net.sourceforge.pmd.PMD - Log level is at DEBUG");
+ result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.PMD - Log level is at TRACE");
}
@Test
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java
index d698822019..6a4fe24939 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstDisambiguationPass.java
@@ -6,18 +6,17 @@
package net.sourceforge.pmd.lang.java.ast;
import static net.sourceforge.pmd.lang.java.symbols.table.internal.JavaSemanticErrors.CANNOT_RESOLVE_AMBIGUOUS_NAME;
-import static net.sourceforge.pmd.lang.java.symbols.table.internal.JavaSemanticErrors.CANNOT_RESOLVE_SYMBOL;
import java.util.Iterator;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
+import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.NodeStream;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
-import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable;
@@ -57,7 +56,7 @@ final class AstDisambiguationPass {
*/
public static void disambigWithCtx(NodeStream extends JavaNode> nodes, ReferenceCtx ctx) {
assert ctx != null : "Null context";
- JavaAstProcessor.bench("AST disambiguation", () -> nodes.forEach(it -> it.acceptVisitor(DisambigVisitor.INSTANCE, ctx)));
+ TimeTracker.bench("AST disambiguation", () -> nodes.forEach(it -> it.acceptVisitor(DisambigVisitor.INSTANCE, ctx)));
}
@@ -196,7 +195,7 @@ final class AstDisambiguationPass {
final JTypeMirror resolved = ctx.resolveSingleTypeName(type.getSymbolTable(), type.getSimpleName(), type);
JTypeDeclSymbol sym;
if (resolved == null) {
- ctx.getLogger().warning(type, CANNOT_RESOLVE_SYMBOL, type.getSimpleName());
+ ctx.reportCannotResolveSymbol(type, type.getSimpleName());
sym = setArity(type, ctx, type.getSimpleName());
} else {
sym = resolved.getSymbol();
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java
index c4c84e7b81..3d70e291c3 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java
@@ -11,6 +11,7 @@ import net.sourceforge.pmd.lang.ast.impl.javacc.JavaCharStream;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter;
import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker;
+import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
/**
* Adapter for the JavaParser, using the specified grammar version.
@@ -21,9 +22,11 @@ import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker;
public class JavaParser extends JjtreeParserAdapter {
private final LanguageLevelChecker> checker;
+ private final boolean postProcess;
- public JavaParser(LanguageLevelChecker> checker) {
+ public JavaParser(LanguageLevelChecker> checker, boolean postProcess) {
this.checker = checker;
+ this.postProcess = postProcess;
}
@@ -44,9 +47,17 @@ public class JavaParser extends JjtreeParserAdapter {
parser.setJdkVersion(checker.getJdkVersion());
parser.setPreview(checker.isPreviewEnabled());
- ASTCompilationUnit acu = parser.CompilationUnit();
- acu.setAstInfo(new AstInfo<>(task, acu, parser.getSuppressMap()));
- checker.check(acu);
- return acu;
+ ASTCompilationUnit root = parser.CompilationUnit();
+ root.setAstInfo(new AstInfo<>(task, root, parser.getSuppressMap()));
+ checker.check(root);
+
+ if (postProcess) {
+ JavaAstProcessor processor = JavaAstProcessor.create(task.getAuxclasspathClassLoader(),
+ task.getLanguageVersion(),
+ task.getReporter());
+ processor.process(root);
+ }
+
+ return root;
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java
index b5c95310bc..335d3c8a53 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java
@@ -4,23 +4,23 @@
package net.sourceforge.pmd.lang.java.internal;
+import static net.sourceforge.pmd.lang.java.symbols.table.internal.JavaSemanticErrors.CANNOT_RESOLVE_SYMBOL;
+
import java.util.IdentityHashMap;
import java.util.Locale;
import java.util.Map;
-import java.util.function.Supplier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.event.Level;
import net.sourceforge.pmd.benchmark.TimeTracker;
-import net.sourceforge.pmd.benchmark.TimedOperation;
-import net.sourceforge.pmd.benchmark.TimedOperationCategory;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.ast.NodeStream;
import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.InternalApiBridge;
+import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.symbols.SymbolResolver;
@@ -43,8 +43,6 @@ import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.Ve
*/
public final class JavaAstProcessor {
- private static final Logger DEFAULT_LOG = LoggerFactory.getLogger(JavaAstProcessor.class);
-
private static final Map TYPE_SYSTEMS = new IdentityHashMap<>();
private static final Level INFERENCE_LOG_LEVEL;
@@ -100,10 +98,31 @@ public final class JavaAstProcessor {
}
- public JClassSymbol findSymbolCannotFail(String name) {
- JClassSymbol found = getSymResolver().resolveClassFromCanonicalName(name);
- return found == null ? makeUnresolvedReference(name, 0)
- : found;
+ /**
+ * Find a symbol from the auxclasspath. If not found, will create
+ * an unresolved symbol.
+ */
+ public @NonNull JClassSymbol findSymbolCannotFail(String name) {
+ return findSymbolCannotFail(null, name);
+ }
+
+ /**
+ * Find a symbol from the auxclasspath. If not found, will create
+ * an unresolved symbol, and may report the failure if the location is non-null.
+ */
+ public @NonNull JClassSymbol findSymbolCannotFail(@Nullable JavaNode location, String canoName) {
+ JClassSymbol found = getSymResolver().resolveClassFromCanonicalName(canoName);
+ if (found == null) {
+ if (location != null) {
+ reportCannotResolveSymbol(location, canoName);
+ }
+ return makeUnresolvedReference(canoName, 0);
+ }
+ return found;
+ }
+
+ public void reportCannotResolveSymbol(@NonNull JavaNode location, String canoName) {
+ getLogger().warning(location, CANNOT_RESOLVE_SYMBOL, canoName);
}
public JClassSymbol makeUnresolvedReference(String canonicalName, int typeArity) {
@@ -138,7 +157,7 @@ public final class JavaAstProcessor {
*/
public void process(ASTCompilationUnit acu) {
- SymbolResolver knownSyms = bench("Symbol resolution", () -> SymbolResolutionPass.traverse(this, acu));
+ SymbolResolver knownSyms = TimeTracker.bench("1. Symbol resolution", () -> SymbolResolutionPass.traverse(this, acu));
// Now symbols are on the relevant nodes
this.symResolver = SymbolResolver.layer(knownSyms, this.symResolver);
@@ -147,21 +166,17 @@ public final class JavaAstProcessor {
// as scopes depend on type resolution in some cases.
InternalApiBridge.initTypeResolver(acu, this, typeInferenceLogger);
- bench("2. Symbol table resolution", () -> SymbolTableResolver.traverse(this, acu));
- bench("3. AST disambiguation", () -> InternalApiBridge.disambigWithCtx(NodeStream.of(acu), ReferenceCtx.root(this, acu)));
- bench("4. Comment assignment", () -> InternalApiBridge.assignComments(acu));
- bench("5. Usage resolution", () -> InternalApiBridge.usageResolution(this, acu));
- bench("6. Override resolution", () -> InternalApiBridge.overrideResolution(this, acu));
+ TimeTracker.bench("2. Symbol table resolution", () -> SymbolTableResolver.traverse(this, acu));
+ TimeTracker.bench("3. AST disambiguation", () -> InternalApiBridge.disambigWithCtx(NodeStream.of(acu), ReferenceCtx.root(this, acu)));
+ TimeTracker.bench("4. Comment assignment", () -> InternalApiBridge.assignComments(acu));
+ TimeTracker.bench("5. Usage resolution", () -> InternalApiBridge.usageResolution(this, acu));
+ TimeTracker.bench("6. Override resolution", () -> InternalApiBridge.overrideResolution(this, acu));
}
public TypeSystem getTypeSystem() {
return typeSystem;
}
- public static SemanticErrorReporter defaultLogger() {
- return SemanticErrorReporter.reportToLogger(DEFAULT_LOG);
- }
-
public static JavaAstProcessor create(SymbolResolver symResolver,
TypeSystem typeSystem,
LanguageVersion languageVersion,
@@ -191,6 +206,13 @@ public final class JavaAstProcessor {
);
}
+
+ public static JavaAstProcessor create(ClassLoader classLoader,
+ LanguageVersion languageVersion,
+ SemanticErrorReporter logger) {
+ return create(classLoader, languageVersion, logger, defaultTypeInfLogger());
+ }
+
public static JavaAstProcessor create(TypeSystem typeSystem,
LanguageVersion languageVersion,
SemanticErrorReporter semanticLogger,
@@ -204,15 +226,4 @@ public final class JavaAstProcessor {
);
}
- public static void bench(String label, Runnable runnable) {
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.LANGUAGE_SPECIFIC_PROCESSING, label)) {
- runnable.run();
- }
- }
-
- public static T bench(String label, Supplier runnable) {
- try (TimedOperation ignored = TimeTracker.startOperation(TimedOperationCategory.LANGUAGE_SPECIFIC_PROCESSING, label)) {
- return runnable.get();
- }
- }
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java
index c90973e476..8052595681 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java
@@ -50,7 +50,6 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler {
}
public JavaLanguageHandler(int jdkVersion, boolean preview) {
- super(JavaProcessingStage.class);
this.levelChecker = new LanguageLevelChecker<>(jdkVersion, preview, ReportingStrategy.reporterThatThrows());
}
@@ -60,7 +59,11 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler {
@Override
public Parser getParser() {
- return new JavaParser(levelChecker);
+ return new JavaParser(levelChecker, true);
+ }
+
+ public JavaParser getParserWithoutProcessing() {
+ return new JavaParser(levelChecker, false);
}
@Override
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java
deleted file mode 100644
index 4c03dff3c0..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.internal;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstAnalysisContext;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
-import net.sourceforge.pmd.lang.ast.RootNode;
-import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
-import net.sourceforge.pmd.lang.java.ast.JavaParser;
-import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker;
-import net.sourceforge.pmd.lang.java.symboltable.SymbolFacade;
-
-
-/**
- * Java processing stages.
- *
- * @author ClΓ©ment Fournier
- * @since 7.0.0
- */
-@Experimental
-public enum JavaProcessingStage implements AstProcessingStage {
-
- /**
- * This acts as a merged stage, non-optional. Ideally this would be encapsulated
- * in the {@link JavaParser}, like the {@link LanguageLevelChecker}.
- */
- JAVA_PROCESSING("Java processing") {
- @Override
- public void processAST(RootNode rootNode, AstAnalysisContext configuration) {
- JavaAstProcessor.create(configuration.getTypeResolutionClassLoader(), configuration.getLanguageVersion(), JavaAstProcessor.defaultLogger(), JavaAstProcessor.defaultTypeInfLogger())
- .process((ASTCompilationUnit) rootNode);
- }
- },
-
- /**
- * Symbol table analysis.
- */
- SYMBOL_RESOLUTION("Symbol table") {
- @Override
- public void processAST(RootNode rootNode, AstAnalysisContext configuration) {
- // kept for compatibility with existing tests
- new SymbolFacade().initializeWith(configuration.getTypeResolutionClassLoader(), (ASTCompilationUnit) rootNode);
- }
- };
-
- private final String displayName;
- private final List dependencies;
-
- JavaProcessingStage(String displayName, JavaProcessingStage... dependencies) {
- this.displayName = displayName;
- this.dependencies = Collections.unmodifiableList(Arrays.asList(dependencies));
- }
-
- @Override
- public List getDependencies() {
- return dependencies;
- }
-
-
- @Override
- public String getDisplayName() {
- return displayName;
- }
-
-
- @Override
- public final Language getLanguage() {
- return LanguageRegistry.findLanguageByTerseName("java");
- }
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java
index 73efb9c94e..0da448c81b 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java
@@ -10,13 +10,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor;
-import net.sourceforge.pmd.lang.java.internal.JavaProcessingStage;
import net.sourceforge.pmd.lang.rule.AbstractRule;
@@ -52,12 +50,4 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse
return false;
}
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- if (!(stage instanceof JavaProcessingStage)) {
- throw new IllegalArgumentException("Processing stage wasn't a Java one: " + stage);
- }
-
- return true;
- }
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java
index 063f1613c8..800b67c249 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/ReferenceCtx.java
@@ -46,6 +46,10 @@ public final class ReferenceCtx {
this.enclosingClass = enclosingClass;
}
+ public void reportCannotResolveSymbol(JavaNode location, String simpleName) {
+ processor.reportCannotResolveSymbol(location, simpleName);
+ }
+
public static ReferenceCtx root(JavaAstProcessor processor, ASTCompilationUnit root) {
return new ReferenceCtx(processor, root.getPackageName(), null);
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java
index 069e659158..46ffb2b987 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java
@@ -257,7 +257,7 @@ final class SymTableFactory {
if (!anImport.isStatic()) {
// Single-Type-Import Declaration
- JClassSymbol type = processor.findSymbolCannotFail(anImport.getImportedName());
+ JClassSymbol type = processor.findSymbolCannotFail(anImport, anImport.getImportedName());
importedTypes.append(type.getTypeSystem().typeOf(type, false));
}
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java
index 4de7893d34..4e5ef18906 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java
@@ -9,11 +9,11 @@ import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSuppressed;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
-public class ExcludeLinesTest extends BaseNonParserTest {
+public class ExcludeLinesTest extends BaseParserTest {
@Test
public void testAcceptance() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java
index 24a56156da..baa95be954 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java
@@ -17,7 +17,7 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class ReportTest {
- private final JavaParsingHelper java = JavaParsingHelper.WITH_PROCESSING;
+ private final JavaParsingHelper java = JavaParsingHelper.DEFAULT;
@Test
public void testBasic() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java
index 892f1b82ae..016f208fd6 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java
@@ -4,20 +4,18 @@
package net.sourceforge.pmd.cli;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.util.regex.Pattern;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import org.junit.AfterClass;
-import org.junit.Assert;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.rules.TestRule;
+import net.sourceforge.pmd.PMD.StatusCode;
import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
-import net.sourceforge.pmd.util.FileUtil;
/**
* @author Romain Pelisse <belaran@gmail.com>
@@ -35,64 +33,65 @@ public class CLITest extends BaseCLITest {
Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(null);
}
+ @Before
+ public void setupLogging() {
+ Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(null);
+ }
+
+
@Test
public void minimalArgs() {
- String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/bestpractices.xml,category/java/design.xml", };
- runTest(args, "minimalArgs");
+ runTest("-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/bestpractices.xml,category/java/design.xml");
}
@Test
public void minimumPriority() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-min", "1", };
- runTest(args, "minimumPriority");
+ runTest(args);
}
@Test
public void usingDebug() {
- String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-debug", };
- runTest(args, "minimalArgsWithDebug");
+ runTest("-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-debug");
}
@Test
public void usingDebugLongOption() {
- String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "--debug", };
- runTest(args, "minimalArgsWithDebug");
+ runTest("-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "--debug");
}
@Test
public void changeJavaVersion() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-version", "1.5", "-language",
- "java", "-debug", };
- String resultFilename = runTest(args, "chgJavaVersion");
- assertTrue("Invalid Java version",
- FileUtil.findPatternInFile(new File(resultFilename), "Using Java version: Java 1.5"));
+ "java", "--debug", };
+ String log = runTest(args);
+ assertThat(log, containsPattern("Adding file .*\\.java \\(lang: java 1\\.5\\)"));
}
@Test
public void exitStatusNoViolations() {
- String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", };
- runTest(args, "exitStatusNoViolations");
+ runTest("-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml");
}
@Test
public void exitStatusWithViolations() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", };
- String resultFilename = runTest(args, "exitStatusWithViolations", 4);
- assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if"));
+ String log = runTest(StatusCode.VIOLATIONS_FOUND, args);
+ assertThat(log, containsString("Avoid empty if"));
}
@Test
public void exitStatusWithViolationsAndWithoutFailOnViolations() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", "-failOnViolation", "false", };
- String resultFilename = runTest(args, "exitStatusWithViolationsAndWithoutFailOnViolations", 0);
- assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if"));
+ String log = runTest(StatusCode.OK, args);
+ assertThat(log, containsString("Avoid empty if"));
}
@Test
public void exitStatusWithViolationsAndWithoutFailOnViolationsLongOption() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", "--fail-on-violation", "false", };
- String resultFilename = runTest(args, "exitStatusWithViolationsAndWithoutFailOnViolations", 0);
- assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if"));
+ String log = runTest(StatusCode.OK, args);
+ assertThat(log, containsString("Avoid empty if"));
}
/**
@@ -101,12 +100,9 @@ public class CLITest extends BaseCLITest {
@Test
public void testWrongRuleset() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml", };
- String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt";
- createTestOutputFile(filename);
- runPMDWith(args);
- Assert.assertEquals(1, getStatusCode());
- assertTrue(FileUtil.findPatternInFile(new File(filename),
- "Can't find resource 'category/java/designn.xml' for rule 'null'." + " Make sure the resource is a valid file"));
+ String log = runTest(StatusCode.ERROR, args);
+ assertThat(log, containsString("Can't find resource 'category/java/designn.xml' for rule 'null'."
+ + " Make sure the resource is a valid file"));
}
/**
@@ -115,12 +111,9 @@ public class CLITest extends BaseCLITest {
@Test
public void testWrongRulesetWithRulename() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml/UseCollectionIsEmpty", };
- String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt";
- createTestOutputFile(filename);
- runPMDWith(args);
- Assert.assertEquals(1, getStatusCode());
- assertTrue(FileUtil.findPatternInFile(new File(filename),
- "Can't find resource 'category/java/designn.xml' for rule " + "'UseCollectionIsEmpty'."));
+ String log = runTest(StatusCode.ERROR, args);
+ assertThat(log, containsString("Can't find resource 'category/java/designn.xml' for rule "
+ + "'UseCollectionIsEmpty'."));
}
/**
@@ -129,11 +122,8 @@ public class CLITest extends BaseCLITest {
@Test
public void testWrongRulename() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml/ThisRuleDoesNotExist", };
- String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt";
- createTestOutputFile(filename);
- runPMDWith(args);
- Assert.assertEquals(1, getStatusCode());
- assertTrue(FileUtil.findPatternInFile(new File(filename), Pattern
- .quote("No rules found. Maybe you misspelled a rule name?" + " (category/java/design.xml/ThisRuleDoesNotExist)")));
+ String log = runTest(StatusCode.OK, args);
+ assertThat(log, containsString("No rules found. Maybe you misspelled a rule name?"
+ + " (category/java/design.xml/ThisRuleDoesNotExist)"));
}
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/BaseNonParserTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/BaseParserTest.java
similarity index 66%
rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/BaseNonParserTest.java
rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/BaseParserTest.java
index 67ee741d4a..a42dc34154 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/BaseNonParserTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/BaseParserTest.java
@@ -1,13 +1,9 @@
-/**
+/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
-package net.sourceforge.pmd.lang.java.symboltable;
+package net.sourceforge.pmd.lang.java;
-import java.util.List;
-
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.java.JavaParsingHelper;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
@@ -15,20 +11,18 @@ import net.sourceforge.pmd.lang.java.ast.ASTExpression;
* Base class for tests that usually need processing stages to run when
* parsing code.
*/
-public abstract class BaseNonParserTest {
+public abstract class BaseParserTest {
- protected final JavaParsingHelper java = JavaParsingHelper.WITH_PROCESSING.withResourceContext(getClass());
+ protected final JavaParsingHelper java = JavaParsingHelper.DEFAULT.withResourceContext(getClass());
protected final JavaParsingHelper java5 = java.withDefaultVersion("1.5");
+ protected final JavaParsingHelper java8 = java.withDefaultVersion("1.8");
+ protected final JavaParsingHelper java9 = java.withDefaultVersion("9");
protected ASTCompilationUnit parseCode(final String code) {
return java.parse(code);
}
- protected List getOrderedNodes(Class target, String code) {
- return JavaParsingHelper.WITH_PROCESSING.getNodes(target, code);
- }
-
/**
* Parse and return an expression. Some variables are predeclared.
*/
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java
index e7b507aeff..3e9a884637 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java
@@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.java;
+import static net.sourceforge.pmd.lang.ast.Parser.ParserTask;
+
import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -13,19 +15,17 @@ import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
-import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
-import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.LanguageVersionHandler;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
import net.sourceforge.pmd.lang.ast.SemanticException;
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
+import net.sourceforge.pmd.lang.java.ast.JavaParser;
import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor;
+import net.sourceforge.pmd.lang.java.internal.JavaLanguageHandler;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger;
import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.SimpleLogger;
@@ -42,11 +42,13 @@ public class JavaParsingHelper extends BaseParsingHelper ops = java.getNodes(ASTFormalParameter.class, TEST1, "1.5");
- for (ASTFormalParameter b : ops) {
- ASTVariableDeclaratorId variableDeclId = b.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
- if (!"x".equals(variableDeclId.getImage())) {
- assertTrue(b.isVarargs());
- nrOfVarArgs++;
- } else {
- assertFalse(b.isVarargs());
- nrOfNoVarArgs++;
- }
- }
-
- // Ensure that both possibilities are tested
- assertEquals(1, nrOfVarArgs);
- assertEquals(1, nrOfNoVarArgs);
- }
-
- private static final String TEST1 = "class Foo {\n void bar(int x, int... others) {}\n}";
-}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
index 7547f00363..070170b319 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
@@ -12,6 +12,7 @@ import java.util.List;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.ParseException;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
public class ASTImportDeclarationTest extends BaseParserTest {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
index f333179d1b..cbb3527f99 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
@@ -6,12 +6,13 @@ package net.sourceforge.pmd.lang.java.ast;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class ASTInitializerTest extends BaseParserTest {
@Test
public void testDontCrashOnBlockStatement() {
- java.parse(TEST1);
+ java.parse("public class Foo { { x = 5; } }");
}
- private static final String TEST1 = "public class Foo {\n {\n x = 5;\n }\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
index 0216dab627..4be1ebb900 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
@@ -12,18 +12,18 @@ import java.util.List;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class ASTModuleDeclarationTest extends BaseParserTest {
@Test
public final void jdk9ModuleInfo() {
ASTCompilationUnit ast = java9.parseResource("jdkversiontests/jdk9_module_info.java");
- List modules = ast.findDescendantsOfType(ASTModuleDeclaration.class);
- assertEquals(1, modules.size());
- ASTModuleDeclaration module = modules.get(0);
+ ASTModuleDeclaration module = ast.descendants(ASTModuleDeclaration.class).firstOrThrow();
assertTrue(module.isOpen());
assertEquals("com.example.foo", module.getImage());
assertEquals(7, module.getNumChildren());
- List directives = module.findChildrenOfType(ASTModuleDirective.class);
+ List directives = module.children(ASTModuleDirective.class).toList();
assertEquals(7, directives.size());
// requires com.example.foo.http;
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
index f9152ec1ba..5fa767ab6f 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
@@ -8,6 +8,8 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class ASTPackageDeclarationTest extends BaseParserTest {
private static final String PACKAGE_INFO_ANNOTATED = "@Deprecated\npackage net.sourceforge.pmd.foobar;\n";
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
index abf5bb6c6c..72c8447bdb 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
@@ -11,11 +11,13 @@ import java.util.List;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class ASTSwitchLabelTest extends BaseParserTest {
@Test
public void testDefaultOff() {
- List ops = java.getNodes(ASTSwitchLabel.class, TEST1);
+ List ops = java.getNodes(ASTSwitchLabel.class, "public class Foo {\n void bar() {\n switch (x) {\n case 1: y = 2;\n }\n }\n}");
assertFalse(ops.get(0).isDefault());
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchStatementTest.java
index 037de44c5b..f512d16415 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchStatementTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchStatementTest.java
@@ -7,14 +7,16 @@ package net.sourceforge.pmd.lang.java.ast;
import org.junit.Assert;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class ASTSwitchStatementTest extends BaseParserTest {
@Test
public void exhaustiveEnumSwitchWithDefault() {
- ASTSwitchStatement switchStatement = getNodes(ASTSwitchStatement.class,
+ ASTSwitchStatement switchStatement = java.parse(
"import java.nio.file.AccessMode; class Foo { void bar(AccessMode m) {"
+ "switch (m) { case READ: break; default: break; } } }")
- .get(0);
+ .descendants(ASTSwitchStatement.class).firstOrThrow();
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch()); // this should not throw a NPE...
Assert.assertTrue(switchStatement.hasDefaultCase());
Assert.assertTrue(switchStatement.isFallthroughSwitch());
@@ -22,10 +24,9 @@ public class ASTSwitchStatementTest extends BaseParserTest {
@Test
public void defaultCaseWithArrowBlock() {
- ASTSwitchStatement switchStatement = getNodes(ASTSwitchStatement.class,
- "class Foo { void bar(int x) {"
- + "switch (x) { default -> { } } } }")
- .get(0);
+ ASTSwitchStatement switchStatement =
+ java.parse("class Foo { void bar(int x) {switch (x) { default -> { } } } }")
+ .descendants(ASTSwitchStatement.class).firstOrThrow();
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertTrue(switchStatement.iterator().hasNext());
Assert.assertTrue(switchStatement.hasDefaultCase());
@@ -34,10 +35,9 @@ public class ASTSwitchStatementTest extends BaseParserTest {
@Test
public void emptySwitch() {
- ASTSwitchStatement switchStatement = getNodes(ASTSwitchStatement.class,
- "class Foo { void bar(int x) {"
- + "switch (x) { } } }")
- .get(0);
+ ASTSwitchStatement switchStatement =
+ java.parse("class Foo { void bar(int x) {switch (x) { } } }")
+ .descendants(ASTSwitchStatement.class).firstOrThrow();
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertFalse(switchStatement.iterator().hasNext());
Assert.assertFalse(switchStatement.hasDefaultCase());
@@ -47,7 +47,7 @@ public class ASTSwitchStatementTest extends BaseParserTest {
@Test
public void defaultCaseWithArrowExprs() {
ASTSwitchStatement switchStatement =
- getNodes(ASTSwitchStatement.class,
+ java.parse(
"import net.sourceforge.pmd.lang.java.rule.bestpractices.switchstmtsshouldhavedefault.SimpleEnum;\n"
+ "\n"
+ " public class Foo {\n"
@@ -59,7 +59,7 @@ public class ASTSwitchStatementTest extends BaseParserTest {
+ " }\n"
+ " }\n"
+ " }")
- .get(0);
+ .descendants(ASTSwitchStatement.class).firstOrThrow();
Assert.assertFalse(switchStatement.isExhaustiveEnumSwitch());
Assert.assertTrue(switchStatement.iterator().hasNext());
Assert.assertFalse(switchStatement.isFallthroughSwitch());
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
index c9259722b5..2fe8636e5b 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
@@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
public class ASTVariableDeclaratorIdTest extends BaseParserTest {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/BaseParserTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/BaseParserTest.java
deleted file mode 100644
index 3901cd87b0..0000000000
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/BaseParserTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.ast;
-
-import java.util.List;
-
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.java.JavaParsingHelper;
-
-/**
- * Base class for tests that usually need processing stages to run when
- * parsing code.
- */
-public abstract class BaseParserTest {
-
- protected final JavaParsingHelper java = JavaParsingHelper.JUST_PARSE.withResourceContext(getClass());
- protected final JavaParsingHelper java5 = java.withDefaultVersion("1.5");
- protected final JavaParsingHelper java8 = java.withDefaultVersion("1.8");
- protected final JavaParsingHelper java9 = java.withDefaultVersion("9");
-
-
- protected ASTCompilationUnit parseCode(final String code) {
- return java.parse(code);
- }
-
- protected List getNodes(Class target, String code) {
- return JavaParsingHelper.WITH_PROCESSING.getNodes(target, code);
- }
-}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentAssignmentTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentAssignmentTest.java
index 7508ce63e4..604bf2d010 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentAssignmentTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentAssignmentTest.java
@@ -13,9 +13,9 @@ import java.util.List;
import org.junit.Assert;
import org.junit.Test;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
-public class CommentAssignmentTest extends BaseNonParserTest {
+public class CommentAssignmentTest extends BaseParserTest {
/**
* Blank lines in comments should not raise an exception. See bug #1048.
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentTest.java
index 70e381897e..9b603ee958 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/CommentTest.java
@@ -9,9 +9,9 @@ import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
-public class CommentTest extends BaseNonParserTest {
+public class CommentTest extends BaseParserTest {
@Test
public void testMultiLinesInSingleLine() {
String comment = "/* single line. */";
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ConstantExpressionsTests.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ConstantExpressionsTests.java
index 318c693a83..2138855725 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ConstantExpressionsTests.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ConstantExpressionsTests.java
@@ -11,9 +11,9 @@ import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
-public class ConstantExpressionsTests extends BaseNonParserTest {
+public class ConstantExpressionsTests extends BaseParserTest {
private Executable isConst(String expr, Object value) {
return () -> {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/EncodingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/EncodingTest.java
index bc4ced4c2a..a4b04a4c25 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/EncodingTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/EncodingTest.java
@@ -8,14 +8,15 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class EncodingTest extends BaseParserTest {
@Test
public void testDecodingOfUTF8() {
- ASTCompilationUnit acu = java.parse(TEST_UTF8);
- String methodName = acu.getFirstDescendantOfType(ASTMethodDeclaration.class).getImage();
+ ASTCompilationUnit acu = java.parse("class Foo { void Γ©() {} }");
+ String methodName = acu.descendants(ASTMethodDeclaration.class).firstOrThrow().getName();
assertEquals("Γ©", methodName);
}
- private static final String TEST_UTF8 = "class Foo {\n void Γ©() {}\n void fiddle() {}\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FormalCommentTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FormalCommentTest.java
index 5c8f8b11da..7e0642b6c9 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FormalCommentTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FormalCommentTest.java
@@ -10,6 +10,7 @@ import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
public class FormalCommentTest extends BaseParserTest {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java
index 82fcdcc54e..832f51e87c 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java
@@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class JDKVersionTest {
- private final JavaParsingHelper java3 = JavaParsingHelper.JUST_PARSE
+ private final JavaParsingHelper java3 = JavaParsingHelper.DEFAULT
.withDefaultVersion("1.3")
.withResourceContext(JDKVersionTest.class, "jdkversiontests/");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java10Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java10Test.java
index 9f9086f0aa..ec7780e573 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java10Test.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java10Test.java
@@ -26,8 +26,8 @@ import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
public class Java10Test {
private final JavaParsingHelper java10 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("10")
- .withResourceContext(Java10Test.class, "jdkversiontests/java10/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("10")
+ .withResourceContext(Java10Test.class, "jdkversiontests/java10/");
private final JavaParsingHelper java9 = java10.withDefaultVersion("9");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java14Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java14Test.java
index 0dfaa82373..836f0de1e3 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java14Test.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java14Test.java
@@ -24,8 +24,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
@Ignore("Needs to be fixed for new AST structure. All of this is already much better tested in Kotlin, I don't want to port these tests...")
public class Java14Test {
private final JavaParsingHelper java14 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("14")
- .withResourceContext(Java14Test.class, "jdkversiontests/java14/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("14")
+ .withResourceContext(Java14Test.class, "jdkversiontests/java14/");
private final JavaParsingHelper java13 = java14.withDefaultVersion("13");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java
index ecb6cdbdb6..20df93fd03 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java
@@ -13,8 +13,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java15TreeDumpTest extends BaseJavaTreeDumpTest {
private final JavaParsingHelper java15 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15")
- .withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java15/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("15")
+ .withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java15/");
private final JavaParsingHelper java14 = java15.withDefaultVersion("14");
@Override
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16PreviewTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16PreviewTreeDumpTest.java
index 67c86b8817..858b597ffb 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16PreviewTreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16PreviewTreeDumpTest.java
@@ -18,8 +18,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java16PreviewTreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java16p =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16-preview")
- .withResourceContext(Java16PreviewTreeDumpTest.class, "jdkversiontests/java16p/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("16-preview")
+ .withResourceContext(Java16PreviewTreeDumpTest.class, "jdkversiontests/java16p/");
private final JavaParsingHelper java16 = java16p.withDefaultVersion("16");
public Java16PreviewTreeDumpTest() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java
index 001a187131..7c852d2a53 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java
@@ -18,8 +18,8 @@ import net.sourceforge.pmd.lang.java.types.JPrimitiveType;
public class Java16TreeDumpTest extends BaseJavaTreeDumpTest {
private final JavaParsingHelper java16 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16")
- .withResourceContext(Java16TreeDumpTest.class, "jdkversiontests/java16/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("16")
+ .withResourceContext(Java16TreeDumpTest.class, "jdkversiontests/java16/");
private final JavaParsingHelper java16p = java16.withDefaultVersion("16-preview");
private final JavaParsingHelper java15 = java16.withDefaultVersion("15");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java
index 0e1e2eb5c9..86d198fc52 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java
@@ -19,8 +19,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java17PreviewTreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java17p =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17-preview")
- .withResourceContext(Java17PreviewTreeDumpTest.class, "jdkversiontests/java17p/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("17-preview")
+ .withResourceContext(Java17PreviewTreeDumpTest.class, "jdkversiontests/java17p/");
private final JavaParsingHelper java17 = java17p.withDefaultVersion("17");
public Java17PreviewTreeDumpTest() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java
index 96c4390e0f..19356ba1fa 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java
@@ -16,8 +16,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java17TreeDumpTest extends BaseTreeDumpTest {
private final JavaParsingHelper java17 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17")
- .withResourceContext(Java17TreeDumpTest.class, "jdkversiontests/java17/");
+ JavaParsingHelper.DEFAULT.withDefaultVersion("17")
+ .withResourceContext(Java17TreeDumpTest.class, "jdkversiontests/java17/");
private final JavaParsingHelper java17p = java17.withDefaultVersion("17-preview");
private final JavaParsingHelper java16 = java17.withDefaultVersion("16");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java8Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java8Test.java
index 96064e75dd..c86f124f8f 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java8Test.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java8Test.java
@@ -10,8 +10,8 @@ import net.sourceforge.pmd.lang.java.JavaParsingHelper;
public class Java8Test {
private final JavaParsingHelper java8 =
- JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("8")
- .withResourceContext(Java8Test.class);
+ JavaParsingHelper.DEFAULT.withDefaultVersion("8")
+ .withResourceContext(Java8Test.class);
@Test
public void interfaceMethodShouldBeParseable() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java
index a55fe2d048..3494b6eb00 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java
@@ -24,7 +24,7 @@ public class JavaQualifiedNameTest {
private List getNodes(Class target, String code) {
- return JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15").getNodes(target, code);
+ return JavaParsingHelper.DEFAULT.withDefaultVersion("15").getNodes(target, code);
}
@Test
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java
index ce36be02e5..3a6161dbc8 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java
@@ -23,7 +23,7 @@ import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
public class ParserCornersTest extends BaseJavaTreeDumpTest {
- private final JavaParsingHelper java = JavaParsingHelper.WITH_PROCESSING.withResourceContext(getClass());
+ private final JavaParsingHelper java = JavaParsingHelper.DEFAULT.withResourceContext(getClass());
private final JavaParsingHelper java4 = java.withDefaultVersion("1.4");
private final JavaParsingHelper java5 = java.withDefaultVersion("1.5");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java
index 08077df94a..f13a357ae9 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java
@@ -17,7 +17,7 @@ import org.junit.Test;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.test.TestUtilsKt;
-import net.sourceforge.pmd.lang.java.JavaParsingHelper;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
@Ignore("This test is Java specific even though parts of it should apply to any language implementation")
// The Java specific parts depend on the grammar and are subject to breaking during the grammar update process
@@ -174,7 +174,7 @@ public class SimpleNodeTest extends BaseParserTest {
@Test
public void testParentMethods() {
- ASTCompilationUnit u = JavaParsingHelper.JUST_PARSE.parse(TEST1);
+ ASTCompilationUnit u = java.parse(TEST1);
ASTMethodDeclarator d = u.getFirstDescendantOfType(ASTMethodDeclarator.class);
assertSame("getFirstParentOfType ASTMethodDeclaration", d.getParent(),
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java
index cdedc88f8e..63f30f4510 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/TextBlockEscapeTest.java
@@ -8,6 +8,8 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
+
public class TextBlockEscapeTest extends BaseParserTest {
@Test
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/internal/PrettyPrintingUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/internal/PrettyPrintingUtilTest.java
index fbb158d5d1..aaa272bf1c 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/internal/PrettyPrintingUtilTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/internal/PrettyPrintingUtilTest.java
@@ -17,13 +17,13 @@ import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.util.StringUtil;
-public class PrettyPrintingUtilTest extends BaseNonParserTest {
+public class PrettyPrintingUtilTest extends BaseParserTest {
@Test
public void displaySignatureTestWithExtraDimensions() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java
index 42e4b873b5..74f6f024e9 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java
@@ -24,7 +24,7 @@ import net.sourceforge.pmd.lang.metrics.Metric;
*/
public class JavaMetricsProviderTest {
- private final JavaParsingHelper java8 = JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("1.8");
+ private final JavaParsingHelper java8 = JavaParsingHelper.DEFAULT.withDefaultVersion("1.8");
@Test
public void testComputeAllMetrics() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/MetricsMemoizationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/MetricsMemoizationTest.java
index 8000b1952c..667cbf673f 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/MetricsMemoizationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/MetricsMemoizationTest.java
@@ -15,12 +15,12 @@ import java.util.Random;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaVisitorBase;
import net.sourceforge.pmd.lang.java.metrics.testdata.MetricsVisitorTestData;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.lang.metrics.Metric;
import net.sourceforge.pmd.lang.metrics.MetricOptions;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
@@ -28,7 +28,7 @@ import net.sourceforge.pmd.lang.metrics.MetricsUtil;
/**
* @author ClΓ©ment Fournier
*/
-public class MetricsMemoizationTest extends BaseNonParserTest {
+public class MetricsMemoizationTest extends BaseParserTest {
private final Metric randomMetric = randomMetric();
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java
index 494d91ffdb..1515e37f58 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java
@@ -42,7 +42,7 @@ public class JavaRuleViolationTest {
}
private ASTCompilationUnit parse(final String code) {
- return JavaParsingHelper.WITH_PROCESSING.parse(code);
+ return JavaParsingHelper.DEFAULT.parse(code);
}
/**
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java
index dcf626bd79..ef00d1de9d 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java
@@ -126,7 +126,7 @@ public class XPathRuleTest extends RuleTst {
@Test
public void testFollowingSibling() throws Exception {
final String source = "public interface dummy extends Foo, Bar, Baz {}";
- ASTCompilationUnit cu = JavaParsingHelper.WITH_PROCESSING.parse(source);
+ ASTCompilationUnit cu = JavaParsingHelper.DEFAULT.parse(source);
String xpath = "//ExtendsList/ClassOrInterfaceType/following-sibling::ClassOrInterfaceType";
@@ -143,7 +143,7 @@ public class XPathRuleTest extends RuleTst {
}
private static Report getReportForTestString(Rule r, String test) {
- return JavaParsingHelper.WITH_PROCESSING.executeRule(r, test);
+ return JavaParsingHelper.DEFAULT.executeRule(r, test);
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessParenthesesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessParenthesesTest.java
index 25c3814242..3e1bf44e0d 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessParenthesesTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessParenthesesTest.java
@@ -19,7 +19,7 @@ public class UselessParenthesesTest extends PmdRuleTst {
Executable testImpl(String expression, Necessity necessity) {
return () -> {
String file = "class Foo {{ int a,b,c,d; float f1, f2, f3; String s; Object e = " + expression + ";}}";
- ASTCompilationUnit acu = JavaParsingHelper.WITH_PROCESSING.parse(file);
+ ASTCompilationUnit acu = JavaParsingHelper.DEFAULT.parse(file);
ASTExpression paren = acu.descendants(ASTExpression.class).crossFindBoundaries().first(ASTExpression::isParenthesized);
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtilTest.java
index 75d856dd94..0f3652301c 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtilTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtilTest.java
@@ -14,12 +14,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression;
import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
-public class JavaRuleUtilTest extends BaseNonParserTest {
+public class JavaRuleUtilTest extends BaseParserTest {
@Test
public void testCamelCaseWords() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java
index 4ea93fdb7f..b0e8901217 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java
@@ -18,8 +18,8 @@ import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.ast.FileAnalysisException;
import net.sourceforge.pmd.lang.ast.test.TestUtilsKt;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
@@ -28,7 +28,7 @@ import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
* @author ClΓ©ment Fournier
* @since 7.0.0
*/
-public class BaseXPathFunctionTest extends BaseNonParserTest {
+public class BaseXPathFunctionTest extends BaseParserTest {
private static final String VIOLATION_MESSAGE = "violation";
private static final String RULE_NAME_PLACEHOLDER = "$rule_name";
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/AbruptCompletionTests.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/AbruptCompletionTests.java
index c0d993aea9..048e702359 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/AbruptCompletionTests.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/AbruptCompletionTests.java
@@ -13,16 +13,16 @@ import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import junit.framework.AssertionFailedError;
-public class AbruptCompletionTests extends BaseNonParserTest {
+public class AbruptCompletionTests extends BaseParserTest {
private final JavaParsingHelper java17 = java.withDefaultVersion("17");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsTests.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsTests.java
index b5ade11b72..3183aab108 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsTests.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/table/internal/PatternBindingsTests.java
@@ -16,15 +16,15 @@ import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.symbols.table.internal.PatternBindingsUtil.BindSet;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.util.CollectionUtil;
-public class PatternBindingsTests extends BaseNonParserTest {
+public class PatternBindingsTests extends BaseParserTest {
private final JavaParsingHelper java15p = java.withDefaultVersion("17");
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/AcceptanceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/AcceptanceTest.java
index 5caf3c9f4d..acc562f48a 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/AcceptanceTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/AcceptanceTest.java
@@ -17,6 +17,7 @@ import org.junit.Test;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTCatchClause;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
@@ -30,7 +31,7 @@ import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.lang.symboltable.Scope;
@Ignore
-public class AcceptanceTest extends BaseNonParserTest {
+public class AcceptanceTest extends BaseParserTest {
@Test
public void testClashingSymbols() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ClassScopeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ClassScopeTest.java
index c7cda7ce2d..6351fe0c35 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ClassScopeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ClassScopeTest.java
@@ -17,6 +17,7 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
@@ -32,7 +33,7 @@ import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@Ignore
-public class ClassScopeTest extends BaseNonParserTest {
+public class ClassScopeTest extends BaseParserTest {
@Test
public void testEnumsClassScope() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/GlobalScopeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/GlobalScopeTest.java
index 42a631f05e..f80b59cdec 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/GlobalScopeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/GlobalScopeTest.java
@@ -13,13 +13,14 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.lang.symboltable.Scope;
@Ignore
-public class GlobalScopeTest extends BaseNonParserTest {
+public class GlobalScopeTest extends BaseParserTest {
@Test
public void testClassDeclAppears() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/LocalScopeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/LocalScopeTest.java
index a7a41a0eff..83630f99bc 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/LocalScopeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/LocalScopeTest.java
@@ -13,6 +13,7 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
@@ -21,7 +22,7 @@ import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@Ignore
-public class LocalScopeTest extends BaseNonParserTest {
+public class LocalScopeTest extends BaseParserTest {
@Test
public void testLocalVariableDeclarationFound() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodNameDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodNameDeclarationTest.java
index 48b3543079..828a5f972f 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodNameDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodNameDeclarationTest.java
@@ -14,13 +14,14 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@Ignore
-public class MethodNameDeclarationTest extends BaseNonParserTest {
+public class MethodNameDeclarationTest extends BaseParserTest {
@Test
public void testEquality() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodScopeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodScopeTest.java
index 34b6ccb5c6..0ca2f77957 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodScopeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/MethodScopeTest.java
@@ -13,13 +13,14 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@Ignore
-public class MethodScopeTest extends BaseNonParserTest {
+public class MethodScopeTest extends BaseParserTest {
@Test
public void testMethodParameterOccurrenceRecorded() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/NameOccurrencesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/NameOccurrencesTest.java
index e2ac16f4f9..01d0a58caa 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/NameOccurrencesTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/NameOccurrencesTest.java
@@ -14,11 +14,12 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
@Ignore
-public class NameOccurrencesTest extends BaseNonParserTest {
+public class NameOccurrencesTest extends BaseParserTest {
@Test
public void testSuper() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeAndDeclarationFinderTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeAndDeclarationFinderTest.java
index 58ec72fc41..4bd3abb318 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeAndDeclarationFinderTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeAndDeclarationFinderTest.java
@@ -12,6 +12,7 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
@@ -21,7 +22,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
@Ignore
-public class ScopeAndDeclarationFinderTest extends BaseNonParserTest {
+public class ScopeAndDeclarationFinderTest extends BaseParserTest {
/**
* Unit test for https://sourceforge.net/p/pmd/bugs/1317/
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeCreationVisitorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeCreationVisitorTest.java
index 193c660243..d748cf2883 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeCreationVisitorTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/ScopeCreationVisitorTest.java
@@ -10,12 +10,13 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
@Ignore
-public class ScopeCreationVisitorTest extends BaseNonParserTest {
+public class ScopeCreationVisitorTest extends BaseParserTest {
@Test
public void testScopesAreCreated() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/SourceFileScopeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/SourceFileScopeTest.java
index 609d7fa4aa..befb8bb121 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/SourceFileScopeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/SourceFileScopeTest.java
@@ -15,12 +15,13 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@Ignore
-public class SourceFileScopeTest extends BaseNonParserTest {
+public class SourceFileScopeTest extends BaseParserTest {
@Test
public void testClassDeclAppears() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclarationTest.java
index c205e5f492..b682ba0add 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclarationTest.java
@@ -15,13 +15,14 @@ import org.junit.Ignore;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.Scope;
@Ignore
-public class VariableNameDeclarationTest extends BaseNonParserTest {
+public class VariableNameDeclarationTest extends BaseParserTest {
@Test
public void testConstructor() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java
index bbf100514c..08c0232ec2 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java
@@ -20,7 +20,7 @@ public class GenericMethodReferenceTest {
@Test
public void typeResolveVariable() {
- ASTCompilationUnit root = JavaParsingHelper.WITH_PROCESSING.parseClass(GenericMethodReference.class);
+ ASTCompilationUnit root = JavaParsingHelper.DEFAULT.parseClass(GenericMethodReference.class);
root.descendants(ASTVariableDeclaratorId.class).forEach(variable -> {
Assert.assertTrue(variable.getName().startsWith("supplier"));
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java
index 626a0f6206..598d27eec3 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java
@@ -11,12 +11,12 @@ import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.ast.InvocationNode;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
-public class InvocationMatcherTest extends BaseNonParserTest {
+public class InvocationMatcherTest extends BaseParserTest {
@Test
public void testSimpleMatcher() {
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java
index fc98af1276..72ead82d26 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java
@@ -14,6 +14,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import net.sourceforge.pmd.lang.java.BaseParserTest;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnonymousClassDeclaration;
@@ -23,10 +24,9 @@ import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTType;
import net.sourceforge.pmd.lang.java.ast.TypeNode;
-import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.lang.java.types.testdata.SomeClassWithAnon;
-public class TypeTestUtilTest extends BaseNonParserTest {
+public class TypeTestUtilTest extends BaseParserTest {
@Rule
public final ExpectedException expect = ExpectedException.none();
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
index 78c7214dcf..6f1180e1fb 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
@@ -31,7 +31,7 @@ public class TypesTreeDumpTest extends BaseTreeDumpTest {
@Override
public @NonNull BaseParsingHelper, ?> getParser() {
- return JavaParsingHelper.WITH_PROCESSING.withResourceContext(getClass());
+ return JavaParsingHelper.DEFAULT.withResourceContext(getClass());
}
@Test
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt
index 331930a0e7..3dba0caf2a 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt
@@ -219,6 +219,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getFormalParameters shouldBe formalsList(1) {
child {
+ it::isVarargs shouldBe true
it::getModifiers shouldBe modifiers {
annotation("Oha")
}
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt
index fa1a6e342a..31d1eb30ad 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt
@@ -36,7 +36,7 @@ enum class JavaVersion : Comparable {
val pmdVersion get() = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(pmdName)
- val parser: JavaParsingHelper = WITH_PROCESSING.withDefaultVersion(pmdName)
+ val parser: JavaParsingHelper = DEFAULT.withDefaultVersion(pmdName)
operator fun not(): List = values().toList() - this
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TestUtilitiesForTypes.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TestUtilitiesForTypes.kt
index 955274f357..6dd913262b 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TestUtilitiesForTypes.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TestUtilitiesForTypes.kt
@@ -28,7 +28,7 @@ import kotlin.test.assertTrue
is done lazily.
*/
-val javaParser: JavaParsingHelper = JavaParsingHelper.WITH_PROCESSING
+val javaParser: JavaParsingHelper = JavaParsingHelper.DEFAULT
val testTypeSystem: TypeSystem get() = JavaParsingHelper.TEST_TYPE_SYSTEM
diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java
index a051f56a6f..718995de62 100644
--- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java
+++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cli/CLITest.java
@@ -4,14 +4,10 @@
package net.sourceforge.pmd.cli;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
+import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.Test;
-import net.sourceforge.pmd.util.FileUtil;
-
/**
* @author Romain Pelisse <belaran@gmail.com>
*
@@ -20,9 +16,8 @@ public class CLITest extends BaseCLITest {
@Test
public void useEcmaScript() {
String[] args = { "-d", SOURCE_FOLDER, "-f", "xml", "-R", "ecmascript-basic", "-l",
- "ecmascript", "-debug", };
- String resultFilename = runTest(args, "useEcmaScript");
- assertTrue("Invalid JavaScript version",
- FileUtil.findPatternInFile(new File(resultFilename), "Using Ecmascript version: Ecmascript ES6"));
+ "ecmascript", "--debug", };
+ String log = runTest(args);
+ assertThat(log, containsPattern("Adding file .*\\.js \\(lang: ecmascript ES6\\)"));
}
}
diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsParsingHelper.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsParsingHelper.java
index 055891146a..ac28fc7203 100644
--- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsParsingHelper.java
+++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/JsParsingHelper.java
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule;
public final class JsParsingHelper extends BaseParsingHelper {
- public static final JsParsingHelper DEFAULT = new JsParsingHelper(Params.getDefaultProcess());
+ public static final JsParsingHelper DEFAULT = new JsParsingHelper(Params.getDefault());
private JsParsingHelper(Params params) {
super(EcmascriptLanguageModule.NAME, ASTAstRoot.class, params);
diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/JspParsingHelper.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/JspParsingHelper.java
index bc647b37c2..46110e66bc 100644
--- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/JspParsingHelper.java
+++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/JspParsingHelper.java
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.jsp.JspLanguageModule;
public final class JspParsingHelper extends BaseParsingHelper {
- public static final JspParsingHelper DEFAULT = new JspParsingHelper(Params.getDefaultProcess());
+ public static final JspParsingHelper DEFAULT = new JspParsingHelper(Params.getDefault());
private JspParsingHelper(Params params) {
super(JspLanguageModule.NAME, ASTCompilationUnit.class, params);
diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt
index e5c8a96be5..268bcd9745 100644
--- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt
+++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt
@@ -38,10 +38,7 @@ abstract class BaseParsingHelper, T : RootNode
companion object {
@JvmStatic
- val defaultNoProcess = Params(false, null, null, "")
-
- @JvmStatic
- val defaultProcess = Params(true, null, null, "")
+ val default = Params(true, null, null, "")
}
}
@@ -124,7 +121,6 @@ abstract class BaseParsingHelper, T : RootNode
): T {
val lversion = if (version == null) defaultVersion else getVersion(version)
val handler = lversion.languageVersionHandler
- val parser = handler.parser
val source = DataSource.forString(sourceCode, fileName)
val toString = DataSource.readToString(source, StandardCharsets.UTF_8) // this removed the BOM
val task = Parser.ParserTask(lversion, fileName, toString, SemanticErrorReporter.noop())
@@ -132,35 +128,12 @@ abstract class BaseParsingHelper, T : RootNode
handler.declareParserTaskProperties(it)
it.setProperty(Parser.ParserTask.COMMENT_MARKER, params.suppressMarker)
}
- val rootNode = rootClass.cast(parser.parse(task))
- if (params.doProcess) {
- postProcessing(handler, lversion, rootNode)
- }
- return rootNode
+ return doParse(params, task)
}
- /**
- * Select the processing stages that this should run in [postProcessing],
- * by default runs everything.
- */
- protected open fun selectProcessingStages(handler: LanguageVersionHandler): List> =
- handler.processingStages
-
- /**
- * Called only if [Params.doProcess] is true.
- */
- protected open fun postProcessing(handler: LanguageVersionHandler, lversion: LanguageVersion, rootNode: T) {
- val astAnalysisContext = object : AstAnalysisContext {
- override fun getTypeResolutionClassLoader(): ClassLoader = javaClass.classLoader
-
- override fun getLanguageVersion(): LanguageVersion = lversion
- }
-
- val stages = selectProcessingStages(handler).sortedWith { o1, o2 -> o1.compare(o2) }
-
- stages.forEach {
- it.processAST(rootNode, astAnalysisContext)
- }
+ protected open fun doParse(params: Params, task: Parser.ParserTask): T {
+ val parser = task.languageVersion.languageVersionHandler.parser
+ return rootClass.cast(parser.parse(task))
}
/**
@@ -252,5 +225,12 @@ abstract class BaseParsingHelper, T : RootNode
}
fun executeRuleOnResource(rule: Rule, resourcePath: String): Report =
- executeRule(rule, readResource(resourcePath))
+ executeRule(rule, code = readResource(resourcePath))
+
+ fun executeRuleOnFile(rule: Rule, path: Path): Report =
+ executeRule(
+ rule,
+ code = Files.newBufferedReader(path).readText(),
+ fileName = path.toString()
+ )
}
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java
index 1fe768cdb8..2deb0547f9 100644
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java
+++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java
@@ -7,16 +7,9 @@ package net.sourceforge.pmd.lang.modelica;
import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.modelica.ast.ModelicaParser;
-import net.sourceforge.pmd.lang.modelica.internal.ModelicaProcessingStage;
public class ModelicaHandler extends AbstractPmdLanguageVersionHandler {
- public ModelicaHandler() {
- super(ModelicaProcessingStage.class);
- }
-
-
-
@Override
public Parser getParser() {
return new ModelicaParser();
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java
index 3712b7e6e1..2e7d2e0d79 100644
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java
+++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParser.java
@@ -4,10 +4,12 @@
package net.sourceforge.pmd.lang.modelica.ast;
+import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.lang.ast.CharStream;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter;
+import net.sourceforge.pmd.lang.modelica.resolver.ModelicaSymbolFacade;
public class ModelicaParser extends JjtreeParserAdapter {
@@ -19,7 +21,9 @@ public class ModelicaParser extends JjtreeParserAdapter {
@Override
protected ASTStoredDefinition parseImpl(CharStream cs, ParserTask task) throws ParseException {
- return new ModelicaParserImpl(cs).StoredDefinition().makeTaskInfo(task);
+ ASTStoredDefinition root = new ModelicaParserImpl(cs).StoredDefinition().makeTaskInfo(task);
+ TimeTracker.bench("Modelica symbols", () -> ModelicaSymbolFacade.process(root));
+ return root;
}
}
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/internal/ModelicaProcessingStage.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/internal/ModelicaProcessingStage.java
deleted file mode 100644
index 30f6266a59..0000000000
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/internal/ModelicaProcessingStage.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.modelica.internal;
-
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstAnalysisContext;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
-import net.sourceforge.pmd.lang.ast.RootNode;
-import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule;
-import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition;
-import net.sourceforge.pmd.lang.modelica.resolver.ModelicaSymbolFacade;
-
-/**
- * @author ClΓ©ment Fournier
- */
-public enum ModelicaProcessingStage implements AstProcessingStage {
- SYMBOL_RESOLUTION("Symbol resolution") {
- @Override
- public void processAST(RootNode rootNode, AstAnalysisContext configuration) {
- new ModelicaSymbolFacade().initializeWith((ASTStoredDefinition) rootNode);
- }
- };
-
- private final String displayName;
-
- ModelicaProcessingStage(String displayName) {
- this.displayName = displayName;
- }
-
-
- @Override
- public Language getLanguage() {
- return LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME);
- }
-
- @Override
- public String getDisplayName() {
- return displayName;
- }
-}
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java
index cf91c2edda..6786d3193f 100644
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java
+++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java
@@ -4,10 +4,17 @@
package net.sourceforge.pmd.lang.modelica.resolver;
+import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition;
-public class ModelicaSymbolFacade {
- public void initializeWith(ASTStoredDefinition node) {
+@InternalApi
+public final class ModelicaSymbolFacade {
+
+ private ModelicaSymbolFacade() {
+ // util class
+ }
+
+ public static void process(ASTStoredDefinition node) {
ScopeAndDeclarationFinder sc = new ScopeAndDeclarationFinder();
node.acceptVisitor(sc, null);
}
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java
index 66a47e3cf9..fb3bceb9a9 100644
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java
+++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java
@@ -14,7 +14,7 @@ import net.sourceforge.pmd.lang.modelica.ast.InternalModelicaNodeApi;
import net.sourceforge.pmd.lang.modelica.ast.ModelicaNode;
import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitorAdapter;
-public class ScopeAndDeclarationFinder extends ModelicaParserVisitorAdapter {
+class ScopeAndDeclarationFinder extends ModelicaParserVisitorAdapter {
private final Deque scopes = new ArrayDeque<>();
ScopeAndDeclarationFinder() {
diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java
index 417cd767fb..5950b7fe9c 100644
--- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java
+++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java
@@ -6,11 +6,9 @@ package net.sourceforge.pmd.lang.modelica.rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule;
import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitor;
-import net.sourceforge.pmd.lang.modelica.internal.ModelicaProcessingStage;
import net.sourceforge.pmd.lang.rule.AbstractRule;
/**
@@ -26,11 +24,4 @@ public abstract class AbstractModelicaRule extends AbstractRule implements Model
target.acceptVisitor(this, ctx);
}
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- if (!(stage instanceof ModelicaProcessingStage)) {
- throw new IllegalArgumentException("Processing stage wasn't a Modelica one: " + stage);
- }
- return true;
- }
}
diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParsingHelper.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParsingHelper.java
index de4c39c3bd..ddc3cde1ad 100644
--- a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParsingHelper.java
+++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParsingHelper.java
@@ -11,7 +11,7 @@ import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition;
public class ModelicaParsingHelper extends BaseParsingHelper {
/** This runs all processing stages when parsing. */
- public static final ModelicaParsingHelper DEFAULT = new ModelicaParsingHelper(Params.getDefaultProcess());
+ public static final ModelicaParsingHelper DEFAULT = new ModelicaParsingHelper(Params.getDefault());
private ModelicaParsingHelper(Params params) {
super(ModelicaLanguageModule.NAME, ASTStoredDefinition.class, params);
diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java
index a403100e40..5c6b7295e3 100644
--- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java
+++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java
@@ -16,11 +16,6 @@ import net.sourceforge.pmd.lang.plsql.ast.PLSQLParser;
*/
public class PLSQLHandler extends AbstractPmdLanguageVersionHandler {
-
- public PLSQLHandler() {
- super(PlsqlProcessingStage.class);
- }
-
@Override
public Parser getParser() {
return new PLSQLParser();
diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PlsqlProcessingStage.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PlsqlProcessingStage.java
deleted file mode 100644
index 53f496db42..0000000000
--- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PlsqlProcessingStage.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.plsql;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstAnalysisContext;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
-import net.sourceforge.pmd.lang.ast.RootNode;
-import net.sourceforge.pmd.lang.plsql.ast.ASTInput;
-import net.sourceforge.pmd.lang.plsql.symboltable.SymbolFacade;
-
-
-/**
- * PL-SQL AST processing stages.
- *
- * @author ClΓ©ment Fournier
- * @since 7.0.0
- */
-@Experimental
-public enum PlsqlProcessingStage implements AstProcessingStage {
-
- /**
- * Symbol table analysis.
- */
- SYMBOL_RESOLUTION("Symbol table") {
- @Override
- public void processAST(RootNode rootNode, AstAnalysisContext configuration) {
- new SymbolFacade().initializeWith((ASTInput) rootNode);
- }
- };
-
-
- private final String displayName;
- private final List dependencies;
-
-
- PlsqlProcessingStage(String displayName, PlsqlProcessingStage... dependencies) {
- this.displayName = displayName;
- this.dependencies = Collections.unmodifiableList(Arrays.asList(dependencies));
- }
-
-
- @Override
- public Language getLanguage() {
- return LanguageRegistry.findLanguageByTerseName("plsql");
- }
-
-
- @Override
- public List getDependencies() {
- return dependencies;
- }
-
-
- @Override
- public String getDisplayName() {
- return displayName;
- }
-
-
-}
-
diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java
index c15ab94e8d..611d7d077a 100644
--- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java
+++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/PLSQLParser.java
@@ -6,10 +6,12 @@ package net.sourceforge.pmd.lang.plsql.ast;
import org.checkerframework.checker.nullness.qual.Nullable;
+import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.lang.ast.CharStream;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter;
+import net.sourceforge.pmd.lang.plsql.symboltable.SymbolFacade;
public class PLSQLParser extends JjtreeParserAdapter {
@@ -25,7 +27,9 @@ public class PLSQLParser extends JjtreeParserAdapter {
@Override
protected ASTInput parseImpl(CharStream cs, ParserTask task) throws ParseException {
- return new PLSQLParserImpl(cs).Input().addTaskInfo(task);
+ ASTInput root = new PLSQLParserImpl(cs).Input().addTaskInfo(task);
+ TimeTracker.bench("PLSQL symbols", () -> SymbolFacade.process(root));
+ return root;
}
}
diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java
index 7d5c66d05a..97e226c28e 100644
--- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java
+++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java
@@ -7,10 +7,8 @@ package net.sourceforge.pmd.lang.plsql.rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.ast.AstProcessingStage;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.plsql.PLSQLLanguageModule;
-import net.sourceforge.pmd.lang.plsql.PlsqlProcessingStage;
import net.sourceforge.pmd.lang.plsql.ast.ASTInput;
import net.sourceforge.pmd.lang.plsql.ast.ASTPackageBody;
import net.sourceforge.pmd.lang.plsql.ast.ASTPackageSpecification;
@@ -82,14 +80,6 @@ public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLPar
return false;
}
- @Override
- public boolean dependsOn(AstProcessingStage> stage) {
- if (!(stage instanceof PlsqlProcessingStage)) {
- throw new IllegalArgumentException("Processing stage wasn't a " + PLSQLLanguageModule.NAME + " one: " + stage);
- }
- return true;
- }
-
/*
* Treat all Executable Code
*/
diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/SymbolFacade.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/SymbolFacade.java
index 88f645dd9b..3cda6ef332 100644
--- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/SymbolFacade.java
+++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/SymbolFacade.java
@@ -6,8 +6,13 @@ package net.sourceforge.pmd.lang.plsql.symboltable;
import net.sourceforge.pmd.lang.plsql.ast.ASTInput;
-public class SymbolFacade {
- public void initializeWith(ASTInput node) {
+public final class SymbolFacade {
+
+ private SymbolFacade() {
+
+ }
+
+ public static void process(ASTInput node) {
ScopeAndDeclarationFinder sc = new ScopeAndDeclarationFinder();
node.acceptVisitor(sc, null);
OccurrenceFinder of = new OccurrenceFinder();
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
index 9d49470e56..058bcb2125 100644
--- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/AbstractPLSQLParserTst.java
@@ -6,6 +6,6 @@ package net.sourceforge.pmd.lang.plsql;
public abstract class AbstractPLSQLParserTst {
- protected final PlsqlParsingHelper plsql = PlsqlParsingHelper.WITH_PROCESSING.withResourceContext(getClass());
+ protected final PlsqlParsingHelper plsql = PlsqlParsingHelper.DEFAULT.withResourceContext(getClass());
}
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PlsqlParsingHelper.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PlsqlParsingHelper.java
index c340aabaad..57400761e0 100644
--- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PlsqlParsingHelper.java
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/PlsqlParsingHelper.java
@@ -9,10 +9,8 @@ import net.sourceforge.pmd.lang.plsql.ast.ASTInput;
public class PlsqlParsingHelper extends BaseParsingHelper {
- /** This just runs the parser and no processing stages. */
- public static final PlsqlParsingHelper JUST_PARSE = new PlsqlParsingHelper(Params.getDefaultNoProcess());
/** This runs all processing stages when parsing. */
- public static final PlsqlParsingHelper WITH_PROCESSING = new PlsqlParsingHelper(Params.getDefaultProcess());
+ public static final PlsqlParsingHelper DEFAULT = new PlsqlParsingHelper(Params.getDefault());
private PlsqlParsingHelper(Params params) {
super(PLSQLLanguageModule.NAME, ASTInput.class, params);
diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java
index 3a4c1cc8f8..9e2e7c6699 100644
--- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java
+++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java
@@ -19,7 +19,7 @@ public class PlsqlTreeDumpTest extends BaseTreeDumpTest {
@Override
public BaseParsingHelper, ?> getParser() {
- return PlsqlParsingHelper.WITH_PROCESSING.withResourceContext(getClass());
+ return PlsqlParsingHelper.DEFAULT.withResourceContext(getClass());
}
@Test
diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java
index 7c4e8d4978..f2387cc649 100644
--- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java
+++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/lang/scala/ast/ScalaParsingHelper.java
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.scala.ScalaLanguageModule;
public final class ScalaParsingHelper extends BaseParsingHelper {
- public static final ScalaParsingHelper DEFAULT = new ScalaParsingHelper(Params.getDefaultProcess());
+ public static final ScalaParsingHelper DEFAULT = new ScalaParsingHelper(Params.getDefault());
private ScalaParsingHelper(Params params) {
super(ScalaLanguageModule.NAME, ASTSource.class, params);
diff --git a/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/ast/SwiftParsingHelper.java b/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/ast/SwiftParsingHelper.java
index ebfb36b62d..bc1828ac0f 100644
--- a/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/ast/SwiftParsingHelper.java
+++ b/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/ast/SwiftParsingHelper.java
@@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwTopLevel;
*/
public class SwiftParsingHelper extends BaseParsingHelper {
- public static final SwiftParsingHelper DEFAULT = new SwiftParsingHelper(Params.getDefaultNoProcess());
+ public static final SwiftParsingHelper DEFAULT = new SwiftParsingHelper(Params.getDefault());
public SwiftParsingHelper(@NotNull Params params) {
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java
index 1c8645fc78..54e9739159 100644
--- a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java
@@ -4,20 +4,28 @@
package net.sourceforge.pmd.cli;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
+import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import net.sourceforge.pmd.PMD;
+import net.sourceforge.pmd.PMD.StatusCode;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
/**
* @author Romain Pelisse <belaran@gmail.com>
@@ -72,25 +80,79 @@ public abstract class BaseCLITest {
}
}
+ /**
+ * @deprecated Use {@link #runTest(String...)}, note that
+ * it returns the log while this returns the name of a file containing the log.
+ */
+ @Deprecated
protected String runTest(String[] args, String testname) {
return runTest(args, testname, 0);
}
+ /**
+ * @deprecated Use {@link #runTest(StatusCode, String...)}, note that
+ * it returns the log while this returns the name of a file containing the log.
+ */
+ @Deprecated
protected String runTest(String[] args, String testname, int expectedExitCode) {
String filename = TEST_OUPUT_DIRECTORY + testname + ".txt";
long start = System.currentTimeMillis();
createTestOutputFile(filename);
System.out.println("Start running test " + testname);
- runPMDWith(args);
- checkStatusCode(expectedExitCode);
+ StatusCode statusCode = PMD.runPmd(args);
+ assertEquals(expectedExitCode, statusCode.toInt());
System.out.println("Test finished successfully after " + (System.currentTimeMillis() - start) + "ms.");
return filename;
}
+ /**
+ * Returns the log output.
+ */
+ protected String runTest(String... args) {
+ return runTest(StatusCode.OK, args);
+ }
+
+ /**
+ * Returns the log output.
+ *
+ * @deprecated Use {@link #runTest(StatusCode, String...)}
+ */
+ @Deprecated
+ protected String runTest(int expectedExitCode, String... args) {
+ switch (expectedExitCode) {
+ case 0:
+ return runTest(StatusCode.OK, args);
+ case 1:
+ return runTest(StatusCode.ERROR, args);
+ case 4:
+ return runTest(StatusCode.VIOLATIONS_FOUND, args);
+ default:
+ throw AssertionUtil.shouldNotReachHere("unknown status code " + expectedExitCode);
+ }
+ }
+
+ protected String runTest(StatusCode expectedExitCode, String... args) {
+ ByteArrayOutputStream console = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(console);
+ System.setOut(out);
+ System.setErr(out);
+ StatusCode statusCode = PMD.runPmd(args);
+ assertEquals(expectedExitCode, statusCode);
+ return console.toString();
+ }
+
+ /**
+ * @deprecated Use {@link #runTest(StatusCode, String...)}
+ */
+ @Deprecated
protected void runPMDWith(String[] args) {
PMD.main(args);
}
+ /**
+ * @deprecated Use {@link #runTest(StatusCode, String...)} instead of checking the return code manually
+ */
+ @Deprecated
protected void checkStatusCode(int expectedExitCode) {
int statusCode = getStatusCode();
if (statusCode != expectedExitCode) {
@@ -98,7 +160,28 @@ public abstract class BaseCLITest {
}
}
+ /**
+ * @deprecated Use {@link #runTest(StatusCode, String...)} instead
+ * of checking the return code manually
+ */
+ @Deprecated
protected int getStatusCode() {
return Integer.parseInt(System.getProperty(PMDCommandLineInterface.STATUS_CODE_PROPERTY));
}
+
+ public static Matcher containsPattern(final String regex) {
+ return new BaseMatcher() {
+ final Pattern pattern = Pattern.compile(regex);
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("a string containing the pattern '" + this.pattern + "'");
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ return o instanceof String && pattern.matcher((String) o).find();
+ }
+ };
+ }
}
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
index e3a35697ac..e9ed0fe288 100644
--- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
@@ -263,7 +263,7 @@ public abstract class RuleTst {
if (isUseAuxClasspath) {
// configure the "auxclasspath" option for unit testing
- configuration.prependClasspath(".");
+ configuration.prependAuxClasspath(".");
} else {
// simple class loader, that doesn't delegate to parent.
// this allows us in the tests to simulate PMD run without
diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java
index bcb97098f5..bd9292b8bd 100644
--- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java
+++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParsingHelper.java
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.vf.VfLanguageModule;
public final class VfParsingHelper extends BaseParsingHelper {
- public static final VfParsingHelper DEFAULT = new VfParsingHelper(Params.getDefaultProcess());
+ public static final VfParsingHelper DEFAULT = new VfParsingHelper(Params.getDefault());
private VfParsingHelper(Params params) {
super(VfLanguageModule.NAME, ASTCompilationUnit.class, params);
diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java
index 3cd0019f9b..1f76408722 100644
--- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java
+++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElTest.java
@@ -4,28 +4,20 @@
package net.sourceforge.pmd.lang.vf.rule.security;
-import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Collections;
import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
-import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleViolation;
-import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.vf.VFTestUtils;
import net.sourceforge.pmd.lang.vf.ast.VfParsingHelper;
import net.sourceforge.pmd.testframework.PmdRuleTst;
-import net.sourceforge.pmd.util.datasource.FileDataSource;
public class VfUnescapeElTest extends PmdRuleTst {
public static final String EXPECTED_RULE_MESSAGE = "Avoid unescaped user controlled content in EL";
@@ -34,7 +26,7 @@ public class VfUnescapeElTest extends PmdRuleTst {
* Verify that CustomFields stored in sfdx project format are correctly parsed
*/
@Test
- public void testSfdxCustomFields() throws Exception {
+ public void testSfdxCustomFields() {
Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf)
.resolve("StandardAccount.page");
@@ -47,7 +39,7 @@ public class VfUnescapeElTest extends PmdRuleTst {
RuleViolation ruleViolation = ruleViolations.get(i);
assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription());
int expectedLineNumber = firstLineWithErrors + i;
- if ((ruleViolations.size() + firstLineWithErrors - 1) == expectedLineNumber) {
+ if (ruleViolations.size() + firstLineWithErrors - 1 == expectedLineNumber) {
// The last line has two errors on the same page
expectedLineNumber = expectedLineNumber - 1;
}
@@ -59,7 +51,7 @@ public class VfUnescapeElTest extends PmdRuleTst {
* Verify that CustomFields stored in mdapi format are correctly parsed
*/
@Test
- public void testMdapiCustomFields() throws Exception {
+ public void testMdapiCustomFields() {
Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("StandardAccount.page");
Report report = runRule(vfPagePath);
@@ -77,7 +69,7 @@ public class VfUnescapeElTest extends PmdRuleTst {
* Tests a page with a single Apex controller
*/
@Test
- public void testApexController() throws Exception {
+ public void testApexController() {
Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("ApexController.page");
Report report = runRule(vfPagePath);
@@ -96,7 +88,7 @@ public class VfUnescapeElTest extends PmdRuleTst {
* Tests a page with a standard controller and two Apex extensions
*/
@Test
- public void testExtensions() throws Exception {
+ public void testExtensions() {
Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf)
.resolve(Paths.get("StandardAccountWithExtensions.page"));
@@ -114,32 +106,8 @@ public class VfUnescapeElTest extends PmdRuleTst {
/**
* Runs a rule against a Visualforce page on the file system.
*/
- private Report runRule(Path vfPagePath) throws Exception {
- Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath);
- assertNotNull(node);
-
- PMDConfiguration config = new PMDConfiguration();
- config.setIgnoreIncrementalAnalysis(true);
- // simple class loader, that doesn't delegate to parent.
- // this allows us in the tests to simulate PMD run without
- // auxclasspath, not even the classes from the test dependencies
- // will be found.
- config.setClassLoader(new ClassLoader() {
- @Override
- protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- if (name.startsWith("java.") || name.startsWith("javax.")) {
- return super.loadClass(name, resolve);
- }
- throw new ClassNotFoundException(name);
- }
- });
+ private Report runRule(Path vfPagePath) {
Rule rule = findRule("category/vf/security.xml", "VfUnescapeEl");
-
- return PMD.processFiles(
- config,
- listOf(RuleSet.forSingleRule(rule)),
- listOf(new FileDataSource(vfPagePath.toAbsolutePath().toFile())),
- Collections.emptyList()
- );
+ return VfParsingHelper.DEFAULT.executeRuleOnFile(rule, vfPagePath);
}
}
diff --git a/pmd-vm/src/test/java/net/sourceforge/pmd/lang/vm/ast/VmParsingHelper.java b/pmd-vm/src/test/java/net/sourceforge/pmd/lang/vm/ast/VmParsingHelper.java
index 00a2319c63..38365e7270 100644
--- a/pmd-vm/src/test/java/net/sourceforge/pmd/lang/vm/ast/VmParsingHelper.java
+++ b/pmd-vm/src/test/java/net/sourceforge/pmd/lang/vm/ast/VmParsingHelper.java
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.vm.VmLanguageModule;
public final class VmParsingHelper extends BaseParsingHelper {
- public static final VmParsingHelper DEFAULT = new VmParsingHelper(Params.getDefaultProcess());
+ public static final VmParsingHelper DEFAULT = new VmParsingHelper(Params.getDefault());
private VmParsingHelper(Params params) {
super(VmLanguageModule.NAME, ASTTemplate.class, params);
diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java
index ee5722798b..14e717b237 100644
--- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java
+++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java
@@ -4,18 +4,17 @@
package net.sourceforge.pmd.lang.xml;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
+import net.sourceforge.pmd.PMD.StatusCode;
import net.sourceforge.pmd.cli.BaseCLITest;
public class XmlCliTest extends BaseCLITest {
@@ -23,44 +22,38 @@ public class XmlCliTest extends BaseCLITest {
private static final String RULE_MESSAGE = "A tags are not allowed";
private String[] createArgs(String directory, String... args) {
- List arguments = new ArrayList<>();
- arguments.add("-f");
- arguments.add("text");
- arguments.add("-no-cache");
- arguments.add("-R");
- arguments.add(BASE_DIR + "/ruleset.xml");
- arguments.add("-d");
- arguments.add(BASE_DIR + directory);
+ List arguments = new ArrayList<>(listOf(
+ "-f",
+ "text",
+ "-no-cache",
+ "-R",
+ BASE_DIR + "/ruleset.xml",
+ "-d",
+ BASE_DIR + directory
+ ));
arguments.addAll(Arrays.asList(args));
return arguments.toArray(new String[0]);
}
@Test
public void analyzeSingleXmlWithoutForceLanguage() {
- String resultFilename = runTest(createArgs("/src/file1.ext"), "analyzeSingleXmlWithoutForceLanguage", 0);
- assertRuleMessage(0, resultFilename);
+ String log = runTest(StatusCode.OK, createArgs("/src/file1.ext"));
+ assertRuleMessage(0, log);
}
@Test
public void analyzeSingleXmlWithForceLanguage() {
- String resultFilename = runTest(createArgs("/src/file1.ext", "-force-language", "xml"),
- "analyzeSingleXmlWithForceLanguage", 4);
- assertRuleMessage(1, resultFilename);
+ String log = runTest(StatusCode.VIOLATIONS_FOUND, createArgs("/src/file1.ext", "-force-language", "xml"));
+ assertRuleMessage(1, log);
}
@Test
public void analyzeDirectoryWithForceLanguage() {
- String resultFilename = runTest(createArgs("/src/", "-force-language", "xml"),
- "analyzeDirectoryWithForceLanguage", 4);
- assertRuleMessage(3, resultFilename);
+ String log = runTest(StatusCode.VIOLATIONS_FOUND, createArgs("/src/", "-force-language", "xml"));
+ assertRuleMessage(3, log);
}
- private void assertRuleMessage(int expectedCount, String resultFilename) {
- try {
- String result = FileUtils.readFileToString(new File(resultFilename), StandardCharsets.UTF_8);
- Assert.assertEquals(expectedCount, StringUtils.countMatches(result, RULE_MESSAGE));
- } catch (IOException e) {
- throw new AssertionError(e);
- }
+ private void assertRuleMessage(int expectedCount, String log) {
+ Assert.assertEquals(expectedCount, StringUtils.countMatches(log, RULE_MESSAGE));
}
}
diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java
index 8faa47e547..62974c6509 100644
--- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java
+++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java
@@ -14,9 +14,9 @@ import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl.RootXmlNode;
*/
public final class XmlParsingHelper extends BaseParsingHelper {
- public static final XmlParsingHelper XML = new XmlParsingHelper(XmlLanguageModule.NAME, Params.getDefaultProcess());
- public static final XmlParsingHelper WSDL = new XmlParsingHelper(WsdlLanguageModule.NAME, Params.getDefaultProcess());
- public static final XmlParsingHelper POM = new XmlParsingHelper(PomLanguageModule.NAME, Params.getDefaultProcess());
+ public static final XmlParsingHelper XML = new XmlParsingHelper(XmlLanguageModule.NAME, Params.getDefault());
+ public static final XmlParsingHelper WSDL = new XmlParsingHelper(WsdlLanguageModule.NAME, Params.getDefault());
+ public static final XmlParsingHelper POM = new XmlParsingHelper(PomLanguageModule.NAME, Params.getDefault());
private XmlParsingHelper(String langName, Params params) {