[core] PMD stops processing file completely, if one rule in a rule chain fails

Fixes #1300
This commit is contained in:
Andreas Dangel
2018-08-13 11:15:57 +02:00
parent 7e53729baa
commit f773581459
4 changed files with 90 additions and 0 deletions

View File

@ -36,6 +36,7 @@ This is a minor release.
* core
* [#1191](https://github.com/pmd/pmd/issues/1191): \[core] Test Framework: Sort violations by line/column
* [#1288](https://github.com/pmd/pmd/issues/1288): \[core] No supported build listeners found with Gradle
* [#1300](https://github.com/pmd/pmd/issues/1300): \[core] PMD stops processing file completely, if one rule in a rule chain fails
* java-bestpractices
* [#1267](https://github.com/pmd/pmd/pull/1267): \[java] MissingOverrideRule: Avoid NoClassDefFoundError with incomplete classpath
* java-codestyle

View File

@ -12,7 +12,10 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
@ -27,6 +30,8 @@ import net.sourceforge.pmd.lang.ast.Node;
* expressed interest in.
*/
public abstract class AbstractRuleChainVisitor implements RuleChainVisitor {
private static final Logger LOG = Logger.getLogger(AbstractRuleChainVisitor.class.getName());
/**
* These are all the rules participating in the RuleChain, grouped by
* RuleSet.
@ -93,6 +98,17 @@ public abstract class AbstractRuleChainVisitor implements RuleChainVisitor {
visits += ns.size();
}
rcto.close(visits);
} catch (RuntimeException e) {
if (ctx.isIgnoreExceptions()) {
ctx.getReport().addError(new Report.ProcessingError(e, ctx.getSourceCodeFilename()));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFilename() + ", continuing with next rule", e);
}
} else {
throw e;
}
}
}
}

View File

@ -551,4 +551,76 @@ public class RuleSetTest {
context.setIgnoreExceptions(false);
ruleset.apply(makeCompilationUnits(), context);
}
@Test
public void ruleExceptionShouldNotStopProcessingFile() {
RuleSet ruleset = createRuleSetBuilder("ruleExceptionShouldBeReported").addRule(new MockRule() {
@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
throw new RuntimeException("Test exception while applying rule");
}
}).addRule(new MockRule() {
@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
for (Node node : nodes) {
addViolationWithMessage(ctx, node, "Test violation of the second rule in the ruleset");
}
}
}).build();
RuleContext context = new RuleContext();
context.setReport(new Report());
context.setLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getDefaultVersion());
context.setSourceCodeFilename(RuleSetTest.class.getName() + ".ruleExceptionShouldBeReported");
context.setIgnoreExceptions(true); // the default
ruleset.apply(makeCompilationUnits(), context);
assertTrue("Report should have processing errors", context.getReport().hasErrors());
List<ProcessingError> errors = CollectionUtil.toList(context.getReport().errors());
assertEquals("Errors expected", 1, errors.size());
assertEquals("Wrong error message", "Test exception while applying rule", errors.get(0).getMsg());
assertTrue("Should be a RuntimeException", errors.get(0).getError() instanceof RuntimeException);
assertEquals("There should be a violation", 1, context.getReport().size());
}
@Test
public void ruleExceptionShouldNotStopProcessingFileWithRuleChain() {
RuleSet ruleset = createRuleSetBuilder("ruleExceptionShouldBeReported").addRule(new MockRule() {
{
addRuleChainVisit("dummyNode");
}
@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
throw new RuntimeException("Test exception while applying rule");
}
}).addRule(new MockRule() {
{
addRuleChainVisit("dummyNode");
}
@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
for (Node node : nodes) {
addViolationWithMessage(ctx, node, "Test violation of the second rule in the ruleset");
}
}
}).build();
RuleContext context = new RuleContext();
context.setReport(new Report());
context.setLanguageVersion(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getDefaultVersion());
context.setSourceCodeFilename(RuleSetTest.class.getName() + ".ruleExceptionShouldBeReported");
context.setIgnoreExceptions(true); // the default
RuleSets rulesets = new RuleSets(ruleset);
rulesets.apply(makeCompilationUnits(), context, LanguageRegistry.getLanguage(DummyLanguageModule.NAME));
assertTrue("Report should have processing errors", context.getReport().hasErrors());
List<ProcessingError> errors = CollectionUtil.toList(context.getReport().errors());
assertEquals("Errors expected", 1, errors.size());
assertEquals("Wrong error message", "Test exception while applying rule", errors.get(0).getMsg());
assertTrue("Should be a RuntimeException", errors.get(0).getError() instanceof RuntimeException);
assertEquals("There should be a violation", 1, context.getReport().size());
}
}

View File

@ -53,6 +53,7 @@ public class XPathMetricFunctionTest {
Report report = new Report();
ctx.setReport(report);
ctx.setSourceCodeFilename("n/a");
ctx.setIgnoreExceptions(false); // for test, we want immediate exceptions thrown and not collect them
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx);
return report.iterator();