Merge branch '7.0.x' into java-rule-bulk-update

This commit is contained in:
Clément Fournier committed 2020-11-13 14:37:16 +01:00
commit c1e58ad5e7
7 files changed
+199 -2

No files matched your search

+3
View File
@@ -33,6 +33,9 @@ This is a {{ site.pmd.release_type }} release.
{% jdoc !!java::lang.java.ast.ASTTypeParameter#getParameterName() %}
and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`,
the attribute is `@Name`.
* {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isAnonymousInnerClass() %},
and {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceBody#isEnumChild() %},
refs [#905](https://github.com/pmd/pmd/issues/905)
#### Internal API
@@ -14,6 +14,7 @@ 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.Parser;
import net.sourceforge.pmd.lang.ast.Node;
@@ -105,9 +106,15 @@ public class SourceCodeProcessor {
} catch (ParseException pe) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while parsing " + ctx.getSourceCodeFile(), pe);
} catch (Exception | StackOverflowError | AssertionError e) {
} catch (Exception e) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while processing " + ctx.getSourceCodeFile(), e);
} catch (StackOverflowError | AssertionError e) {
if (SystemProps.isErrorRecoveryMode()) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while processing " + ctx.getSourceCodeFile(), e);
}
throw e;
} finally {
ruleSets.end(ctx);
}
@@ -0,0 +1,28 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.internal;
public final class SystemProps {
public static final String PMD_ERROR_RECOVERY = "pmd.error_recovery";
private SystemProps() {
}
/**
* In error recovery mode errors like StackOverflowError or AssertionErrors are logged
* and the execution continues.
* These exceptions mean, that something went really wrong while executing and
* depending on where the error occurred, the internal state might be corrupted
* or not. Hence, it might work to continue and "ignore" (just log) the error
* or we'll see more problems when continuing. That's why error recovery mode
* is not enabled by default.
* <p>
* The System Property is called {@code pmd.error_recovery}.
*/
public static boolean isErrorRecoveryMode() {
return System.getProperty(PMD_ERROR_RECOVERY) != null;
}
}
@@ -16,6 +16,7 @@ import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.benchmark.TimedOperation;
import net.sourceforge.pmd.benchmark.TimedOperationCategory;
import net.sourceforge.pmd.internal.SystemProps;
import net.sourceforge.pmd.lang.ast.Node;
/** Applies a set of rules to a set of ASTs. */
@@ -60,10 +61,21 @@ public class RuleApplicator {
try (TimedOperation rcto = TimeTracker.startOperation(TimedOperationCategory.RULE, rule.getName())) {
rule.apply(node, ctx);
rcto.close(1);
} catch (RuntimeException | StackOverflowError | AssertionError e) {
} catch (RuntimeException e) {
if (ctx.isIgnoreExceptions()) {
ctx.getReport().addError(new ProcessingError(e, String.valueOf(ctx.getSourceCodeFile())));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFile() + ", continuing with next rule", e);
}
} else {
throw e;
}
} catch (StackOverflowError | AssertionError e) {
if (SystemProps.isErrorRecoveryMode()) {
ctx.getReport().addError(new ProcessingError(e, String.valueOf(ctx.getSourceCodeFile())));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFile() + ", continuing with next rule", e);
@@ -0,0 +1,104 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.rules.TestRule;
import net.sourceforge.pmd.internal.SystemProps;
import net.sourceforge.pmd.lang.DummyLanguageModule;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRule;
public class SourceCodeProcessorTest {
@org.junit.Rule
public TestRule restoreSystemProperties = new RestoreSystemProperties();
private SourceCodeProcessor processor;
private StringReader sourceCode;
private RuleContext ctx;
private List<RuleSet> rulesets;
private LanguageVersion dummyThrows;
private LanguageVersion dummyDefault;
@Before
public void prepare() {
Language dummyLanguage = LanguageRegistry.findLanguageByTerseName(DummyLanguageModule.TERSE_NAME);
dummyDefault = dummyLanguage.getDefaultVersion();
dummyThrows = dummyLanguage.getVersion("1.9-throws");
processor = new SourceCodeProcessor(new PMDConfiguration());
sourceCode = new StringReader("test");
Rule rule = new RuleThatThrows();
rulesets = Arrays.asList(RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule));
ctx = new RuleContext();
}
@Test
public void inErrorRecoveryModeErrorsShouldBeLoggedByParser() {
System.setProperty(SystemProps.PMD_ERROR_RECOVERY, "");
ctx.setLanguageVersion(dummyThrows);
Assert.assertThrows(PMDException.class, () -> {
processor.processSourceCode(sourceCode, new RuleSets(rulesets), ctx);
});
// the error is actually logged by PmdRunnable
}
@Test
public void inErrorRecoveryModeErrorsShouldBeLoggedByRule() throws Exception {
System.setProperty(SystemProps.PMD_ERROR_RECOVERY, "");
ctx.setLanguageVersion(dummyDefault);
processor.processSourceCode(sourceCode, new RuleSets(rulesets), ctx);
Assert.assertEquals(1, ctx.getReport().getProcessingErrors().size());
Assert.assertSame(AssertionError.class, ctx.getReport().getProcessingErrors().get(0).getError().getClass());
}
@Test
public void withoutErrorRecoveryModeProcessingShouldBeAbortedByParser() {
Assert.assertNull(System.getProperty(SystemProps.PMD_ERROR_RECOVERY));
ctx.setLanguageVersion(dummyThrows);
Assert.assertThrows(AssertionError.class, () -> {
processor.processSourceCode(sourceCode, new RuleSets(rulesets), ctx);
});
}
@Test
public void withoutErrorRecoveryModeProcessingShouldBeAbortedByRule() {
Assert.assertNull(System.getProperty(SystemProps.PMD_ERROR_RECOVERY));
ctx.setLanguageVersion(dummyDefault);
Assert.assertThrows(AssertionError.class, () -> {
processor.processSourceCode(sourceCode, new RuleSets(rulesets), ctx);
});
}
private static class RuleThatThrows extends AbstractRule {
RuleThatThrows() {
Language dummyLanguage = LanguageRegistry.findLanguageByTerseName(DummyLanguageModule.TERSE_NAME);
setLanguage(dummyLanguage);
}
@Override
public void apply(Node target, RuleContext ctx) {
throw new AssertionError("test");
}
}
}
@@ -14,6 +14,7 @@ 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.ParseException;
import net.sourceforge.pmd.lang.ast.RootNode;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
@@ -36,6 +37,7 @@ public class DummyLanguageModule extends BaseLanguageModule {
addVersion("1.6", new Handler(), "6");
addDefaultVersion("1.7", new Handler(), "7");
addVersion("1.8", new Handler(), "8");
addVersion("1.9-throws", new HandlerWithParserThatThrows());
}
public static class Handler extends AbstractPmdLanguageVersionHandler {
@@ -64,6 +66,18 @@ public class DummyLanguageModule extends BaseLanguageModule {
}
}
public static class HandlerWithParserThatThrows extends Handler {
@Override
public Parser getParser(ParserOptions parserOptions) {
return new AbstractParser(parserOptions) {
@Override
public RootNode parse(String fileName, Reader source) throws ParseException {
throw new AssertionError("test error while parsing");
}
};
}
}
public static class RuleViolationFactory extends DefaultRuleViolationFactory {
@Override
@@ -698,4 +698,33 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description>AvoidReassigningLoopVariables detects some harmless reassigning of loop variables in foreach #2595</description>
<rule-property name="foreachReassign">firstOnly</rule-property>
<expected-problems>1</expected-problems>
<expected-linenumbers>6</expected-linenumbers>
<code><![CDATA[
public class Foo {
void foo(int bar) {
String[] strings = getStrings();
for (String g : strings) {
g = g.substring(0,g.lastIndexOf(":"));
g = g + ": 1;"; // here
FileUtil.writeLineToFile(w, g);
}
for (String item : strings) {
item = item.trim();
httpMethods = httpMethods | HttpMethods.getMethod(item);
}
for (String message : strings) {
message = OutputFormatter.escape(message);
lines.add(String.format(large ? " %s " : " %s ", message));
len = Math.max(length(message) + (large ? 4 : 2), len);
}
}
}
]]></code>
</test-code>
</test-data>