Merge pull request #4050 from oowekyala:violation-decorators

[core] Implement violation decorators #4050
This commit is contained in:
Andreas Dangel committed 2023-01-16 11:05:13 +01:00
commit 276a674509
39 files changed
+723 -746

No files matched your search

+2
View File
@@ -247,6 +247,8 @@ The following previously deprecated rules have been finally removed:
* [#4080](https://github.com/pmd/pmd/issues/4080): \[ant] Split off Ant integration into a new submodule
* core
* [#2234](https://github.com/pmd/pmd/issues/2234): \[core] Consolidate PMD CLI into a single command
* [#3203](https://github.com/pmd/pmd/issues/3203): \[core] Replace RuleViolationFactory implementations with ViolationDecorator
* [#3902](https://github.com/pmd/pmd/issues/3902): \[core] Violation decorators
* [#4035](https://github.com/pmd/pmd/issues/4035): \[core] ConcurrentModificationException in DefaultRuleViolationFactory
* cli
* [#3828](https://github.com/pmd/pmd/issues/3828): \[core] Progress reporting
@@ -9,6 +9,7 @@ import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import net.sourceforge.pmd.lang.BaseLanguageModule;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.apex.internal.ApexHandler;
import apex.jorje.services.Version;
@@ -1,35 +1,32 @@
/**
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex;
package net.sourceforge.pmd.lang.apex.internal;
import static net.sourceforge.pmd.util.CollectionUtil.setOf;
import java.util.List;
import java.util.Set;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.ViolationSuppressor;
import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
import net.sourceforge.pmd.lang.apex.ApexLanguageModule;
import net.sourceforge.pmd.lang.apex.ast.ApexParser;
import net.sourceforge.pmd.lang.apex.internal.ApexDesignerBindings;
import net.sourceforge.pmd.lang.apex.metrics.ApexMetrics;
import net.sourceforge.pmd.lang.apex.rule.internal.ApexRuleViolationFactory;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.metrics.Metric;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
import net.sourceforge.pmd.properties.PropertySource;
import net.sourceforge.pmd.util.designerbindings.DesignerBindings;
@InternalApi
public class ApexHandler extends AbstractPmdLanguageVersionHandler {
private final ApexMetricsProvider myMetricsProvider = new ApexMetricsProvider();
@Override
public RuleViolationFactory getRuleViolationFactory() {
return ApexRuleViolationFactory.INSTANCE;
public List<ViolationSuppressor> getExtraViolationSuppressors() {
return ApexViolationSuppressors.ALL_APEX_SUPPRESSORS;
}
@Override
@@ -2,10 +2,11 @@
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.internal;
package net.sourceforge.pmd.lang.apex.internal;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -29,11 +30,9 @@ import net.sourceforge.pmd.lang.apex.ast.ASTUserEnum;
import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclarationStatements;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
public final class ApexRuleViolationFactory extends DefaultRuleViolationFactory {
public final class ApexViolationSuppressors {
public static final ApexRuleViolationFactory INSTANCE = new ApexRuleViolationFactory();
private static final ViolationSuppressor APEX_ANNOT_SUPPRESSOR = new ViolationSuppressor() {
@Override
public String getId() {
@@ -49,12 +48,9 @@ public final class ApexRuleViolationFactory extends DefaultRuleViolationFactory
}
};
private ApexRuleViolationFactory() {
}
static final List<ViolationSuppressor> ALL_APEX_SUPPRESSORS = listOf(APEX_ANNOT_SUPPRESSOR);
@Override
protected List<ViolationSuppressor> getSuppressors() {
return Collections.singletonList(APEX_ANNOT_SUPPRESSOR);
private ApexViolationSuppressors() {
}
/**
@@ -4,21 +4,29 @@
package net.sourceforge.pmd;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextRange2d;
import net.sourceforge.pmd.lang.rule.AbstractRule;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.processor.AbstractPMDProcessor;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.ViolationDecorator;
/**
* The API for rules to report violations or errors during analysis.
@@ -37,6 +45,9 @@ public final class RuleContext {
// they are stack-local
private static final Object[] NO_ARGS = new Object[0];
private static final List<ViolationSuppressor> DEFAULT_SUPPRESSORS = listOf(ViolationSuppressor.NOPMD_COMMENT_SUPPRESSOR,
ViolationSuppressor.REGEX_SUPPRESSOR,
ViolationSuppressor.XPATH_SUPPRESSOR);
private final FileAnalysisListener listener;
private final Rule rule;
@@ -118,7 +129,7 @@ public final class RuleContext {
* as a format string for a {@link MessageFormat} and should hence use
* appropriate escapes. The given formatting arguments are used.
*
* @param location Location of the violation
* @param node Location of the violation
* @param message Violation message
* @param formatArgs Format arguments for the message
*/
@@ -127,17 +138,18 @@ public final class RuleContext {
Objects.requireNonNull(message, "Message was null");
Objects.requireNonNull(formatArgs, "Format arguments were null, use an empty array");
RuleViolationFactory fact = node.getTextDocument().getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory();
LanguageVersionHandler handler = node.getTextDocument().getLanguageVersion().getLanguageVersionHandler();
FileLocation location = node.getReportLocation();
if (beginLine != -1 && endLine != -1) {
location = FileLocation.range(location.getFileName(), TextRange2d.range2d(beginLine, 1, endLine, 1));
}
RuleViolation violation = fact.createViolation(rule, node, location, makeMessage(message, formatArgs));
final Map<String, String> extraVariables = ViolationDecorator.apply(handler.getViolationDecorator(), node);
final String description = makeMessage(message, formatArgs, extraVariables);
final RuleViolation violation = new ParametricRuleViolation(rule, location, description, extraVariables);
SuppressedViolation suppressed = fact.suppressOrNull(node, violation);
final SuppressedViolation suppressed = suppressOrNull(node, violation, handler);
if (suppressed != null) {
listener.onSuppressedRuleViolation(suppressed);
@@ -146,10 +158,18 @@ public final class RuleContext {
}
}
private static @Nullable SuppressedViolation suppressOrNull(Node location, RuleViolation rv, LanguageVersionHandler handler) {
SuppressedViolation suppressed = ViolationSuppressor.suppressOrNull(handler.getExtraViolationSuppressors(), rv, location);
if (suppressed == null) {
suppressed = ViolationSuppressor.suppressOrNull(DEFAULT_SUPPRESSORS, rv, location);
}
return suppressed;
}
/**
* Force the recording of a violation, ignoring the violation
* suppression mechanism ({@link ViolationSuppressor}).
*
*
* @param rv A violation
*/
@InternalApi
@@ -157,13 +177,46 @@ public final class RuleContext {
listener.onRuleViolation(rv);
}
private static String makeMessage(@NonNull String message, Object[] args) {
private String makeMessage(@NonNull String message, Object[] args, Map<String, String> extraVars) {
// Escape PMD specific variable message format, specifically the {
// in the ${, so MessageFormat doesn't bitch.
final String escapedMessage = StringUtils.replace(message, "${", "$'{'");
return MessageFormat.format(escapedMessage, args);
String formatted = MessageFormat.format(escapedMessage, args);
return expandVariables(formatted, extraVars);
}
private String expandVariables(String message, Map<String, String> extraVars) {
if (!message.contains("${")) {
return message;
}
StringBuilder buf = new StringBuilder(message);
int startIndex = -1;
while ((startIndex = buf.indexOf("${", startIndex + 1)) >= 0) {
final int endIndex = buf.indexOf("}", startIndex);
if (endIndex >= 0) {
final String name = buf.substring(startIndex + 2, endIndex);
String variableValue = getVariableValue(name, extraVars);
if (variableValue != null) {
buf.replace(startIndex, endIndex + 1, variableValue);
}
}
}
return buf.toString();
}
private String getVariableValue(String name, Map<String, String> extraVars) {
String value = extraVars.get(name);
if (value != null) {
return value;
}
final PropertyDescriptor<?> propertyDescriptor = rule.getPropertyDescriptor(name);
return propertyDescriptor == null ? null : String.valueOf(rule.getProperty(propertyDescriptor));
}
/**
* Create a new RuleContext. This is internal API owned by {@link AbstractPMDProcessor}
* (can likely be hidden when everything relevant is moved into rule package).
@@ -5,7 +5,9 @@
package net.sourceforge.pmd;
import java.util.Comparator;
import java.util.Map;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.lang.document.FileLocation;
/**
@@ -35,6 +37,28 @@ public interface RuleViolation {
.thenComparingInt(RuleViolation::getEndColumn)
.thenComparing(rv -> rv.getRule().getName());
/**
* Key in {@link #getAdditionalInfo()} for the name of the class in
* which the violation was identified.
*/
String CLASS_NAME = "className";
/**
* Key in {@link #getAdditionalInfo()} for the name of the variable
* related to the violation.
*/
String VARIABLE_NAME = "variableName";
/**
* Key in {@link #getAdditionalInfo()} for the name of the method in
* which the violation was identified.
*/
String METHOD_NAME = "methodName";
/**
* Key in {@link #getAdditionalInfo()} for the name of the package in
* which the violation was identified.
*/
String PACKAGE_NAME = "packageName";
/**
* Get the Rule which identified this violation.
*
@@ -104,34 +128,63 @@ public interface RuleViolation {
return getLocation().getEndPos().getColumn();
}
/**
* A map of additional key-value pairs known about this violation.
* What data is in there is language specific. Common keys supported
* by several languages are defined as constants on this interface.
* The map is unmodifiable.
*/
Map<String, String> getAdditionalInfo();
/**
* Get the package name of the Class in which this violation was identified.
*
* @return The package name.
*
* @deprecated Use {@link #PACKAGE_NAME}
*/
// TODO Isn't this Java specific?
String getPackageName();
@Deprecated
@DeprecatedUntil700
default String getPackageName() {
return getAdditionalInfo().get(PACKAGE_NAME);
}
/**
* Get the name of the Class in which this violation was identified.
*
* @return The Class name.
* @deprecated Use {@link #CLASS_NAME}
*/
// TODO Isn't this Java specific?
String getClassName();
@Deprecated
@DeprecatedUntil700
default String getClassName() {
return getAdditionalInfo().get(CLASS_NAME);
}
/**
* Get the method name in which this violation was identified.
*
* @return The method name.
* @deprecated Use {@link #METHOD_NAME}
*/
// TODO Isn't this Java specific?
String getMethodName();
@Deprecated
@DeprecatedUntil700
default String getMethodName() {
return getAdditionalInfo().get(METHOD_NAME);
}
/**
* Get the variable name on which this violation was identified.
*
* @return The variable name.
* @deprecated Use {@link #VARIABLE_NAME}
*/
String getVariableName();
@Deprecated
@DeprecatedUntil700
default String getVariableName() {
return getAdditionalInfo().get(VARIABLE_NAME);
}
}
@@ -4,6 +4,7 @@
package net.sourceforge.pmd;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@@ -13,14 +14,13 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.lang.ast.AstInfo;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
import net.sourceforge.pmd.lang.rule.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery;
/**
* An object that suppresses rule violations. Suppressors are used by
* {@link RuleViolationFactory} to filter out violations. In PMD 6.0.x,
* {@link RuleContext} to filter out violations. In PMD 6.0.x,
* the {@link Report} object filtered violations itself - but it has
* no knowledge of language-specific suppressors.
*/
@@ -121,4 +121,20 @@ public interface ViolationSuppressor {
SuppressedViolation suppressOrNull(RuleViolation rv, @NonNull Node node);
/**
* Apply a list of suppressors on the violation. Returns the violation
* of the first suppressor that matches the input violation. If no
* suppressor matches, then returns null.
*/
static @Nullable SuppressedViolation suppressOrNull(List<ViolationSuppressor> suppressorList,
RuleViolation rv,
Node node) {
for (ViolationSuppressor suppressor : suppressorList) {
SuppressedViolation suppressed = suppressor.suppressOrNull(rv, node);
if (suppressed != null) {
return suppressed;
}
}
return null;
}
}
@@ -7,12 +7,19 @@ package net.sourceforge.pmd.cache;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextRange2d;
import net.sourceforge.pmd.util.StringUtil;
/**
* A {@link RuleViolation} implementation that is immutable, and therefore cache friendly
@@ -30,26 +37,20 @@ public final class CachedRuleViolation implements RuleViolation {
private final String ruleClassName;
private final String ruleName;
private final String ruleTargetLanguage;
private final String packageName;
private final String className;
private final String methodName;
private final String variableName;
private final Map<String, String> additionalInfo;
private CachedRuleViolation(final CachedRuleMapper mapper, final String description,
final String fileName, final String ruleClassName, final String ruleName,
final String ruleTargetLanguage, final int beginLine, final int beginColumn,
final int endLine, final int endColumn, final String packageName,
final String className, final String methodName, final String variableName) {
final String fileName, final String ruleClassName, final String ruleName,
final String ruleTargetLanguage, final int beginLine, final int beginColumn,
final int endLine, final int endColumn,
final Map<String, String> additionalInfo) {
this.mapper = mapper;
this.description = description;
this.location = FileLocation.range(fileName, TextRange2d.range2d(beginLine, beginColumn, endLine, endColumn));
this.ruleClassName = ruleClassName;
this.ruleName = ruleName;
this.ruleTargetLanguage = ruleTargetLanguage;
this.packageName = packageName;
this.className = className;
this.methodName = methodName;
this.variableName = variableName;
this.additionalInfo = additionalInfo;
}
@Override
@@ -69,23 +70,8 @@ public final class CachedRuleViolation implements RuleViolation {
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public String getClassName() {
return className;
}
@Override
public String getMethodName() {
return methodName;
}
@Override
public String getVariableName() {
return variableName;
public Map<String, String> getAdditionalInfo() {
return additionalInfo;
}
/**
@@ -107,41 +93,51 @@ public final class CachedRuleViolation implements RuleViolation {
final int beginColumn = stream.readInt();
final int endLine = stream.readInt();
final int endColumn = stream.readInt();
final String packageName = stream.readUTF();
final String className = stream.readUTF();
final String methodName = stream.readUTF();
final String variableName = stream.readUTF();
final Map<String, String> additionalInfo = readAdditionalInfo(stream);
return new CachedRuleViolation(mapper, description, fileName, ruleClassName, ruleName, ruleTargetLanguage,
beginLine, beginColumn, endLine, endColumn, packageName, className, methodName, variableName);
beginLine, beginColumn, endLine, endColumn, additionalInfo);
}
private static @NonNull Map<String, String> readAdditionalInfo(DataInputStream stream) throws IOException {
int numAdditionalInfoKeyValuePairs = stream.readInt();
if (numAdditionalInfoKeyValuePairs == 0) {
return Collections.emptyMap();
}
Map<String, String> additionalInfo = new LinkedHashMap<>();
while (numAdditionalInfoKeyValuePairs-- > 0) {
final String key = stream.readUTF();
final String value = stream.readUTF();
additionalInfo.put(key, value);
}
return Collections.unmodifiableMap(additionalInfo);
}
/**
* Helper method to store a {@link RuleViolation} in an output stream to be later
* retrieved as a {@link CachedRuleViolation}
*
* @param stream The stream on which to store the violation.
* @param stream The stream on which to store the violation.
* @param violation The rule violation to cache.
* @throws IOException
*/
/* package */ static void storeToStream(final DataOutputStream stream,
final RuleViolation violation) throws IOException {
stream.writeUTF(getValueOrEmpty(violation.getDescription()));
stream.writeUTF(getValueOrEmpty(violation.getRule().getRuleClass()));
stream.writeUTF(getValueOrEmpty(violation.getRule().getName()));
stream.writeUTF(getValueOrEmpty(violation.getRule().getLanguage().getTerseName()));
stream.writeUTF(StringUtil.nullToEmpty(violation.getDescription()));
stream.writeUTF(StringUtil.nullToEmpty(violation.getRule().getRuleClass()));
stream.writeUTF(StringUtil.nullToEmpty(violation.getRule().getName()));
stream.writeUTF(StringUtil.nullToEmpty(violation.getRule().getLanguage().getTerseName()));
FileLocation location = violation.getLocation();
stream.writeInt(location.getStartPos().getLine());
stream.writeInt(location.getStartPos().getColumn());
stream.writeInt(location.getEndPos().getColumn());
stream.writeInt(location.getEndPos().getColumn());
stream.writeUTF(getValueOrEmpty(violation.getPackageName()));
stream.writeUTF(getValueOrEmpty(violation.getClassName()));
stream.writeUTF(getValueOrEmpty(violation.getMethodName()));
stream.writeUTF(getValueOrEmpty(violation.getVariableName()));
Map<String, String> additionalInfo = violation.getAdditionalInfo();
stream.writeInt(additionalInfo.size());
for (Entry<String, String> entry : additionalInfo.entrySet()) {
stream.writeUTF(entry.getKey());
stream.writeUTF(StringUtil.nullToEmpty(entry.getValue()));
}
}
private static String getValueOrEmpty(final String value) {
return value == null ? "" : value;
}
}
@@ -4,13 +4,16 @@
package net.sourceforge.pmd.lang;
import java.util.Collections;
import java.util.List;
import net.sourceforge.pmd.ViolationSuppressor;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler;
import net.sourceforge.pmd.properties.PropertySource;
import net.sourceforge.pmd.reporting.ViolationDecorator;
import net.sourceforge.pmd.util.designerbindings.DesignerBindings;
import net.sourceforge.pmd.util.designerbindings.DesignerBindings.DefaultDesignerBindings;
@@ -42,19 +45,24 @@ public interface LanguageVersionHandler {
/**
* Get the Parser.
*
* @return Parser
* Returns the parser instance.
*/
Parser getParser();
/**
* Returns the language-specific violation decorator.
*/
default ViolationDecorator getViolationDecorator() {
return ViolationDecorator.noop();
}
/**
* Get the RuleViolationFactory.
* Returns additional language-specific violation suppressors.
* These take precedence over the default suppressors (eg nopmd comment),
* but do not replace them.
*/
default RuleViolationFactory getRuleViolationFactory() {
return DefaultRuleViolationFactory.defaultInstance();
default List<ViolationSuppressor> getExtraViolationSuppressors() {
return Collections.emptyList();
}
@@ -4,12 +4,14 @@
package net.sourceforge.pmd.lang.rule;
import java.util.Collections;
import java.util.Map;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.reporting.Reportable;
/**
@@ -25,60 +27,43 @@ public class ParametricRuleViolation implements RuleViolation {
private final FileLocation location;
protected String packageName = "";
protected String className = "";
protected String methodName = "";
protected String variableName = "";
private final Map<String, String> additionalInfo;
// todo add factory methods on the interface and hide the class.
/**
* @deprecated Update tests that use this not to call the ctor directly.
*/
@Deprecated
public ParametricRuleViolation(Rule theRule, Reportable node, String message) {
this(theRule, node.getReportLocation(), message);
this(theRule, node.getReportLocation(), message, Collections.emptyMap());
}
public ParametricRuleViolation(Rule theRule, FileLocation location, String message) {
this(theRule, location, message, Collections.emptyMap());
}
public ParametricRuleViolation(Rule theRule, Reportable node, String message, Map<String, String> additionalInfo) {
this(theRule, node.getReportLocation(), message, additionalInfo);
}
public ParametricRuleViolation(Rule theRule, FileLocation location, String message, Map<String, String> additionalInfo) {
this.rule = AssertionUtil.requireParamNotNull("rule", theRule);
this.description = AssertionUtil.requireParamNotNull("message", message);
this.location = location;
}
protected String expandVariables(String message) {
// TODO move that to RuleContext with the rest of the formatting logic
if (!message.contains("${")) {
return message;
}
StringBuilder buf = new StringBuilder(message);
int startIndex = -1;
while ((startIndex = buf.indexOf("${", startIndex + 1)) >= 0) {
final int endIndex = buf.indexOf("}", startIndex);
if (endIndex >= 0) {
final String name = buf.substring(startIndex + 2, endIndex);
String variableValue = getVariableValue(name);
if (variableValue != null) {
buf.replace(startIndex, endIndex + 1, variableValue);
}
}
}
return buf.toString();
}
protected String getVariableValue(String name) {
if ("variableName".equals(name)) {
return variableName;
} else if ("methodName".equals(name)) {
return methodName;
} else if ("className".equals(name)) {
return className;
} else if ("packageName".equals(name)) {
return packageName;
if (!additionalInfo.isEmpty()) {
this.additionalInfo = Collections.unmodifiableMap(additionalInfo);
} else {
final PropertyDescriptor<?> propertyDescriptor = rule.getPropertyDescriptor(name);
return propertyDescriptor == null ? null : String.valueOf(rule.getProperty(propertyDescriptor));
this.additionalInfo = Collections.emptyMap();
}
}
@Override
public Map<String, String> getAdditionalInfo() {
return additionalInfo;
}
@Override
public Rule getRule() {
return rule;
@@ -86,7 +71,7 @@ public class ParametricRuleViolation implements RuleViolation {
@Override
public String getDescription() {
return expandVariables(description);
return description;
}
@Override
@@ -94,26 +79,6 @@ public class ParametricRuleViolation implements RuleViolation {
return location;
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public String getClassName() {
return className;
}
@Override
public String getMethodName() {
return methodName;
}
@Override
public String getVariableName() {
return variableName;
}
@Override
public String toString() {
return getFilename() + ':' + getRule() + ':' + getDescription() + ':' + getLocation().startPosToString();
@@ -1,36 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.document.FileLocation;
/**
* Creates violations and controls suppression behavior for a language.
*
* TODO split this into violation decorators + violation suppressors.
* There is no need to have language-specific violation classes.
*
* <p>Since PMD 6.43.0, {@link RuleContext} has been enriched with methods that should
* be strongly preferred to using this interface directly. The interface will change a
* lot in PMD 7.
*/
public interface RuleViolationFactory {
// todo move to package reporting
default RuleViolation createViolation(Rule rule, Node node, FileLocation location, String formattedMessage) {
return new ParametricRuleViolation(rule, location, formattedMessage);
}
SuppressedViolation suppressOrNull(Node location, RuleViolation violation);
}
@@ -1,75 +0,0 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.impl;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.ViolationSuppressor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
/**
* This is a functional implementation of {@link RuleViolationFactory}.
* It uses only the standard {@link ViolationSuppressor}s (constants in the interface).
* It may be extended to add more suppression options.
*
* <p>Implementations should be internal. Only the interface should be exposed.
*/
public class DefaultRuleViolationFactory implements RuleViolationFactory {
// todo move to package reporting
private static final DefaultRuleViolationFactory DEFAULT = new DefaultRuleViolationFactory();
// volatile for lazy init - see #getAllSuppressors
private volatile Set<ViolationSuppressor> allSuppressors; // NOPMD volatile needed for lazy init
@Override
public SuppressedViolation suppressOrNull(Node location, RuleViolation violation) {
for (ViolationSuppressor suppressor : getAllSuppressors()) {
SuppressedViolation suppressed = suppressor.suppressOrNull(violation, location);
if (suppressed != null) {
return suppressed;
}
}
return null;
}
/**
* Returns a list of additional suppressors for this language. These
* are added to regular //NOPMD, regex and XPath suppression.
*/
protected List<ViolationSuppressor> getSuppressors() {
return Collections.emptyList();
}
private Set<ViolationSuppressor> getAllSuppressors() {
Set<ViolationSuppressor> result = allSuppressors;
if (result == null) {
// lazy loaded because calling getSuppressors in constructor
// is not safe wrt initialization of static constants
// (order dependent otherwise)
result = new LinkedHashSet<>(getSuppressors());
result.add(ViolationSuppressor.NOPMD_COMMENT_SUPPRESSOR);
result.add(ViolationSuppressor.REGEX_SUPPRESSOR);
result.add(ViolationSuppressor.XPATH_SUPPRESSOR);
// note 1: allSuppressors must be volatile to avoid other threads seeing the HashSet under construction
// note 2: multiple threads might create their own HashSets and the last HashSet is stored and overwrites
// previously created HashSets. This is ok, because this method is supposed to be idempotent.
allSuppressors = result;
}
return result;
}
/** Returns the default instance (no additional suppressors, creates a ParametricRuleViolation). */
public static RuleViolationFactory defaultInstance() {
return DEFAULT;
}
}
@@ -12,6 +12,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.RuleViolation;
@@ -28,8 +29,8 @@ import net.sourceforge.pmd.renderers.ColumnDescriptor.Accessor;
*/
public class CSVRenderer extends AbstractIncrementingRenderer {
private String separator;
private String cr;
private final String separator;
private final String cr;
private CSVWriter<RuleViolation> csvWriter;
@@ -41,47 +42,19 @@ public class CSVRenderer extends AbstractIncrementingRenderer {
@SuppressWarnings("unchecked")
private final ColumnDescriptor<RuleViolation>[] allColumns = new ColumnDescriptor[] {
new ColumnDescriptor<>("problem", "Problem", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return Integer.toString(idx);
}
}), new ColumnDescriptor<>("package", "Package", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return rv.getPackageName();
}
}), new ColumnDescriptor<>("file", "File", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return CSVRenderer.this.determineFileName(rv.getFilename());
}
}), new ColumnDescriptor<>("priority", "Priority", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return Integer.toString(rv.getRule().getPriority().getPriority());
}
}), new ColumnDescriptor<>("line", "Line", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return Integer.toString(rv.getBeginLine());
}
}), new ColumnDescriptor<>("desc", "Description", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return StringUtils.replaceChars(rv.getDescription(), '\"', '\'');
}
}), new ColumnDescriptor<>("ruleSet", "Rule set", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return rv.getRule().getRuleSetName();
}
}), new ColumnDescriptor<>("rule", "Rule", new Accessor<RuleViolation>() {
@Override
public String get(int idx, RuleViolation rv, String cr) {
return rv.getRule().getName();
}
}), };
newColDescriptor("problem", "Problem", (idx, rv, cr) -> Integer.toString(idx)),
newColDescriptor("package", "Package", (idx, rv, cr) -> rv.getAdditionalInfo().getOrDefault(RuleViolation.PACKAGE_NAME, "")),
newColDescriptor("file", "File", (idx, rv, cr) -> determineFileName(rv.getFilename())),
newColDescriptor("priority", "Priority", (idx, rv, cr) -> Integer.toString(rv.getRule().getPriority().getPriority())),
newColDescriptor("line", "Line", (idx, rv, cr) -> Integer.toString(rv.getBeginLine())),
newColDescriptor("desc", "Description", (idx, rv, cr) -> StringUtils.replaceChars(rv.getDescription(), '\"', '\'')),
newColDescriptor("ruleSet", "Rule set", (idx, rv, cr) -> rv.getRule().getRuleSetName()),
newColDescriptor("rule", "Rule", (idx, rv, cr) -> rv.getRule().getName()),
};
private static @NonNull ColumnDescriptor<RuleViolation> newColDescriptor(String id, String title, Accessor<RuleViolation> accessor) {
return new ColumnDescriptor<>(id, title, accessor);
}
public CSVRenderer(ColumnDescriptor<RuleViolation>[] columns, String theSeparator, String theCR) {
super(NAME, "Comma-separated values tabular format.");
@@ -16,13 +16,13 @@ import java.util.List;
* @author Brian Remedios
* @param <T>
*/
public class CSVWriter<T extends Object> {
class CSVWriter<T> {
private final String separator; // e.g., the comma
private final String lineSeparator; // cr
private final List<ColumnDescriptor<T>> columns;
public CSVWriter(List<ColumnDescriptor<T>> theColumns, String theSeparator, String theLineSeparator) {
CSVWriter(List<ColumnDescriptor<T>> theColumns, String theSeparator, String theLineSeparator) {
columns = theColumns;
separator = theSeparator;
lineSeparator = theLineSeparator;
@@ -67,7 +67,7 @@ public class CSVWriter<T extends Object> {
private void quote(StringBuilder buffer, String s) {
if (s == null) {
return;
s = "";
}
buffer.append('"').append(s).append('"');
}
@@ -4,28 +4,21 @@
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.annotation.InternalApi;
/**
*
* @author Brian Remedios
*
* @param <T>
* @deprecated Is internal API
*/
@InternalApi
@Deprecated
public class ColumnDescriptor<T extends Object> {
final class ColumnDescriptor<T> {
public final String id;
public final String title;
public final Accessor<T> accessor;
public interface Accessor<T extends Object> {
public interface Accessor<T> {
String get(int idx, T violation, String lineSeparator);
}
public ColumnDescriptor(String theId, String theTitle, Accessor<T> theAccessor) {
ColumnDescriptor(String theId, String theTitle, Accessor<T> theAccessor) {
id = theId;
title = theTitle;
accessor = theAccessor;
@@ -259,7 +259,9 @@ public class TextColorRenderer extends AbstractAccumulatingRenderer {
}
private static String keyFor(RuleViolation rv) {
return StringUtils.isNotBlank(rv.getPackageName()) ? rv.getPackageName() + '.' + rv.getClassName() : "";
String packageName = rv.getAdditionalInfo().getOrDefault(RuleViolation.PACKAGE_NAME, "");
String className = rv.getAdditionalInfo().getOrDefault(RuleViolation.CLASS_NAME, "");
return StringUtils.isNotBlank(packageName) ? packageName + '.' + className : "";
}
@@ -166,10 +166,11 @@ public class XMLRenderer extends AbstractIncrementingRenderer {
xmlWriter.writeAttribute("endcolumn", String.valueOf(rv.getEndColumn()));
xmlWriter.writeAttribute("rule", rv.getRule().getName());
xmlWriter.writeAttribute("ruleset", rv.getRule().getRuleSetName());
maybeAdd("package", rv.getPackageName());
maybeAdd("class", rv.getClassName());
maybeAdd("method", rv.getMethodName());
maybeAdd("variable", rv.getVariableName());
maybeAdd("package", rv.getAdditionalInfo().get(RuleViolation.PACKAGE_NAME));
maybeAdd("class", rv.getAdditionalInfo().get(RuleViolation.CLASS_NAME));
maybeAdd("method", rv.getAdditionalInfo().get(RuleViolation.METHOD_NAME));
maybeAdd("variable", rv.getAdditionalInfo().get(RuleViolation.VARIABLE_NAME));
// todo other additional info keys are not rendered
maybeAdd("externalInfoUrl", rv.getRule().getExternalInfoUrl());
xmlWriter.writeAttribute("priority", String.valueOf(rv.getRule().getPriority().getPriority()));
writeNewLine();
@@ -21,6 +21,7 @@ import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
import net.sourceforge.pmd.util.StringUtil;
/**
* Renderer to another HTML format.
@@ -49,7 +50,8 @@ public class YAHTMLRenderer extends AbstractAccumulatingRenderer {
}
private void addViolation(RuleViolation violation) {
String packageName = violation.getPackageName();
String packageName = violation.getAdditionalInfo().getOrDefault(RuleViolation.PACKAGE_NAME, "");
String className = violation.getAdditionalInfo().getOrDefault(RuleViolation.CLASS_NAME, "");
// report each part of the package name: e.g. net.sf.pmd.test will create nodes for
// net, net.sf, net.sf.pmd, and net.sf.pmd.test
@@ -71,10 +73,10 @@ public class YAHTMLRenderer extends AbstractAccumulatingRenderer {
}
// add one node per class collecting the actual violations
String fqClassName = packageName + "." + violation.getClassName();
String fqClassName = packageName + "." + className;
ReportNode classNode = reportNodesByPackage.get(fqClassName);
if (classNode == null) {
classNode = new ReportNode(packageName, violation.getClassName());
classNode = new ReportNode(packageName, className);
reportNodesByPackage.put(fqClassName, classNode);
}
classNode.addRuleViolation(violation);
@@ -163,15 +165,17 @@ public class YAHTMLRenderer extends AbstractAccumulatingRenderer {
out.println(" <tr><th>Method</th><th>Violation</th></tr>");
for (RuleViolation violation : node.getViolations()) {
out.print(" <tr><td>");
out.print(violation.getMethodName());
String methodName = violation.getAdditionalInfo().get(RuleViolation.METHOD_NAME);
out.print(StringUtil.nullToEmpty(methodName));
out.print("</td><td>");
out.print("<table border=\"0\">");
out.print(renderViolationRow("Rule:", violation.getRule().getName()));
out.print(renderViolationRow("Description:", violation.getDescription()));
if (StringUtils.isNotBlank(violation.getVariableName())) {
out.print(renderViolationRow("Variable:", violation.getVariableName()));
String variableName = violation.getAdditionalInfo().get(RuleViolation.VARIABLE_NAME);
if (StringUtils.isNotBlank(variableName)) {
out.print(renderViolationRow("Variable:", variableName));
}
out.print(renderViolationRow("Line:", violation.getEndLine() > 0
@@ -0,0 +1,56 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.reporting;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.Node;
/**
* Adds additional key/value pairs to a violation in a language-specific manner.
* The keys are completely free. {@link RuleViolation} defines some of these keys.
*/
@FunctionalInterface
public interface ViolationDecorator {
/**
* Compute additional key/value pairs about the violation that should be
* reflected in {@link RuleViolation#getAdditionalInfo()}. This additional
* info should be accumulated into the {@code additionalInfo} parameter.
*
* @param violationNode The node on which the violation was reported
* @param additionalInfo Accumulator
*/
void decorate(Node violationNode, Map<String, String> additionalInfo);
static Map<String, String> apply(ViolationDecorator decorator, Node violationNode) {
Map<String, String> additionalInfo = new HashMap<>();
decorator.decorate(violationNode, additionalInfo);
if (!additionalInfo.isEmpty()) {
return Collections.unmodifiableMap(additionalInfo);
} else {
return Collections.emptyMap();
}
}
/**
* Apply several decorators in a chain.
*/
static ViolationDecorator chain(List<? extends ViolationDecorator> list) {
return (node, map) -> {
for (ViolationDecorator decorator : list) {
decorator.decorate(node, map);
}
};
}
static ViolationDecorator noop() {
return (node, map) -> { };
}
}
@@ -6,7 +6,7 @@
* Logic about reporting: violations, suppression etc.
*
* <p>TODO move {@link net.sourceforge.pmd.Report}, {@link net.sourceforge.pmd.RuleViolation},
* {@link net.sourceforge.pmd.RuleContext}, {@link net.sourceforge.pmd.ViolationSuppressor},
* {@link net.sourceforge.pmd.lang.rule.RuleViolationFactory} into this package
* {@link net.sourceforge.pmd.RuleContext}, {@link net.sourceforge.pmd.ViolationSuppressor}
* into this package
*/
package net.sourceforge.pmd.reporting;
@@ -280,6 +280,13 @@ public final class CollectionUtil {
return Collections.singletonMap(k0, v0);
}
public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2) {
Map<K, V> map = new LinkedHashMap<>();
map.put(k1, v1);
map.put(k2, v2);
return Collections.unmodifiableMap(map);
}
public static <K, V> Map<K, V> buildMap(Consumer<Map<K, V>> effect) {
Map<K, V> map = new LinkedHashMap<>();
effect.accept(map);
@@ -48,6 +48,10 @@ public final class StringUtil {
return "'" + s + "'";
}
public static @NonNull String inDoubleQuotes(String expected) {
return "\"" + expected + "\"";
}
/**
* Returns the (1-based) line number of the character at the given index.
@@ -551,8 +555,10 @@ public final class StringUtil {
return str.replaceAll("'", "''");
}
public static @NonNull String inDoubleQuotes(String expected) {
return "\"" + expected + "\"";
/** Return the empty string if the parameter is null. */
public static String nullToEmpty(final String value) {
return value == null ? "" : value;
}
@@ -7,21 +7,24 @@ package net.sourceforge.pmd;
import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.lang.DummyLanguageModule;
import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRule;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
public class AbstractRuleTest {
@@ -105,17 +108,20 @@ public class AbstractRuleTest {
DummyRootNode s = DummyLanguageModule.parse("abc()", "filename");
RuleViolation rv = RuleContextTest.getReportForRuleApply(r, s).getViolations().get(0);
assertEquals("Message foo 10 ${noSuchProperty}", rv.getDescription());
assertEquals("Message foo ${className} ${methodName} ${variableName} 10 ${noSuchProperty}", rv.getDescription());
}
@Test
void testRuleSuppress() {
DummyRootNode n = DummyLanguageModule.parse("abc()", "filename")
.withNoPmdComments(Collections.singletonMap(1, "ohio"));
RuleViolation violation = DefaultRuleViolationFactory.defaultInstance().createViolation(new MyRule(), n, n.getReportLocation(), "specificdescription");
SuppressedViolation suppressed = DefaultRuleViolationFactory.defaultInstance().suppressOrNull(n, violation);
assertNotNull(suppressed);
FileAnalysisListener listener = mock(FileAnalysisListener.class);
RuleContext ctx = RuleContext.create(listener, new MyRule());
ctx.addViolationWithMessage(n, "message");
verify(listener, never()).onRuleViolation(any());
verify(listener, times(1)).onSuppressedRuleViolation(any());
}
@Test
@@ -103,11 +103,11 @@ public class ReportTest {
assertEquals(2, union.getViolations().size());
}
private @NonNull RuleViolation violation(Rule rule, FileLocation loc2) {
public static @NonNull RuleViolation violation(Rule rule, FileLocation loc2) {
return violation(rule, loc2, rule.getMessage());
}
private @NonNull RuleViolation violation(Rule rule, FileLocation loc1, String rule1) {
public static @NonNull RuleViolation violation(Rule rule, FileLocation loc1, String rule1) {
return new ParametricRuleViolation(rule, loc1, rule1);
}
@@ -72,6 +72,6 @@ class RuleViolationComparatorTest {
private RuleViolation createJavaRuleViolation(Rule rule, String fileName, int beginLine, String description,
int beginColumn, int endLine, int endColumn) {
FileLocation loc = FileLocation.range(fileName, TextRange2d.range2d(beginLine, beginColumn, endLine, endColumn));
return new ParametricRuleViolation(rule, loc, description);
return new ParametricRuleViolation(rule, loc, description, Collections.emptyMap());
}
}
@@ -4,6 +4,7 @@
package net.sourceforge.pmd;
import static net.sourceforge.pmd.ReportTest.violation;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -76,8 +77,8 @@ class RuleViolationTest {
FileLocation loc = FileLocation.range(filename, TextRange2d.range2d(10, 1, 15, 10));
RuleViolation r1 = new ParametricRuleViolation(rule, loc, "description");
RuleViolation r2 = new ParametricRuleViolation(rule, loc, "description");
RuleViolation r1 = violation(rule, loc, "description");
RuleViolation r2 = violation(rule, loc, "description");
assertEquals(0, comp.compare(r1, r2));
assertEquals(0, comp.compare(r2, r1));
@@ -6,25 +6,19 @@ package net.sourceforge.pmd.lang;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.ast.Parser.ParserTask;
import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
import net.sourceforge.pmd.lang.document.Chars;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextDocument;
import net.sourceforge.pmd.lang.document.TextFile;
import net.sourceforge.pmd.lang.document.TextRegion;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory;
import net.sourceforge.pmd.processor.PmdRunnableTest;
import net.sourceforge.pmd.reporting.ViolationDecorator;
/**
* Dummy language used for testing PMD.
@@ -68,13 +62,13 @@ public class DummyLanguageModule extends BaseLanguageModule {
public static class Handler extends AbstractPmdLanguageVersionHandler {
@Override
public RuleViolationFactory getRuleViolationFactory() {
return new RuleViolationFactory();
public Parser getParser() {
return DummyLanguageModule::readLispNode;
}
@Override
public Parser getParser() {
return DummyLanguageModule::readLispNode;
public ViolationDecorator getViolationDecorator() {
return (node, data) -> data.put(RuleViolation.PACKAGE_NAME, "foo");
}
}
@@ -138,15 +132,4 @@ public class DummyLanguageModule extends BaseLanguageModule {
return root;
}
public static class RuleViolationFactory extends DefaultRuleViolationFactory {
@Override
public RuleViolation createViolation(Rule rule, @NonNull Node node, FileLocation location, @NonNull String formattedMessage) {
return new ParametricRuleViolation(rule, location, formattedMessage) {
{
this.packageName = "foo"; // just for testing variable expansion
}
};
}
}
}
@@ -13,6 +13,7 @@ import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collections;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
@@ -99,7 +100,7 @@ abstract class AbstractRendererTest {
protected RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, Rule rule) {
FileLocation loc = createLocation(beginLine, beginColumn, endLine, endColumn);
return new ParametricRuleViolation(rule, loc, "blah");
return new ParametricRuleViolation(rule, loc, "blah", Collections.emptyMap());
}
/**
@@ -9,11 +9,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Pattern;
import org.junit.jupiter.api.BeforeEach;
@@ -28,6 +28,7 @@ import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sourceforge.pmd.util.IOUtil;
class YAHTMLRendererTest extends AbstractRendererTest {
@@ -38,19 +39,16 @@ class YAHTMLRendererTest extends AbstractRendererTest {
private Path folder;
@BeforeEach
void setUp() throws IOException {
void setUp() {
outputDir = folder.resolve("pmdtest").toFile();
assertTrue(outputDir.mkdir());
}
private RuleViolation newRuleViolation(int beginLine, int beginColumn, int endLine, int endColumn, final String packageNameArg, final String classNameArg) {
FileLocation loc = createLocation(beginLine, beginColumn, endLine, endColumn);
return new ParametricRuleViolation(new FooRule(), loc, "blah") {
{
packageName = packageNameArg;
className = classNameArg;
}
};
Map<String, String> additionalInfo = CollectionUtil.mapOf(RuleViolation.PACKAGE_NAME, packageNameArg,
RuleViolation.CLASS_NAME, classNameArg);
return new ParametricRuleViolation(new FooRule(), loc, "blah", additionalInfo);
}
@Override
@@ -2,15 +2,23 @@
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.internal;
package net.sourceforge.pmd.lang.java.internal;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.ViolationSuppressor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
@@ -48,10 +56,27 @@ final class AnnotationSuppressionUtil {
private static final Set<String> UNUSED_RULES
= new HashSet<>(Arrays.asList("UnusedPrivateField", "UnusedLocalVariable", "UnusedPrivateMethod",
"UnusedFormalParameter", "UnusedAssignment", "SingularField"));
"UnusedFormalParameter", "UnusedAssignment", "SingularField"));
private static final Set<String> SERIAL_RULES =
new HashSet<>(Arrays.asList("BeanMembersShouldSerialize", "NonSerializableClass", "MissingSerialVersionUID"));
static final ViolationSuppressor JAVA_ANNOT_SUPPRESSOR = new ViolationSuppressor() {
@Override
public String getId() {
return "@SuppressWarnings";
}
@Override
public Report.SuppressedViolation suppressOrNull(RuleViolation rv, @NonNull Node node) {
if (contextSuppresses(node, rv.getRule())) {
return new SuppressedViolation(rv, this, null);
}
return null;
}
};
static final List<ViolationSuppressor> ALL_JAVA_SUPPRESSORS = listOf(JAVA_ANNOT_SUPPRESSOR);
private AnnotationSuppressionUtil() {
}
Loaded 30 of 39 files, more files were not shown because too many files have changed in this diff. Show more